From 758d788bbe651be62887c0cc018bfe14d8f6b2f0 Mon Sep 17 00:00:00 2001 From: Tomas Hruby Date: Tue, 25 Oct 2011 18:32:30 +0000 Subject: [PATCH] SMP - asyn send SMP safe - we must not deliver messages from/to unstable address spaces. In such a case, we must postpone the delivery. To make sute that a process which is expecting an asynchronous message does not starve, we must remember that we skipped delivery of some messages and we must try to deliver again once the source address space is stable again. --- kernel/proc.c | 134 ++++++++++++++++++++++++++++++++------- kernel/proc.h | 8 ++- kernel/proto.h | 3 + kernel/system/do_vmctl.c | 7 ++ 4 files changed, 127 insertions(+), 25 deletions(-) diff --git a/kernel/proc.c b/kernel/proc.c index 5f7523c7f..c83dc291a 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -710,6 +710,9 @@ PRIVATE int has_pending(sys_map_t *map, int src_p, int asynm) int src_id; sys_id_t id = NULL_PRIV_ID; +#ifdef CONFIG_SMP + struct proc * p; +#endif /* Either check a specific bit in the mask map, or find the first bit set in * it (if any), depending on whether the receive was called on a specific @@ -717,16 +720,48 @@ PRIVATE int has_pending(sys_map_t *map, int src_p, int asynm) */ if (src_p != ANY) { src_id = nr_to_id(src_p); - if (get_sys_bit(*map, src_id)) - id = src_id; + if (get_sys_bit(*map, src_id)) { +#ifdef CONFIG_SMP + p = proc_addr(id_to_nr(src_id)); + if (asynm && RTS_ISSET(p, RTS_VMINHIBIT)) + p->p_misc_flags |= MF_SENDA_VM_MISS; + else +#endif + id = src_id; + } } else { /* Find a source with a pending message */ for (src_id = 0; src_id < NR_SYS_PROCS; src_id += BITCHUNK_BITS) { if (get_sys_bits(*map, src_id) != 0) { - while(!get_sys_bit(*map, src_id)) src_id++; - break; +#ifdef CONFIG_SMP + while (src_id < NR_SYS_PROCS) { + while (!get_sys_bit(*map, src_id)) { + if (src_id == NR_SYS_PROCS) + goto quit_search; + src_id++; + } + p = proc_addr(id_to_nr(src_id)); + /* + * We must not let kernel fiddle with pages of a + * process which are currently being changed by + * VM. It is dangerous! So do not report such a + * process as having pending async messages. + * Skip it. + */ + if (asynm && RTS_ISSET(p, RTS_VMINHIBIT)) { + p->p_misc_flags |= MF_SENDA_VM_MISS; + src_id++; + } else + goto quit_search; + } +#else + while (!get_sys_bit(*map, src_id)) src_id++; + goto quit_search; +#endif } } + +quit_search: if (src_id < NR_SYS_PROCS) /* Found one */ id = src_id; } @@ -1066,7 +1101,8 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab) KERNEL, (vir_bytes) &tabent.field, \ sizeof(tabent.field)) != OK) {\ ASCOMPLAIN(caller_ptr, entry, #field); \ - return EFAULT; \ + r = EFAULT; \ + goto asyn_error; \ } #define A_RETR(entry) do { \ @@ -1075,7 +1111,8 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab) KERNEL, (vir_bytes) &tabent, \ sizeof(tabent)) != OK) { \ ASCOMPLAIN(caller_ptr, entry, "message entry"); \ - return(EFAULT); \ + r = EFAULT; \ + goto asyn_error; \ } \ } while(0) @@ -1085,7 +1122,8 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab) table_v + (entry)*sizeof(asynmsg_t) + offsetof(struct asynmsg,field),\ sizeof(tabent.field)) != OK) {\ ASCOMPLAIN(caller_ptr, entry, #field); \ - return EFAULT; \ + r = EFAULT; \ + goto asyn_error; \ } #define A_INSRT(entry) do { \ @@ -1093,14 +1131,17 @@ field, caller->p_name, entry, priv(caller)->s_asynsize, priv(caller)->s_asyntab) caller_ptr->p_endpoint, table_v + (entry)*sizeof(asynmsg_t),\ sizeof(tabent)) != OK) { \ ASCOMPLAIN(caller_ptr, entry, "message entry"); \ - return(EFAULT); \ + r = EFAULT; \ + goto asyn_error; \ } \ } while(0) /*===========================================================================* - * mini_senda * + * try_deliver_senda * *===========================================================================*/ -PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size) +PUBLIC int try_deliver_senda(struct proc *caller_ptr, + asynmsg_t *table, + size_t size) { int r, dst_p, done, do_notify; unsigned int i; @@ -1112,33 +1153,34 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size) const vir_bytes table_v = (vir_bytes) table; privp = priv(caller_ptr); - if (!(privp->s_flags & SYS_PROC)) { - printf( "mini_senda: warning caller has no privilege structure\n"); - return(EPERM); - } /* Clear table */ - privp->s_asyntab = -1; + privp->s_asyntab = -1; privp->s_asynsize = 0; if (size == 0) return(OK); /* Nothing to do, just return */ + /* Scan the table */ + do_notify = FALSE; + done = TRUE; + /* Limit size to something reasonable. An arbitrary choice is 16 * times the number of process table entries. * * (this check has been duplicated in sys_call but is left here * as a sanity check) */ - if (size > 16*(NR_TASKS + NR_PROCS)) return(EDOM); - - /* Scan the table */ - do_notify = FALSE; - done = TRUE; + if (size > 16*(NR_TASKS + NR_PROCS)) { + r = EDOM; + return r; + } + 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. */ int pending_recv = FALSE; + dst = NONE; /* Copy message to kernel */ A_RETR(i); flags = tabent.flags; @@ -1147,9 +1189,14 @@ 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|AMF_NOTIFY_ERR)) - return(EINVAL); - if (!(flags & AMF_VALID)) return(EINVAL); /* Must contain message */ + if(flags & ~(AMF_VALID|AMF_DONE|AMF_NOTIFY|AMF_NOREPLY|AMF_NOTIFY_ERR)) { + r = EINVAL; + goto asyn_error; + } + if (!(flags & AMF_VALID)) { /* Must contain message */ + r = EINVAL; + goto asyn_error; + } if (flags & AMF_DONE) continue; /* Already done processing */ r = OK; @@ -1196,6 +1243,13 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size) else if (r != OK && (flags & AMF_NOTIFY_ERR)) do_notify = TRUE; A_INSRT(i); /* Copy results to caller */ + continue; + +asyn_error: + if (dst != NONE) + printf("KERNEL senda error %d to %d\n", r, dst); + else + printf("KERNEL senda error %d\n", r); } if (do_notify) @@ -1207,6 +1261,24 @@ PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size) } return(OK); + + return r; +} + +/*===========================================================================* + * mini_senda * + *===========================================================================*/ +PRIVATE int mini_senda(struct proc *caller_ptr, asynmsg_t *table, size_t size) +{ + struct priv *privp; + + privp = priv(caller_ptr); + if (!(privp->s_flags & SYS_PROC)) { + printf( "mini_senda: warning caller has no privilege structure\n"); + return(EPERM); + } + + return try_deliver_senda(caller_ptr, table, size); } @@ -1233,6 +1305,17 @@ struct proc *caller_ptr; src_ptr = proc_addr(privp->s_proc_nr); +#ifdef CONFIG_SMP + /* + * Do not copy from a process which does not have a stable address space + * due to VM fiddling with it + */ + if (RTS_ISSET(src_ptr, RTS_VMINHIBIT)) { + src_ptr->p_misc_flags |= MF_SENDA_VM_MISS; + continue; + } +#endif + assert(!(caller_ptr->p_misc_flags & MF_DELIVERMSG)); if ((r = try_one(src_ptr, caller_ptr)) == OK) return(r); @@ -1273,6 +1356,7 @@ PRIVATE int try_one(struct proc *src_ptr, struct proc *dst_ptr) /* 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. @@ -1342,6 +1426,7 @@ store_result: set_sys_bit(priv(dst_ptr)->s_asyn_pending, privp->s_id); } +asyn_error: return(r); } @@ -1379,6 +1464,8 @@ PUBLIC int cancel_async(struct proc *src_ptr, struct proc *dst_ptr) /* 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. @@ -1424,6 +1511,7 @@ PUBLIC int cancel_async(struct proc *src_ptr, struct proc *dst_ptr) privp->s_asynsize = size; } +asyn_error: return(OK); } diff --git a/kernel/proc.h b/kernel/proc.h index 6d212ae72..4f9d07579 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -25,8 +25,8 @@ struct proc { struct segframe p_seg; /* segment descriptors */ proc_nr_t p_nr; /* number of this process (for fast access) */ struct priv *p_priv; /* system privileges structure */ - u32_t p_rts_flags; /* process is runnable only if zero */ - u32_t p_misc_flags; /* flags that do not suspend the process */ + volatile u32_t p_rts_flags; /* process is runnable only if zero */ + volatile u32_t p_misc_flags; /* flags that do not suspend the process */ char p_priority; /* current process priority */ u64_t p_cpu_time_left; /* time left to use the cpu */ @@ -252,6 +252,10 @@ struct proc { #define MF_FLUSH_TLB 0x10000 /* if set, TLB must be flushed before letting this process run again. Currently it only applies to SMP */ +#define MF_SENDA_VM_MISS 0x20000 /* set if a processes wanted to receive an asyn + message from this sender but could not + because of VM modifying the sender's address + space*/ /* Magic process table addresses. */ #define BEG_PROC_ADDR (&proc[0]) diff --git a/kernel/proto.h b/kernel/proto.h index 023d366a2..e9fc457d1 100644 --- a/kernel/proto.h +++ b/kernel/proto.h @@ -67,6 +67,9 @@ _PROTOTYPE( int isokendpt_f, (endpoint_t e, int *p, int f) ); _PROTOTYPE( void proc_no_time, (struct proc *p)); _PROTOTYPE( void reset_proc_accounting, (struct proc *p)); _PROTOTYPE( void flag_account, (struct proc *p, int flag)); +_PROTOTYPE( int try_deliver_senda, (struct proc *caller_ptr, + asynmsg_t *table, + size_t size) ); /* start.c */ _PROTOTYPE( void cstart, (u16_t cs, u16_t ds, u16_t mds, diff --git a/kernel/system/do_vmctl.c b/kernel/system/do_vmctl.c index 65493973f..e5a945f7f 100644 --- a/kernel/system/do_vmctl.c +++ b/kernel/system/do_vmctl.c @@ -160,6 +160,13 @@ PUBLIC int do_vmctl(struct proc * caller, message * m_ptr) */ RTS_UNSET(p, RTS_VMINHIBIT); #ifdef CONFIG_SMP + if (p->p_misc_flags & MF_SENDA_VM_MISS) { + struct priv *privp; + p->p_misc_flags &= ~MF_SENDA_VM_MISS; + privp = priv(p); + try_deliver_senda(p, (asynmsg_t *) privp->s_asyntab, + privp->s_asynsize); + } /* * We don't know whether kernel has the changed mapping * installed to access userspace memory. And if so, on what CPU.