diff --git a/common/include/minix/sysutil.h b/common/include/minix/sysutil.h index 1f89db356..67dc218ab 100644 --- a/common/include/minix/sysutil.h +++ b/common/include/minix/sysutil.h @@ -68,6 +68,7 @@ _PROTOTYPE( void get_randomness, (struct k_randomness *, int)); #define asynsend(ep, msg) asynsend3(ep, msg, 0) _PROTOTYPE( int asynsend3, (endpoint_t ep, message *msg, int flags)); +_PROTOTYPE( int asyn_geterror, (endpoint_t *dst, message *msg, int *err)); #define ASSERT(c) if(!(c)) { panic("%s:%d: assert %s failed", __FILE__, __LINE__, #c); } diff --git a/lib/libsys/asynsend.c b/lib/libsys/asynsend.c index 8c2c74974..3893572b6 100644 --- a/lib/libsys/asynsend.c +++ b/lib/libsys/asynsend.c @@ -1,4 +1,3 @@ - #define _MINIX 1 #define _SYSTEM 1 @@ -20,121 +19,161 @@ #define ASYN_NR (2*_NR_PROCS) PRIVATE asynmsg_t msgtable[ASYN_NR]; -PRIVATE int first_slot= 0, next_slot= 0; +PRIVATE int first_slot = 0, next_slot = 0; +PRIVATE int initialized = 0; +/*===========================================================================* + * asynsend3 * + *===========================================================================*/ PUBLIC int asynsend3(dst, mp, fl) endpoint_t dst; message *mp; int fl; { - int r, src_ind, dst_ind; - unsigned flags; - static int first = 1; - static int inside = 0; - int len; + int i, r, src_ind, dst_ind; + unsigned flags; + static int inside = 0; + int len, needack = 0; - /* printf() causes asynsend? */ - if(inside) { - exit(1); - } + /* Debug printf() causes asynchronous sends? */ + if (inside) /* Panic will not work either then, so exit */ + exit(1); - inside = 1; + inside = 1; - if(first) { - int i; - for(i = 0; i < ASYN_NR; i++) { - msgtable[i].flags = AMF_EMPTY; + if(!initialized) { + /* Initialize table by marking all entries empty */ + for (i = 0; i < ASYN_NR; i++) msgtable[i].flags = AMF_EMPTY; + + initialized = 1; + } + + /* Update first_slot. That is, find the first not-completed slot by the + * kernel since the last time we sent this table (e.g., the receiving end of + * the message wasn't ready yet). + */ + for (; first_slot < next_slot; first_slot++) { + flags = msgtable[first_slot].flags; + if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE)) { + /* Marked in use by us (VALID) and processed by the kernel */ + if (msgtable[first_slot].result != OK) { +#if NDEBUG + printf("asynsend: found entry %d with error %d\n", + first_slot, msgtable[first_slot].result); +#endif + needack = (flags & (AMF_NOTIFY|AMF_NOTIFY_ERR)); } - first = 0; + continue; } - /* Update first_slot */ - for (; first_slot < next_slot; first_slot++) - { - flags= msgtable[first_slot].flags; - if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE)) - { - if (msgtable[first_slot].result != OK) - { -#if 0 + if (flags != AMF_EMPTY) + /* Found first not-completed table entry */ + break; + } + + /* Reset to the beginning of the table when all messages are completed */ + if (first_slot >= next_slot && !needack) + next_slot = first_slot = 0; + + /* Can the table handle one more message? */ + if (next_slot >= ASYN_NR) { + /* We're full; tell the kernel to stop processing for now */ + if ((r = senda(NULL, 0)) != OK) + panic("asynsend: senda failed: %d", r); + + /* Move all unprocessed messages to the beginning */ + dst_ind = 0; + for (src_ind = first_slot; src_ind < next_slot; src_ind++) { + flags = msgtable[src_ind].flags; + + /* Skip empty entries */ + if (flags == AMF_EMPTY) continue; + + /* and completed entries only if result is OK or if error + * doesn't need to be acknowledged */ + if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE)) { + if (msgtable[src_ind].result == OK) + continue; + else { +#if NDEBUG printf( - "asynsend: found completed entry %d with error %d\n", - first_slot, - msgtable[first_slot].result); + "asynsend: found entry %d with error %d\n", + src_ind, msgtable[src_ind].result); #endif + if (!(flags & (AMF_NOTIFY|AMF_NOTIFY_ERR))) + /* Don't need to ack this error */ + continue; } - continue; } - if (flags != AMF_EMPTY) - break; - } - if (first_slot >= next_slot) - { - /* Reset first_slot and next_slot */ - next_slot= first_slot= 0; - } - if (next_slot >= ASYN_NR) - { - /* Tell the kernel to stop processing */ - r= senda(NULL, 0); - if (r != OK) - panic("asynsend: senda failed: %d", r); - - dst_ind= 0; - for (src_ind= first_slot; src_ind= ASYN_NR) - panic("asynsend: msgtable full"); } - fl |= AMF_VALID; - msgtable[next_slot].dst= dst; - msgtable[next_slot].msg= *mp; - msgtable[next_slot].flags= fl; /* Has to be last. The kernel + /* Mark unused entries empty */ + for (i = dst_ind; i < ASYN_NR; i++) msgtable[i].flags = AMF_EMPTY; + + first_slot = 0; + next_slot = dst_ind; + if (next_slot >= ASYN_NR) /* Cleanup failed */ + panic("asynsend: msgtable full"); + } + + fl |= AMF_VALID; /* Mark in use */ + msgtable[next_slot].dst = dst; + msgtable[next_slot].msg = *mp; + msgtable[next_slot].flags = fl; /* Has to be last. The kernel * scans this table while we * are sleeping. */ - next_slot++; + next_slot++; - assert(first_slot < ASYN_NR); - assert(next_slot >= first_slot); - len = next_slot-first_slot; - assert(first_slot + len <= ASYN_NR); + assert(next_slot >= first_slot); + len = next_slot - first_slot; + assert(first_slot + len <= ASYN_NR); + assert(len >= 0); - /* Tell the kernel to rescan the table */ - r = senda(msgtable+first_slot, len); + inside = 0; - inside = 0; - - return r; + /* Tell the kernel to rescan the table */ + return senda(&msgtable[first_slot], len); +} + +/*===========================================================================* + * asyn_geterror * + *===========================================================================*/ +PUBLIC int asyn_geterror(endpoint_t *dst, message *msg, int *err) +{ + int src_ind, flags, result; + + if (!initialized) return(0); + + for (src_ind = 0; src_ind < next_slot; src_ind++) { + flags = msgtable[src_ind].flags; + result = msgtable[src_ind].result; + + /* Find a message that has been completed with an error */ + if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE)) { + if (result != OK && (flags & (AMF_NOTIFY|AMF_NOTIFY_ERR))) { + /* Found one */ + if (dst != NULL) *dst = msgtable[src_ind].dst; + if (msg != NULL) *msg = msgtable[src_ind].msg; + if (err != NULL) *err = result; + + /* Acknowledge error so it can be cleaned up upon next + * asynsend */ + msgtable[src_ind].result = OK; + + return(1); + } + } + } + + return(0); }