//****************************************************************************** // Mailbox system. //****************************************************************************** // // Author: Simon Pickartz, // Chair for Operating Systems, // RWTH Aachen University // Date: 005/08/2011 // //****************************************************************************** // // Copyright 2010 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // /** * * @file iRCCE_mailbox.c * @brief contains implementation of the mailbox system * @author Simon Pickartz * * */ #include #include #ifdef CONFIG_ROCKCREEK #include #include // forward declaration static int iRCCE_mailbox_close_one(int rank, int check); //------------------------------------------------------------------------------ // FUNCTION: iRCCE_mailbox_print_header //------------------------------------------------------------------------------ // routine for printing given header (debugging purpose) //------------------------------------------------------------------------------ /** * @brief routine for printing a given header * @param header is a pointer to a given iRCCE_MAIL_HEADER structure */ void iRCCE_mailbox_print_header(iRCCE_MAIL_HEADER* header) { kprintf( "\n" "-------------------------\n" "| RCK%d\n" "-------------------------\n" "| Sender\t: %d\t\n" "| Size\t\t: %d\t\n" "| Tag\t\t: %d\t\n" "| Prio\t\t: %d\t\n" "| Payload\t: %s\n" "-------------------------\n\n", RCCE_IAM, header->source, header->size, header->tag, header->prio, header->payload); } //------------------------------------------------------------------------------ // FUNCTION: iRCCE_mail_fetch //------------------------------------------------------------------------------ /** * @brief routine to check for new mail in a given mailbox * @param rank is the ID of the ranks mailbox to be emptied * * The function checks if the mailbox has new mail for a given rank. In case of * new mail it needs memory for the received header. Either there is memory * in the internal garbage collection or it has to allocated. The next step is * to check wheter a last-mail was received or a normal one. A last-mail is * indicated by the iRCCE_LAST_MAIL tag. A last-mail entails the appropriate * flag in the iRCCE_last_mail array to be set. Otherwise the header has to be * enqueued in the mailbox_recv_queue accordingly to the priority field. */ //------------------------------------------------------------------------------ static int iRCCE_mail_fetch( int rank // rank from whom to empty mailbox ) { /* interrupts should already be disabled! */ iRCCE_MAIL_HEADER* header; // check for memory in garbage collection or allocate new if( iRCCE_mail_garbage.first ) { header = iRCCE_mail_garbage.first; iRCCE_mail_garbage.first = iRCCE_mail_garbage.first->next; header->next = NULL; if( iRCCE_mail_garbage.first == NULL ) { iRCCE_mail_garbage.last = NULL; } } else { header = (iRCCE_MAIL_HEADER*)kmalloc(sizeof(iRCCE_MAIL_HEADER)); } // copy header to allocated memory //RC_cache_invalidate(); iRCCE_memcpy_get( (void*)header, (void*)iRCCE_mailbox_recv[rank], RCCE_LINE_SIZE ); // check if received a last-mail if( header->tag == iRCCE_LAST_MAIL ) { iRCCE_last_mail[rank] = 1; iRCCE_mailbox_close_one( rank, 0 ); // we can close respective mailbox iRCCE_mail_release( &header ); } else { // check mail priority int prio = header->prio; // enqueue accordingly header->next = NULL; if( iRCCE_mailbox_recv_queue[prio].first == NULL ) { iRCCE_mailbox_recv_queue[prio].first = header; iRCCE_mailbox_recv_queue[prio].last = header; } else { iRCCE_mailbox_recv_queue[prio].last->next = header; iRCCE_mailbox_recv_queue[prio].last = header; } } return iRCCE_SUCCESS; } //------------------------------------------------------------------------------ // FUNCTION: iRCCE_mail_check //------------------------------------------------------------------------------ /** * @brief routine to check one specific mailbox or all * @param sender is the core ID from which the mailbox is checked use * iRCCE_MAILBOX_ALL as wildcard to check all mailboxes * * This function may be called by the user application to check one specific * mailbox. It is recommended to use it in combination with an inter-core * interrupt. It empties one or all mailboxes of the participating cores if the * corresponding sent-flag is set and the mailbox is not closed. After calling * iRCCE_mail_fetch the sent-flag has to be reset. Here we have to use a little * trick because we can only write to the MPB in cacheline granularity. We set * the appropriate flag to zero and afterwords touch the MPB on another * cacheline. That causes the write combine buffer to write out the data. * */ //------------------------------------------------------------------------------ const static iRCCE_MAIL_HEADER dummy_header = {0, 0, 0, NULL, 0, 0, 0, {[0 ... iRCCE_MAIL_HEADER_PAYLOAD-1] = 0} }; int iRCCE_mail_check(int sender) { uint32_t flags; int j, i; int found = 0; // check all mailboxes in case of wildcard if( sender == iRCCE_MAILBOX_ALL ) { /* disable interrupts */ flags = irq_nested_disable(); for( j=1; jsent ) { if( !found ) found = 1; iRCCE_mail_fetch(i); // reset senders flag RC_cache_invalidate(); *(iRCCE_mailbox_recv[i]) = dummy_header; } } } /* enable interrupts */ irq_nested_enable(flags); return (found == 1)? iRCCE_SUCCESS : iRCCE_MAILBOX_EMPTY; } // verify sender's ID if(BUILTIN_EXPECT((sender < 0) || (sender > RCCE_NP) || (sender == RCCE_IAM), 0)) { return iRCCE_ERROR_SOURCE; } // check if mailbox is open if( iRCCE_mailbox_status[sender] == iRCCE_MAILBOX_CLOSED ) { return iRCCE_MAILBOX_CLOSED; } for(i=0; i<5; i++) { RC_cache_invalidate(); if( iRCCE_mailbox_recv[sender]->sent ) { /* disable interrupts */ flags = irq_nested_disable(); iRCCE_mail_fetch(sender); // reset senders flag RC_cache_invalidate(); *(iRCCE_mailbox_recv[sender]) = dummy_header; /* enable interrupts */ irq_nested_enable(flags); return iRCCE_SUCCESS; } NOP8; } return iRCCE_MAILBOX_EMPTY; } //------------------------------------------------------------------------------ // FUNCTION: iRCCE_mail_recv //------------------------------------------------------------------------------ /** * @brief routine for fetching received headers out of iRCCE_mailbox_recv_queue * @param header is the address of a pointer to an iRCCE_MAIL_HEADER structure * @return iRCCE_SUCCESS if there was new mail; iRCCE_MAILBOX_EMPTY else * @todo implement fairer dequeue mechanism * * The function checks if the receive queue with highest priority (priority 0) * contains any mail headers. In this case we pop the first element of that list * in a FIFO maner. Afterwards the first element of a non-empty receive queue * with highest priority is returned. */ //------------------------------------------------------------------------------ int iRCCE_mail_recv( iRCCE_MAIL_HEADER** header // pointer to incoming header ) { // (memory allocated by iRCCE) int i, found = 0; uint32_t flags; iRCCE_MAIL_HEADER* help_header; // if no mail queued pointer must be ZERO *header = NULL; /* disable interrupts */ flags = irq_nested_disable(); // check priority queues for( i=0; inext; if( iRCCE_mailbox_recv_queue[i].first == NULL ) { iRCCE_mailbox_recv_queue[i].last = NULL; } /* prepare return value */ help_header->next = NULL; *header = help_header; found = 1; break; } } /* enable interrupts */ irq_nested_enable(flags); return (found == 1)? iRCCE_SUCCESS : iRCCE_MAILBOX_EMPTY; } //------------------------------------------------------------------------------ // FUNCTION: iRCCE_mail_release //------------------------------------------------------------------------------ /** * @brief routine to store released header by user in garbage collection * @param header is the address of a pointer to an iRCCE_MAIL_HEADER structure * @return iRCCE_SUCCESS in any case * * This function enqueus a pointer to memory for an iRCCE_MAIL_HEADER structure * that is not used by the user program anymore. 'header' points to NULL by * return of the function. */ //------------------------------------------------------------------------------ int iRCCE_mail_release( iRCCE_MAIL_HEADER** header ) { uint32_t flags; /* disable interrupts */ flags = irq_nested_disable(); // put header in garbage collection if( (iRCCE_mail_garbage.first == NULL) && (iRCCE_mail_garbage.last == NULL ) ) { iRCCE_mail_garbage.first = *header; iRCCE_mail_garbage.last = *header; } else { iRCCE_mail_garbage.last->next = *header; iRCCE_mail_garbage.last = *header; } iRCCE_mail_garbage.last->next = NULL; // reset header *header = NULL; /* enable interrupts */ irq_nested_enable(flags); return iRCCE_SUCCESS; } //------------------------------------------------------------------------------ // FUNCTION: iRCCE_mail_send //------------------------------------------------------------------------------ /** * @brief routine to send a mail to a given destination (blocking) * @param size is the size of the following message. This message may be * contained in the payload or send by an isend()-call * @param tag is an integer value to distinguish between different mail types * @param prio indicates the priority of the mail. 0 is the highest priority * whereas 4 is the lowest. * @param payload is a pointer to byte array with a size of * iRCCE_MAIL_HEADER_PAYLOAD. If NULL is passed nothing is done, otherwise array * pointed by payload is copied into the header. * @param dest indicates the destination of the mail in terms of the ID of * one of the participating ranks * @return iRCCE_SUCCESS if send was successful. If target mailbox is closed * iRCCE_MAILBOX_CLOESD is returned. * * First it has to be checked if the target mailbox still contains an unread mail. * If this is the case there is time to empty the own mailboxes. It blocks until * the receiver has emptied its mailbox. The next step is to acquire the lock * for the target mailbox to be sure that the mailbox is not closed by the * receiver while the mail is delivered. After locking the mailbox an * iRCCE_MAIL_HEADER is generated according with the parameters (but with a * sent-flag set to zero)and is copied into the target mailbox. After all data * beeing written the appropropriate sent-flag has to be set with the same trick * already used in iRCCE_mail_check(). Now the lock can be released. */ //------------------------------------------------------------------------------ int iRCCE_mail_send( size_t size, // size of following message expected to be send/received int tag, // tag to indicate message type char prio, // mail priority char* payload, // pointer to buffer for header payload int dest // UE that will receive the header ) { uint32_t flags; // verify sender's ID if(BUILTIN_EXPECT((dest < 0) || (dest > RCCE_NP) || (dest == RCCE_IAM),0)) { return iRCCE_ERROR_TARGET; } // if dest mailbox is full, check for incoming mail RC_cache_invalidate(); while( iRCCE_mailbox_send[dest]->sent ) { // iRCCE_mail_check(iRCCE_MAILBOX_ALL); RC_cache_invalidate(); NOP8; NOP8; NOP8; } /* disable interrupts */ flags = irq_nested_disable(); // check if mailbox is closed RCCE_acquire_lock( dest ); RC_cache_invalidate(); if( iRCCE_mailbox_send[dest]->closed ) { RCCE_release_lock( dest ); /* enable interrupts */ irq_nested_enable(flags); return iRCCE_MAILBOX_CLOSED; } // prepare header iRCCE_MAIL_HEADER header = { RCCE_IAM, size, tag, NULL, prio, RCCE_FLAG_UNSET, RCCE_FLAG_UNSET, {[0 ... iRCCE_MAIL_HEADER_PAYLOAD-1] = 0} }; // payload within the header? if( payload ) { memcpy( header.payload, payload, iRCCE_MAIL_HEADER_PAYLOAD ); } // do the actual copy to MPB RC_cache_invalidate(); iRCCE_memcpy_put( (void*)iRCCE_mailbox_send[dest], (void*)&header, RCCE_LINE_SIZE ); // set senders flag RC_cache_invalidate(); iRCCE_mailbox_send[dest]->sent = RCCE_FLAG_SET; *(int *)RCCE_fool_write_combine_buffer = 1; RC_cache_invalidate(); RCCE_release_lock( dest ); /* enable interrupts */ irq_nested_enable(flags); return iRCCE_SUCCESS; } //------------------------------------------------------------------------------ // FUNCTION: iRCCE_last_mail_recv //------------------------------------------------------------------------------ /** * @brief check if all final headers are received from all UEs * @return iRCCE_SUCCES if all last-mails arrive iRCCE_LAST_MAILS_NOT_RECV * otherwise * * This functions is used to determine if all last-mails arrived at the calling * UE. Therefore it checks the iRCCE_last_mail array if all flags are set. */ //------------------------------------------------------------------------------ int iRCCE_last_mail_recv(void) { int i; int res = iRCCE_SUCCESS; for( i=0; inext; kfree( erase_header, sizeof(iRCCE_MAIL_HEADER) ); erase_header = iRCCE_mailbox_recv_queue[i].first; } } /* enable interrupts */ irq_nested_enable(flags); return iRCCE_SUCCESS; } //------------------------------------------------------------------------------ // FUNCTION: iRCCE_mailbox_close_one //------------------------------------------------------------------------------ /** * @brief routine to close one mailbox * @return iRCCE_SUCCESS * @param rank is the ID of the ranks mailbox to be closed * @param check is a flag indicating wether the mailbox has to be emptied before * closing or not. This is required for a close-call as a result of a received * last-mail. * * This function closes a mailbox of the given rank. If the check flag is set * an iRCCE_mail_check()-call is performed. The close procedure has to be * locked to be sure that no UE sends any mail while closing the mailbox. */ //------------------------------------------------------------------------------ static int iRCCE_mailbox_close_one(int rank, int check) { uint32_t flags; RCCE_acquire_lock( RCCE_IAM ); /* disable interrupts */ flags = irq_nested_disable(); // check if it contains new mail RC_cache_invalidate(); if( check && iRCCE_mailbox_recv[rank]->sent ) { iRCCE_mail_fetch(rank); } // close mailbox iRCCE_MAIL_HEADER help_header = { 0, 0, 0, NULL, 0, RCCE_FLAG_UNSET, RCCE_FLAG_SET, {[0 ... iRCCE_MAIL_HEADER_PAYLOAD-1] = 0} }; RC_cache_invalidate(); iRCCE_memcpy_put( (void*)iRCCE_mailbox_recv[rank], &help_header, RCCE_LINE_SIZE ); /* enable interrupts */ irq_nested_enable(flags); RCCE_release_lock( RCCE_IAM ); iRCCE_mailbox_status[rank] = iRCCE_MAILBOX_CLOSED; return iRCCE_SUCCESS; } //------------------------------------------------------------------------------ // FUNCTION: iRCCE_mailbox_close() //------------------------------------------------------------------------------ /** * @brief routine to close one or all mailboxes * @param rank is the ID of the UEs mailbox to be closed if iRCCE_MAILBOX_ALL * is passed all mailboxes are closed by the calling UE * @return iRCCE_SUCCESS * * This functions closed one or all mailboxes of the calling UE. This is done by * calling iRCCE_mailbox_close_one for one or all mailboxes. */ //------------------------------------------------------------------------------ int iRCCE_mailbox_close(int rank) { if( rank == iRCCE_MAILBOX_ALL ) { int i; for( i=0; i