From 7457cbe62fc9ab8ecf69dadb9b2066b0361b0d8b Mon Sep 17 00:00:00 2001 From: Thomas Veerman Date: Fri, 8 Apr 2011 15:14:48 +0000 Subject: [PATCH] Enable sending a notification when sending of an asynchronous message was completed (successfully or not). AMF_NOTIFY_ERR can be used if the sender only wishes to be notified in case of an error (e.g., EDEADSRCDST). A new endpoint ASYNCM will be the sender of the notification. --- common/include/minix/com.h | 3 +- common/include/minix/ipc.h | 12 ++-- kernel/proc.c | 127 +++++++++++++++++++++++++++++++------ kernel/proto.h | 2 + kernel/system.c | 8 +++ kernel/table.c | 3 +- 6 files changed, 130 insertions(+), 25 deletions(-) diff --git a/common/include/minix/com.h b/common/include/minix/com.h index 97f5b3ff0..b5557835c 100644 --- a/common/include/minix/com.h +++ b/common/include/minix/com.h @@ -54,6 +54,7 @@ */ /* Kernel tasks. These all run in the same address space. */ +#define ASYNCM ((endpoint_t) -5) /* notifies about finished async sends */ #define IDLE ((endpoint_t) -4) /* runs when no one else can run */ #define CLOCK ((endpoint_t) -3) /* alarms and other clock functions */ #define SYSTEM ((endpoint_t) -2) /* request system functionality */ @@ -62,7 +63,7 @@ /* Number of tasks. Note that NR_PROCS is defined in . */ #define MAX_NR_TASKS 1023 -#define NR_TASKS 4 +#define NR_TASKS 5 /* User-space processes, that is, device drivers, servers, and INIT. */ #define PM_PROC_NR ((endpoint_t) 0) /* process manager */ diff --git a/common/include/minix/ipc.h b/common/include/minix/ipc.h index b53af790d..8ce24a479 100644 --- a/common/include/minix/ipc.h +++ b/common/include/minix/ipc.h @@ -132,13 +132,15 @@ typedef struct asynmsg } asynmsg_t; /* Defines for flags field */ -#define AMF_EMPTY 0 /* slot is not inuse */ -#define AMF_VALID 1 /* slot contains message */ -#define AMF_DONE 2 /* Kernel has processed the message. The +#define AMF_EMPTY 000 /* slot is not inuse */ +#define AMF_VALID 001 /* slot contains message */ +#define AMF_DONE 002 /* Kernel has processed the message. The * result is stored in 'result' */ -#define AMF_NOTIFY 4 /* Send a notification when AMF_DONE is set */ -#define AMF_NOREPLY 8 /* Not a reply message for a SENDREC */ +#define AMF_NOTIFY 004 /* Send a notification when AMF_DONE is set */ +#define AMF_NOREPLY 010 /* Not a reply message for a SENDREC */ +#define AMF_NOTIFY_ERR 020 /* Send a notification when AMF_DONE is set and + * delivery of the message failed */ /* Hide names to avoid name space pollution. */ #define echo _echo diff --git a/kernel/proc.c b/kernel/proc.c index 1cbb307c5..f91ff7555 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -54,14 +54,13 @@ FORWARD _PROTOTYPE( void idle, (void)); FORWARD _PROTOTYPE( int mini_send, (struct proc *caller_ptr, endpoint_t dst_e, message *m_ptr, int flags)); */ -FORWARD _PROTOTYPE( int has_pending, (sys_map_t *map, int src_p) ); FORWARD _PROTOTYPE( int mini_receive, (struct proc *caller_ptr, endpoint_t src, message *m_ptr, int flags)); FORWARD _PROTOTYPE( int mini_senda, (struct proc *caller_ptr, asynmsg_t *table, size_t size)); FORWARD _PROTOTYPE( int deadlock, (int function, register struct proc *caller, endpoint_t src_dst_e)); -FORWARD _PROTOTYPE( int try_async, (struct proc *caller_ptr)); +FORWARD _PROTOTYPE( int try_async, (struct proc *caller_ptr) ); FORWARD _PROTOTYPE( int try_one, (struct proc *src_ptr, struct proc *dst_ptr)); FORWARD _PROTOTYPE( struct proc * pick_proc, (void)); FORWARD _PROTOTYPE( void enqueue_head, (struct proc *rp)); @@ -691,7 +690,7 @@ endpoint_t src_dst_e; /* src or dst process */ /*===========================================================================* * has_pending * *===========================================================================*/ -PRIVATE int has_pending(sys_map_t *map, int src_p) +PUBLIC int has_pending(sys_map_t *map, int src_p) { /* Check to see if there is a pending message from the desired source * available. @@ -1098,7 +1097,7 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size) if (size > 16*(NR_TASKS + NR_PROCS)) return(EDOM); /* Scan the table */ - do_notify = FALSE; /* XXX: this doesn't do anything? */ + do_notify = FALSE; done = TRUE; for (i = 0; i < size; i++) { /* Process each entry in the table and store the result in the table. @@ -1113,7 +1112,7 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size) if (flags == 0) continue; /* Skip empty entries */ /* 'flags' field must contain only valid bits */ - if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY)) + if(flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY|AMF_NOTIFY_ERR)) return(EINVAL); if (!(flags & AMF_VALID)) return(EINVAL); /* Must contain message */ if (flags & AMF_DONE) continue; /* Already done processing */ @@ -1157,14 +1156,17 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size) tabent.result = r; tabent.flags = flags; if (flags & AMF_DONE) { - if (r != EDEADSRCDST && (flags & AMF_NOTIFY)) - do_notify = TRUE; /* XXX: ? */ + if (flags & AMF_NOTIFY) + do_notify = TRUE; + else if (r != OK && (flags & AMF_NOTIFY_ERR)) + do_notify = TRUE; A_INSRT(i); /* Copy results to caller */ } else done = FALSE; } - if (do_notify) printf("mini_senda: should notify caller\n"); /* XXX: ? */ + if (do_notify) + mini_notify(proc_addr(ASYNCM), caller_ptr->p_endpoint); if (!done) { privp->s_asyntab = (vir_bytes) table; @@ -1227,9 +1229,9 @@ PRIVATE int try_one(struct proc *src_ptr, struct proc *dst_ptr) unset_sys_bit(priv(dst_ptr)->s_asyn_pending, privp->s_id); if (size == 0) return(EAGAIN); - if (!may_send_to(src_ptr, proc_nr(dst_ptr))) return(EAGAIN); + if (!may_send_to(src_ptr, proc_nr(dst_ptr))) return(ECALLDENIED); - caller_ptr = src_ptr; + caller_ptr = src_ptr; /* Needed for A_ macros later on */ /* Scan the table */ do_notify = FALSE; /* XXX: this doesn't do anything? */ @@ -1249,10 +1251,14 @@ PRIVATE int try_one(struct proc *src_ptr, struct proc *dst_ptr) if (flags == 0) continue; /* Skip empty entries */ /* 'flags' field must contain only valid bits */ - if (flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY)) - return(EINVAL); - if (!(flags & AMF_VALID)) return(EINVAL); /* Must contain message */ - if (flags & AMF_DONE) continue; /* Already done processing */ + if(flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY|AMF_NOTIFY_ERR)) + r = EINVAL; + else if (!(flags & AMF_VALID)) /* Must contain message */ + r = EINVAL; + else if (flags & AMF_DONE) continue; /* Already done processing */ + + if (r == EINVAL) + goto store_result; /* Clear done flag. The sender is done sending when all messages in the * table are marked done or empty. However, we will know that only @@ -1273,21 +1279,24 @@ PRIVATE int try_one(struct proc *src_ptr, struct proc *dst_ptr) continue; /* Destination is ready to receive the message; deliver it */ + r = OK; dst_ptr->p_delivermsg = tabent.msg; dst_ptr->p_delivermsg.m_source = src_ptr->p_endpoint; dst_ptr->p_misc_flags |= MF_DELIVERMSG; +store_result: /* Store results for sender */ - tabent.result = OK; + tabent.result = r; tabent.flags = flags | AMF_DONE; - if (flags & AMF_NOTIFY) do_notify = TRUE; /* XXX: ? */ + if (flags & AMF_NOTIFY) do_notify = TRUE; + else if (r != OK && (flags & AMF_NOTIFY_ERR)) do_notify = TRUE; A_INSRT(i); /* Copy results to sender */ - r = OK; break; } - if (do_notify) printf("mini_senda: should notify caller\n"); /* XXX: ? */ + if (do_notify) + mini_notify(proc_addr(ASYNCM), src_ptr->p_endpoint); if (!done) { privp->s_asyntab = table_v; @@ -1298,6 +1307,88 @@ PRIVATE int try_one(struct proc *src_ptr, struct proc *dst_ptr) return(r); } +/*===========================================================================* + * cancel_async * + *===========================================================================*/ +PUBLIC int cancel_async(struct proc *src_ptr, struct proc *dst_ptr) +{ +/* Cancel asynchronous messages from src to dst, because dst is not interested + * in them (e.g., dst has been restarted) */ + int done, do_notify, pending_recv = FALSE; + unsigned int flags, i; + size_t size; + endpoint_t dst; + struct proc *caller_ptr; + struct priv *privp; + asynmsg_t tabent; + vir_bytes table_v; + + privp = priv(src_ptr); + if (!(privp->s_flags & SYS_PROC)) return(EPERM); + size = privp->s_asynsize; + table_v = privp->s_asyntab; + + /* Clear table pending message flag. We're done unless we're not. */ + privp->s_asyntab = -1; + privp->s_asynsize = 0; + unset_sys_bit(priv(dst_ptr)->s_asyn_pending, privp->s_id); + + if (size == 0) return(EAGAIN); + if (!may_send_to(src_ptr, proc_nr(dst_ptr))) return(ECALLDENIED); + + caller_ptr = src_ptr; /* Needed for A_ macros later on */ + + /* Scan the table */ + do_notify = FALSE; + done = TRUE; + for (i = 0; i < size; i++) { + /* Process each entry in the table and store the result in the table. + * If we're done handling a message, copy the result to the sender. + * Some checks done in mini_senda are duplicated here, as the sender + * could've altered the contents of the table in the mean time. + */ + + int r = EDEADSRCDST; /* Cancel delivery due to dead dst */ + + /* Copy message to kernel */ + A_RETR(i); + flags = tabent.flags; + dst = tabent.dst; + + if (flags == 0) continue; /* Skip empty entries */ + + /* 'flags' field must contain only valid bits */ + if(flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY|AMF_NOTIFY_ERR)) + r = EINVAL; + else if (!(flags & AMF_VALID)) /* Must contain message */ + r = EINVAL; + else if (flags & AMF_DONE) continue; /* Already done processing */ + + /* Message must be directed at receiving end */ + if (dst != dst_ptr->p_endpoint) { + done = FALSE; + continue; + } + + /* Store results for sender */ + tabent.result = r; + tabent.flags = flags | AMF_DONE; + if (flags & AMF_NOTIFY) do_notify = TRUE; + else if (r != OK && (flags & AMF_NOTIFY_ERR)) do_notify = TRUE; + A_INSRT(i); /* Copy results to sender */ + } + + if (do_notify) + mini_notify(proc_addr(ASYNCM), src_ptr->p_endpoint); + + if (!done) { + privp->s_asyntab = table_v; + privp->s_asynsize = size; + } + + return(OK); +} + /*===========================================================================* * enqueue * *===========================================================================*/ diff --git a/kernel/proto.h b/kernel/proto.h index b26d7d456..79683763f 100644 --- a/kernel/proto.h +++ b/kernel/proto.h @@ -47,6 +47,8 @@ _PROTOTYPE( void bsp_finish_booting, (void) ); _PROTOTYPE( int do_ipc, (reg_t r1, reg_t r2, reg_t r3) ); _PROTOTYPE( void proc_init, (void) ); +_PROTOTYPE( int has_pending, (sys_map_t *map, int src_p) ); +_PROTOTYPE( int cancel_async, (struct proc *src, struct proc *dst) ); _PROTOTYPE( int mini_notify, (const struct proc *src, endpoint_t dst) ); _PROTOTYPE( void enqueue, (struct proc *rp) ); _PROTOTYPE( void dequeue, (struct proc *rp) ); diff --git a/kernel/system.c b/kernel/system.c index 71bdf5360..fd34336c8 100644 --- a/kernel/system.c +++ b/kernel/system.c @@ -591,6 +591,14 @@ int caller_ret; /* code to return on callers */ { /* Clear IPC references for a given process slot. */ struct proc *rp; /* iterate over process table */ + int src_id; + sys_map_t *map; + + /* Tell processes that sent asynchronous messages to 'rc' they are not + * going to be delivered */ + map = &priv(rc)->s_asyn_pending; + while ((src_id = has_pending(map, ANY)) != NULL_PRIV_ID) + cancel_async(proc_addr(id_to_nr(src_id)), rc); for (rp = BEG_PROC_ADDR; rp < END_PROC_ADDR; rp++) { if(isemptyp(rp)) diff --git a/kernel/table.c b/kernel/table.c index cde061411..d65a9dc79 100644 --- a/kernel/table.c +++ b/kernel/table.c @@ -56,6 +56,7 @@ PUBLIC struct boot_image image[] = { /* process nr, flags, name */ +{ASYNCM, 0, "asyncm"}, {IDLE, 0, "idle" }, {CLOCK, 0, "clock" }, {SYSTEM, 0, "system"}, @@ -66,7 +67,7 @@ PUBLIC struct boot_image image[] = { {PM_PROC_NR, OVM_F, "pm" }, {SCHED_PROC_NR,OVM_F, "sched" }, -{VFS_PROC_NR, OVM_F, "vfs" }, +{VFS_PROC_NR, BVM_F, "vfs" }, {MEM_PROC_NR, BVM_F, "memory"}, {LOG_PROC_NR, BVM_F, "log" }, {TTY_PROC_NR, BVM_F, "tty" }, -- 2.44.0