424 lines
12 KiB
C
424 lines
12 KiB
C
//******************************************************************************
|
|
// Non-blocking send routines.
|
|
//******************************************************************************
|
|
//
|
|
// Author: Rob F. Van der Wijngaart
|
|
// Intel Corporation
|
|
// Date: 008/30/2010
|
|
//
|
|
//******************************************************************************
|
|
//
|
|
// 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.
|
|
//
|
|
// [2010-10-25] added support for non-blocking send/recv operations
|
|
// - iRCCE_isend(), ..._test(), ..._wait(), ..._push()
|
|
// - iRCCE_irecv(), ..._test(), ..._wait(), ..._push()
|
|
// by Carsten Clauss, Chair for Operating Systems,
|
|
// RWTH Aachen University
|
|
//
|
|
// [2010-11-12] extracted non-blocking code into separate library
|
|
// by Carsten Scholtes
|
|
//
|
|
// [2010-12-09] added cancel functions for non-blocking send/recv requests
|
|
// by Carsten Clauss
|
|
//
|
|
// [2011-06-29] added the support of using IPIs
|
|
// by Simon Pickartz, Stefan Lankes
|
|
|
|
#include <metalsvm/stdlib.h>
|
|
#include <metalsvm/string.h>
|
|
|
|
#ifdef CONFIG_ROCKCREEK
|
|
|
|
#include <asm/iRCCE_lib.h>
|
|
#include <asm/SCC_API.h>
|
|
#include <asm/icc.h>
|
|
|
|
static int iRCCE_push_send_request(iRCCE_SEND_REQUEST *request) {
|
|
|
|
char padline[RCCE_LINE_SIZE]; // copy buffer, used if message not multiple of line size
|
|
int test; // flag for calling iRCCE_test_flag()
|
|
|
|
if(request->finished) return(iRCCE_SUCCESS);
|
|
|
|
if(request->label == 1) goto label1;
|
|
if(request->label == 2) goto label2;
|
|
if(request->label == 3) goto label3;
|
|
|
|
// send data in units of available chunk size of comm buffer
|
|
for (; request->wsize< (request->size / request->chunk) * request->chunk; request->wsize += request->chunk) {
|
|
request->bufptr = request->privbuf + request->wsize;
|
|
request->nbytes = request->chunk;
|
|
// copy private data to own comm buffer
|
|
iRCCE_put(request->combuf, (t_vcharp) request->bufptr, request->nbytes, RCCE_IAM);
|
|
RCCE_flag_write(request->sent, RCCE_FLAG_SET, request->dest);
|
|
// wait for the destination to be ready to receive a message
|
|
label1:
|
|
iRCCE_test_flag(*(request->ready), RCCE_FLAG_SET, &test);
|
|
if(!test) {
|
|
request->label = 1;
|
|
return(iRCCE_PENDING);
|
|
}
|
|
RCCE_flag_write(request->ready, RCCE_FLAG_UNSET, RCCE_IAM);
|
|
}
|
|
|
|
request->remainder = request->size % request->chunk;
|
|
// if nothing is left over, we are done
|
|
if (!request->remainder) {
|
|
request->finished = 1;
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
// send remainder of data--whole cache lines
|
|
request->bufptr = request->privbuf + (request->size / request->chunk) * request->chunk;
|
|
request->nbytes = request->remainder - request->remainder % RCCE_LINE_SIZE;
|
|
if (request->nbytes) {
|
|
// copy private data to own comm buffer
|
|
iRCCE_put(request->combuf, (t_vcharp)request->bufptr, request->nbytes, RCCE_IAM);
|
|
RCCE_flag_write(request->sent, RCCE_FLAG_SET, request->dest);
|
|
// wait for the destination to be ready to receive a message
|
|
label2:
|
|
iRCCE_test_flag(*(request->ready), RCCE_FLAG_SET, &test);
|
|
if(!test) {
|
|
request->label = 2;
|
|
return(iRCCE_PENDING);
|
|
}
|
|
RCCE_flag_write(request->ready, RCCE_FLAG_UNSET, RCCE_IAM);
|
|
}
|
|
|
|
request->remainder = request->size % request->chunk;
|
|
request->remainder = request->remainder%RCCE_LINE_SIZE;
|
|
// if nothing is left over, we are done
|
|
if (!request->remainder)
|
|
{
|
|
request->finished = 1;
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
// remainder is less than a cache line. This must be copied into appropriately sized
|
|
// intermediate space before it can be sent to the receiver
|
|
request->bufptr = request->privbuf + (request->size / request->chunk) * request->chunk + request->nbytes;
|
|
request->nbytes = RCCE_LINE_SIZE;
|
|
// copy private data to own comm buffer
|
|
memcpy(padline,request->bufptr,request->remainder);
|
|
iRCCE_put(request->combuf, (t_vcharp)padline, request->nbytes, RCCE_IAM);
|
|
RCCE_flag_write(request->sent, RCCE_FLAG_SET, request->dest);
|
|
// wait for the destination to be ready to receive a message
|
|
label3:
|
|
iRCCE_test_flag(*(request->ready), RCCE_FLAG_SET, &test);
|
|
if(!test) {
|
|
request->label = 3;
|
|
return(iRCCE_PENDING);
|
|
}
|
|
RCCE_flag_write(request->ready, RCCE_FLAG_UNSET, RCCE_IAM);
|
|
|
|
request->finished = 1;
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
static void iRCCE_init_send_request(
|
|
char *privbuf, // source buffer in local private memory (send buffer)
|
|
t_vcharp combuf, // intermediate buffer in MPB
|
|
size_t chunk, // size of MPB available for this message (bytes)
|
|
RCCE_FLAG *ready, // flag indicating whether receiver is ready
|
|
RCCE_FLAG *sent, // flag indicating whether message has been sent by source
|
|
size_t size, // size of message (bytes)
|
|
int dest, // UE that will receive the message
|
|
iRCCE_SEND_REQUEST *request
|
|
) {
|
|
|
|
request->privbuf = privbuf;
|
|
request->combuf = combuf;
|
|
request->chunk = chunk;
|
|
request->ready = ready;
|
|
request->sent = sent;
|
|
request->size = size;
|
|
request->dest = dest;
|
|
|
|
request->wsize = 0;
|
|
request->remainder = 0;
|
|
request->nbytes = 0;
|
|
request->bufptr = NULL;
|
|
|
|
request->label = 0;
|
|
|
|
request->finished = 0;
|
|
|
|
request->next = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// FUNCTION: iRCCE_isend_general
|
|
//--------------------------------------------------------------------------------------
|
|
// non-blocking send function; returns a handle of type iRCCE_SEND_REQUEST
|
|
//--------------------------------------------------------------------------------------
|
|
static iRCCE_SEND_REQUEST blocking_isend_request;
|
|
static int iRCCE_isend_general(char *privbuf, size_t size, int dest, iRCCE_SEND_REQUEST *request) {
|
|
|
|
|
|
|
|
iRCCE_init_send_request(privbuf, RCCE_buff_ptr, RCCE_chunk,
|
|
&RCCE_ready_flag[dest], &RCCE_sent_flag[RCCE_IAM],
|
|
size, dest, request);
|
|
|
|
if(iRCCE_isend_queue == NULL) {
|
|
|
|
if(iRCCE_push_send_request(request) == iRCCE_SUCCESS) {
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
else {
|
|
iRCCE_isend_queue = request;
|
|
|
|
if(request == &blocking_isend_request) {
|
|
iRCCE_isend_wait(request);
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
return(iRCCE_PENDING);
|
|
}
|
|
}
|
|
else {
|
|
if(iRCCE_isend_queue->next == NULL) {
|
|
iRCCE_isend_queue->next = request;
|
|
}
|
|
else {
|
|
iRCCE_SEND_REQUEST *run = iRCCE_isend_queue;
|
|
while(run->next != NULL) run = run->next;
|
|
run->next = request;
|
|
}
|
|
|
|
if(request == &blocking_isend_request) {
|
|
iRCCE_isend_wait(request);
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
return(iRCCE_RESERVED);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// FUNCTION: iRCCE_isend
|
|
//------------------------------------------------------------------------------
|
|
// wrapper function to differentiate between anylength and normal call
|
|
//------------------------------------------------------------------------------
|
|
static iRCCE_SEND_REQUEST blocking_isend_request;
|
|
int iRCCE_isend(
|
|
char *privbuf,
|
|
ssize_t size,
|
|
int dest,
|
|
iRCCE_SEND_REQUEST *request
|
|
) {
|
|
if(request == NULL) request = &blocking_isend_request;
|
|
|
|
if (dest<0 || dest >= RCCE_NP) {
|
|
return(RCCE_error_return(RCCE_debug_comm,RCCE_ERROR_ID));
|
|
}
|
|
else {
|
|
// anylength call
|
|
if( size < 0 ){
|
|
// convert size to positive range */
|
|
int send_size = -size;
|
|
|
|
// use header payload
|
|
if( send_size <= iRCCE_MAIL_HEADER_PAYLOAD ) {
|
|
iRCCE_init_send_request(privbuf, RCCE_buff_ptr,
|
|
RCCE_chunk, &RCCE_ready_flag[dest],
|
|
&RCCE_sent_flag[RCCE_IAM],
|
|
send_size, dest, request);
|
|
request->finished = 1;
|
|
|
|
iRCCE_mail_send( send_size,
|
|
iRCCE_ANYLENGTH_PIGGYBACK,
|
|
0, privbuf, dest );
|
|
//NOP8;
|
|
//NOP8;
|
|
//send_irq( dest );
|
|
return iRCCE_SUCCESS;
|
|
}
|
|
// we need an extra isend-call
|
|
else {
|
|
iRCCE_mail_send( send_size, iRCCE_ANYLENGTH,
|
|
0, NULL, dest );
|
|
//NOP8;
|
|
//NOP8;
|
|
//send_irq( dest );
|
|
return iRCCE_isend_general( privbuf, send_size,
|
|
dest, request );
|
|
}
|
|
}
|
|
// normal call
|
|
else if( size > 0 ) {
|
|
return iRCCE_isend_general( privbuf, size,
|
|
dest, request );
|
|
}
|
|
// do nothing
|
|
else {
|
|
iRCCE_init_send_request(privbuf, RCCE_buff_ptr,
|
|
RCCE_chunk, &RCCE_ready_flag[dest],
|
|
&RCCE_sent_flag[RCCE_IAM],
|
|
size, dest, request);
|
|
request->finished = 1;
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// FUNCTION: iRCCE_isend_test
|
|
//--------------------------------------------------------------------------------------
|
|
// test function for completion of the requestes non-blocking send operation
|
|
// Just provide NULL instead of testvar if you don't need it
|
|
//--------------------------------------------------------------------------------------
|
|
int iRCCE_isend_test(iRCCE_SEND_REQUEST *request, int *test) {
|
|
|
|
if(request == NULL) {
|
|
|
|
iRCCE_isend_push();
|
|
|
|
if(iRCCE_isend_queue == NULL) {
|
|
if (test) (*test) = 1;
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
else {
|
|
if (test) (*test) = 0;
|
|
return(iRCCE_PENDING);
|
|
}
|
|
}
|
|
|
|
if(request->finished) {
|
|
if (test) (*test) = 1;
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
if(iRCCE_isend_queue != request) {
|
|
if (test) (*test) = 0;
|
|
return(iRCCE_RESERVED);
|
|
}
|
|
|
|
iRCCE_push_send_request(request);
|
|
|
|
if(request->finished) {
|
|
iRCCE_isend_queue = request->next;
|
|
|
|
if (test) (*test) = 1;
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
if (test) (*test) = 0;
|
|
return(iRCCE_PENDING);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// FUNCTION: iRCCE_isend_push
|
|
//--------------------------------------------------------------------------------------
|
|
// progress function for pending requests in the isend queue
|
|
//--------------------------------------------------------------------------------------
|
|
int iRCCE_isend_push(void) {
|
|
|
|
iRCCE_SEND_REQUEST *request = iRCCE_isend_queue;
|
|
|
|
if(request == NULL) {
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
if(request->finished) {
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
iRCCE_push_send_request(request);
|
|
|
|
if(request->finished) {
|
|
iRCCE_isend_queue = request->next;
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
return(iRCCE_PENDING);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// FUNCTION: iRCCE_isend_wait
|
|
//--------------------------------------------------------------------------------------
|
|
// just wait for completion of the requestes non-blocking send operation
|
|
//--------------------------------------------------------------------------------------
|
|
int iRCCE_isend_wait(iRCCE_SEND_REQUEST *request) {
|
|
|
|
if(request != NULL) {
|
|
|
|
while(!request->finished) {
|
|
|
|
iRCCE_isend_push();
|
|
iRCCE_irecv_push();
|
|
}
|
|
}
|
|
else {
|
|
|
|
while(iRCCE_isend_queue != NULL) {
|
|
|
|
iRCCE_isend_push();
|
|
iRCCE_irecv_push();
|
|
}
|
|
}
|
|
|
|
return(iRCCE_SUCCESS);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// FUNCTION: iRCCE_isend_cancel
|
|
//--------------------------------------------------------------------------------------
|
|
// try to cancel a pending non-blocking send request
|
|
//--------------------------------------------------------------------------------------
|
|
int iRCCE_isend_cancel(iRCCE_SEND_REQUEST *request, int *test) {
|
|
|
|
iRCCE_SEND_REQUEST *run;
|
|
|
|
if( (request == NULL) || (request->finished) ) {
|
|
if (test) (*test) = 0;
|
|
return iRCCE_NOT_ENQUEUED;
|
|
}
|
|
|
|
if(iRCCE_isend_queue == NULL) {
|
|
if (test) (*test) = 0;
|
|
return iRCCE_NOT_ENQUEUED;
|
|
}
|
|
|
|
if(iRCCE_isend_queue == request) {
|
|
if (test) (*test) = 0;
|
|
return iRCCE_PENDING;
|
|
}
|
|
|
|
for(run = iRCCE_isend_queue; run->next != NULL; run = run->next) {
|
|
|
|
// request found --> remove it from send queue:
|
|
if(run->next == request) {
|
|
|
|
run->next = run->next->next;
|
|
|
|
if (test) (*test) = 1;
|
|
return iRCCE_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (test) (*test) = 0;
|
|
return iRCCE_NOT_ENQUEUED;
|
|
}
|
|
|
|
#endif
|