X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ipc%2Futil.c;h=b9f02e9613b89d9cc680b5dadb31ca6d13b8ae26;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=d84ac5198f51ecca21e00a2bf3e340348cf0da07;hpb=a2c21200f1c81b08cb55e417b68150bba439b646;p=linux-2.6.git diff --git a/ipc/util.c b/ipc/util.c index d84ac5198..b9f02e961 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -101,7 +101,7 @@ int ipc_findkey(struct ipc_ids* ids, key_t key) int max_id = ids->max_id; /* - * read_barrier_depends is not needed here + * rcu_dereference() is not needed here * since ipc_ids.sem is held */ for (id = 0; id <= max_id; id++) { @@ -109,7 +109,7 @@ int ipc_findkey(struct ipc_ids* ids, key_t key) if (p==NULL) continue; if (!vx_check(p->xid, VX_IDENT)) - continue; + continue; if (key == p->key) return id; } @@ -138,7 +138,6 @@ static int grow_ary(struct ipc_ids* ids, int newsize) new[i].p = NULL; } old = ids->entries; - i = ids->size; /* * before setting the ids->entries to the new array, there must be a @@ -150,7 +149,7 @@ static int grow_ary(struct ipc_ids* ids, int newsize) smp_wmb(); /* prevent indexing into old array based on new size. */ ids->size = newsize; - ipc_rcu_free(old, sizeof(struct ipc_id)*i); + ipc_rcu_putref(old); return ids->size; } @@ -175,7 +174,7 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) size = grow_ary(ids,size); /* - * read_barrier_depends() is not needed here since + * rcu_dereference()() is not needed here since * ipc_ids.sem is held */ for (id = 0; id < size; id++) { @@ -224,7 +223,7 @@ struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id) BUG(); /* - * do not need a read_barrier_depends() here to force ordering + * do not need a rcu_dereference()() here to force ordering * on Alpha, since the ipc_ids.sem is held. */ p = ids->entries[lid].p; @@ -280,25 +279,47 @@ void ipc_free(void* ptr, int size) kfree(ptr); } -struct ipc_rcu_kmalloc +/* + * rcu allocations: + * There are three headers that are prepended to the actual allocation: + * - during use: ipc_rcu_hdr. + * - during the rcu grace period: ipc_rcu_grace. + * - [only if vmalloc]: ipc_rcu_sched. + * Their lifetime doesn't overlap, thus the headers share the same memory. + * Unlike a normal union, they are right-aligned, thus some container_of + * forward/backward casting is necessary: + */ +struct ipc_rcu_hdr +{ + int refcount; + int is_vmalloc; + void *data[0]; +}; + + +struct ipc_rcu_grace { struct rcu_head rcu; /* "void *" makes sure alignment of following data is sane. */ void *data[0]; }; -struct ipc_rcu_vmalloc +struct ipc_rcu_sched { - struct rcu_head rcu; struct work_struct work; /* "void *" makes sure alignment of following data is sane. */ void *data[0]; }; +#define HDRLEN_KMALLOC (sizeof(struct ipc_rcu_grace) > sizeof(struct ipc_rcu_hdr) ? \ + sizeof(struct ipc_rcu_grace) : sizeof(struct ipc_rcu_hdr)) +#define HDRLEN_VMALLOC (sizeof(struct ipc_rcu_sched) > HDRLEN_KMALLOC ? \ + sizeof(struct ipc_rcu_sched) : HDRLEN_KMALLOC) + static inline int rcu_use_vmalloc(int size) { /* Too big for a single page? */ - if (sizeof(struct ipc_rcu_kmalloc) + size > PAGE_SIZE) + if (HDRLEN_KMALLOC + size > PAGE_SIZE) return 1; return 0; } @@ -320,16 +341,29 @@ void* ipc_rcu_alloc(int size) * workqueue if necessary (for vmalloc). */ if (rcu_use_vmalloc(size)) { - out = vmalloc(sizeof(struct ipc_rcu_vmalloc) + size); - if (out) out += sizeof(struct ipc_rcu_vmalloc); + out = vmalloc(HDRLEN_VMALLOC + size); + if (out) { + out += HDRLEN_VMALLOC; + container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 1; + container_of(out, struct ipc_rcu_hdr, data)->refcount = 1; + } } else { - out = kmalloc(sizeof(struct ipc_rcu_kmalloc)+size, GFP_KERNEL); - if (out) out += sizeof(struct ipc_rcu_kmalloc); + out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL); + if (out) { + out += HDRLEN_KMALLOC; + container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 0; + container_of(out, struct ipc_rcu_hdr, data)->refcount = 1; + } } return out; } +void ipc_rcu_getref(void *ptr) +{ + container_of(ptr, struct ipc_rcu_hdr, data)->refcount++; +} + /** * ipc_schedule_free - free ipc + rcu space * @@ -338,11 +372,13 @@ void* ipc_rcu_alloc(int size) */ static void ipc_schedule_free(struct rcu_head *head) { - struct ipc_rcu_vmalloc *free = - container_of(head, struct ipc_rcu_vmalloc, rcu); + struct ipc_rcu_grace *grace = + container_of(head, struct ipc_rcu_grace, rcu); + struct ipc_rcu_sched *sched = + container_of(&(grace->data[0]), struct ipc_rcu_sched, data[0]); - INIT_WORK(&free->work, vfree, free); - schedule_work(&free->work); + INIT_WORK(&sched->work, vfree, sched); + schedule_work(&sched->work); } /** @@ -353,25 +389,23 @@ static void ipc_schedule_free(struct rcu_head *head) */ static void ipc_immediate_free(struct rcu_head *head) { - struct ipc_rcu_kmalloc *free = - container_of(head, struct ipc_rcu_kmalloc, rcu); + struct ipc_rcu_grace *free = + container_of(head, struct ipc_rcu_grace, rcu); kfree(free); } - - -void ipc_rcu_free(void* ptr, int size) +void ipc_rcu_putref(void *ptr) { - if (rcu_use_vmalloc(size)) { - struct ipc_rcu_vmalloc *free; - free = ptr - sizeof(*free); - call_rcu(&free->rcu, ipc_schedule_free); + if (--container_of(ptr, struct ipc_rcu_hdr, data)->refcount > 0) + return; + + if (container_of(ptr, struct ipc_rcu_hdr, data)->is_vmalloc) { + call_rcu(&container_of(ptr, struct ipc_rcu_grace, data)->rcu, + ipc_schedule_free); } else { - struct ipc_rcu_kmalloc *free; - free = ptr - sizeof(*free); - call_rcu(&free->rcu, ipc_immediate_free); + call_rcu(&container_of(ptr, struct ipc_rcu_grace, data)->rcu, + ipc_immediate_free); } - } /** @@ -486,13 +520,12 @@ struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id) * Note: The following two read barriers are corresponding * to the two write barriers in grow_ary(). They guarantee * the writes are seen in the same order on the read side. - * smp_rmb() has effect on all CPUs. read_barrier_depends() + * smp_rmb() has effect on all CPUs. rcu_dereference() * is used if there are data dependency between two reads, and * has effect only on Alpha. */ smp_rmb(); /* prevent indexing old array with new size */ - entries = ids->entries; - read_barrier_depends(); /*prevent seeing new array unitialized */ + entries = rcu_dereference(ids->entries); out = entries[lid].p; if(out == NULL) { rcu_read_unlock(); @@ -511,6 +544,12 @@ struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id) return out; } +void ipc_lock_by_ptr(struct kern_ipc_perm *perm) +{ + rcu_read_lock(); + spin_lock(&perm->lock); +} + void ipc_unlock(struct kern_ipc_perm* perm) { spin_unlock(&perm->lock);