* [RFC PATCH 0/3] ipc/sem.c: Optimization for reducing spinlock contention @ 2010-04-18 17:07 Manfred Spraul 2010-04-18 17:07 ` [PATCH 1/3] [PATCH] ipc/sem.c: Optimize update_queue() for bulk wakeup calls Manfred Spraul 0 siblings, 1 reply; 4+ messages in thread From: Manfred Spraul @ 2010-04-18 17:07 UTC (permalink / raw) To: LKML; +Cc: Chris Mason, Zach Brown, Jens Axboe, Nick Piggin, Manfred Spraul Hi, The following series of patches tries to fix the spinlock contention reported by Chris Manson: His benchmark exposes problems of the current code: - In the worst case, the algorithm used by update_queue() is O(N^2). Bulk wake-up calls can enter this worst case. The patch series fix that. Note that the benchmark app doesn't expose the problem, it just should be fixed: Real world apps might do the wake-ups in another order than perfect FIFO. - The part of the code that runs within the semaphore array spinlock is significantly larger than necessary. The patch series fixes that. This change is responsible for the main improvement. - The cacheline with the spinlock is also used for a variable that is read in the hot path (sem_base) and for a variable that is unnecessarily written to multiple times (sem_otime). The last step of the series cacheline-aligns the spinlock. With regards to the last patch: I'm not sure if this should be performed: Cacheline aligning a spinlock helps to avoid cacheline trashing among multiple cpus, at the cost of wasting memory. Testing on a large system might show if it's worth the memory. I've tested the series with a modified version of Chris' benchmark app [1]: - before: echo "512 32000 512 512" > /proc/sys/kernel/sem ./sembench -t 250 -w 64 -o 0 -r 30 -b: <<< 250 threads, waking 64 at a time using ipc sem operations main thread burns: 2194274 worker burn count total 6352691 min 21505 max 29230 avg 25410 run time 30 seconds 211756 worker burns per second lifetime 30 seconds 58573142769 ticks. free 745344 contended 7801967 wait 110278262115 avg 14134 1/1000 cpu time 470 <<< - after: echo "512 32000 512 512" > /proc/sys/kernel/sem ./sembench -t 250 -w 64 -o 0 -r 30 -b: <<< 250 threads, waking 64 at a time using ipc sem operations main thread burns: 112890 worker burn count total 7084335 min 27869 max 28769 avg 28337 run time 30 seconds 236144 worker burns per second lifetime 30 seconds 59518717621 ticks. free 6704845 contended 490448 wait 2625106215 avg 5352 1/1000 cpu time 11 <<< i.e.: - 1.1 % cpu time lost by spinning instead of 47.0 % - 6% of the spinlocks spin instead of 91% - 236144 worker burns/sec instead of 211756 << I do not understand that yet. Why not more? << Perhaps an test app interaction: << With the optimization, only 112890 main thread burns << happen. Without, 2194374. The tests were performed on a 4-core AMD Phenom system. Test results from larger systems are apreciated. Attached is the benchmark patch that I've used: With the patch applied, the kernel prints statistics about the semaphore spinlock when a semaphore array is released. Unfortunately, the patch is x86-only (both 32-bit and 64-bit), it uses rdtscll. The main advantage compared to Chris ipc sem patch series is that it doesn't add special codepaths: All operations are optimized, e.g. even operations that use SEM_UNDO. Note that there is room for further improvement, without resorting to per-semaphore spinlocks: It would be possible to: - first add a task to a global sorted list of pending operations. Just a list_add(). - spin_trylock an 2nd per-array spinlock (e.g. "semop_lock") - then drop the existing ipc spinlock. If the spin_trylock was successful: try_atomic_semop() and update_queue(). Otherwise: schedule_work() a worker thread and schedule(). - the worker thread scans the list of pending operations and does the try_atomic_semop()/update_queue() calls. That way, only list_add() and list_del() would be under the existing "big" spinlock. The second spinlock would be accessed with spin_trylock, i.e. no time would be lost due to spinning. But: This would be a separate improvement, I think we should check first if it is really necessary. -- Manfred [1]: There is an interaction between the benchmark app and the scheduler: Obviosly, there is zero contention when everything happens on one core. On my system, the scheduler sometimes keeps everything on one core. Speeding up ipc/sem.c didn't increase the burn count from sembench at all, the scheduler was just able to keep even more tasks on the same cpu. With cpu binding, the behavior is simple to describe: - the wake-up task spends most of the time in semtimedop(), holding the semaphore array spinlock. - the other cpus try to acquire it as well - and spin for a long time, because waking up 64 tasks is a long operation. - the patch series reduce the time that the spinlock is held. I've posted Chris' app with cpu binding before: https://patchwork.kernel.org/patch/93299/ And below is the statistics patch: -------- diff --git a/include/linux/ipc.h b/include/linux/ipc.h index 3b1594d..85b914a 100644 --- a/include/linux/ipc.h +++ b/include/linux/ipc.h @@ -96,6 +96,12 @@ struct kern_ipc_perm mode_t mode; unsigned long seq; void *security; + + int start_sec; + unsigned long long start_tics; + unsigned long long free; + unsigned long long contended; + unsigned long long wait_sum; }; #endif /* __KERNEL__ */ diff --git a/ipc/sem.c b/ipc/sem.c index dbef95b..c9c24a3 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -187,6 +187,29 @@ static inline void sem_putref(struct sem_array *sma) static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) { + int sc = get_seconds(); + unsigned long long tics_now; + + rdtscll(tics_now); + tics_now -= s->sem_perm.start_tics; + sc -= s->sem_perm.start_sec; + + printk(KERN_ERR "sem_rmid for %p:\n",s); + printk(KERN_ERR "lifetime %d sec %Ld ticks.\n", + sc, tics_now); + printk(KERN_ERR "free %Ld contended %Ld\n", + s->sem_perm.free, s->sem_perm.contended); + if (s->sem_perm.contended != 0) { + unsigned long long fraction; + tics_now *= num_online_cpus(); + + fraction = (s->sem_perm.wait_sum*1000)/tics_now; + printk(KERN_ERR "wait %Ld avg %Ld 1/1000 cpu time %Ld\n", + s->sem_perm.wait_sum, + s->sem_perm.wait_sum/s->sem_perm.contended, + fraction); + } + ipc_rmid(&sem_ids(ns), &s->sem_perm); } diff --git a/ipc/util.c b/ipc/util.c index 79ce84e..3bc31f9 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -260,6 +260,13 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) return -ENOSPC; spin_lock_init(&new->lock); + + new->free = 0; + new->contended = 0; + new->wait_sum = 0; + new->start_sec = get_seconds(); + rdtscll(new->start_tics); + new->deleted = 0; rcu_read_lock(); spin_lock(&new->lock); @@ -701,7 +708,16 @@ struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) return ERR_PTR(-EINVAL); } - spin_lock(&out->lock); + if (spin_trylock(&out->lock)) { + out->free++; + } else { + unsigned long long t1, t2; + rdtscll(t1); + spin_lock(&out->lock); + rdtscll(t2); + out->contended++; + out->wait_sum += (t2-t1); + } /* ipc_rmid() may have already freed the ID while ipc_lock * was spinning: here verify that the structure is still valid ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/3] [PATCH] ipc/sem.c: Optimize update_queue() for bulk wakeup calls 2010-04-18 17:07 [RFC PATCH 0/3] ipc/sem.c: Optimization for reducing spinlock contention Manfred Spraul @ 2010-04-18 17:07 ` Manfred Spraul 2010-04-18 17:07 ` [PATCH 2/3] [PATCH] ipc/sem.c: move wake_up_process out of the spinlock section Manfred Spraul 0 siblings, 1 reply; 4+ messages in thread From: Manfred Spraul @ 2010-04-18 17:07 UTC (permalink / raw) To: LKML; +Cc: Chris Mason, Zach Brown, Jens Axboe, Nick Piggin, Manfred Spraul The SysV semaphore code allows to perform multiple operations on all semaphores in the array as atomic operations. After a modification, update_queue() checks which of the waiting tasks can complete. The algorithm that is used to identify the tasks is O(N^2) in the worst case. The patch fixes that for the case of an array where all sleeping tasks are single sembuf operations and a multi-sembuf operation is used to wake up multiple tasks. --- ipc/sem.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 98 insertions(+), 14 deletions(-) diff --git a/ipc/sem.c b/ipc/sem.c index dbef95b..efadb6d 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -434,6 +434,69 @@ static void unlink_queue(struct sem_array *sma, struct sem_queue *q) sma->complex_count--; } +/** check_restart(sma, q) + * @sma: semaphore array + * @q: the operation that just completed + * + * update_queue is O(N^2) when it restarts scanning the whole queue of + * waiting operations. Therefore this function checks if the restart is + * really necessary. It is called after a previously waiting operation + * was completed. + */ +static int check_restart(struct sem_array *sma, struct sem_queue *q) +{ + struct sem * curr; + struct sem_queue *h; + + /* if the operation didn't modify the array, then no restart */ + if (q->alter == 0) + return 0; + + /* pending complex operations are too difficult to analyse */ + if (sma->complex_count) + return 1; + + /* we were a sleeping complex operation. Too difficult */ + if (q->nsops > 1) + return 1; + + curr = sma->sem_base + q->sops[0].sem_num; + + /* No-one waits on this queue */ + if (list_empty(&curr->sem_pending)) + return 0; + + /* the new semaphore value */ + if (curr->semval) { + /* It is impossible that someone waits for the new value: + * - q is a previously sleeping simple operation that + * altered the array. It must be a decrement, because + * simple increments never sleep. + * - The value is not 0, thus wait-for-zero won't proceed. + * - If there are older (higher priority) decrements + * in the queue, then they have observed the original + * semval value and couldn't proceed. The operation + * decremented to value - thus they won't proceed either. + */ + BUG_ON(q->sops[0].sem_op >= 0); + return 0; + } + /* + * semval is 0. Check if there are wait-for-zero semops. + * They must be the first entries in the per-semaphore simple queue + */ + h=list_first_entry(&curr->sem_pending, struct sem_queue, simple_list); + BUG_ON(h->nsops != 1); + BUG_ON(h->sops[0].sem_num != q->sops[0].sem_num); + + /* Yes, there is a wait-for-zero semop. Restart */ + if (h->sops[0].sem_op == 0) + return 1; + + /* Again - no-one is waiting for the new value. */ + return 0; +} + /** * update_queue(sma, semnum): Look for tasks that can be completed. @@ -469,7 +532,7 @@ static void update_queue(struct sem_array *sma, int semnum) again: walk = pending_list->next; while (walk != pending_list) { - int error, alter; + int error, restart; q = (struct sem_queue *)((char *)walk - offset); walk = walk->next; @@ -494,22 +557,42 @@ again: unlink_queue(sma, q); - /* - * The next operation that must be checked depends on the type - * of the completed operation: - * - if the operation modified the array, then restart from the - * head of the queue and check for threads that might be - * waiting for the new semaphore values. - * - if the operation didn't modify the array, then just - * continue. - */ - alter = q->alter; + if (error) + restart = 0; + else + restart = check_restart(sma, q); + wake_up_sem_queue(q, error); - if (alter && !error) + if (restart) goto again; } } +/** do_smart_update(sma, sops, nsops): Optimized update_queue + * @sma: semaphore array + * @sops: operations that were performed + * @nsops: number of operations + * + * do_smart_update() does the required called to update_queue, based on the + * actual changes that were performed on the semaphore array. + */ +void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsops) +{ + int i; + + if (sma->complex_count) { + update_queue(sma, -1); + return; + } + + for (i=0;i<nsops;i++) { + if (sops[i].sem_op > 0 || + (sops[i].sem_op < 0 && sma->sem_base[sops[i].sem_num].semval == 0)) + update_queue(sma, sops[i].sem_num); + } +} + + /* The following counts are associated to each semaphore: * semncnt number of tasks waiting on semval being nonzero * semzcnt number of tasks waiting on semval being zero @@ -1224,8 +1307,9 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current)); if (error <= 0) { - if (alter && error == 0) - update_queue(sma, (nsops == 1) ? sops[0].sem_num : -1); + if (alter && error == 0) { + do_smart_update(sma, sops, nsops); + } goto out_unlock_free; } -- 1.6.6.1 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 2/3] [PATCH] ipc/sem.c: move wake_up_process out of the spinlock section 2010-04-18 17:07 ` [PATCH 1/3] [PATCH] ipc/sem.c: Optimize update_queue() for bulk wakeup calls Manfred Spraul @ 2010-04-18 17:07 ` Manfred Spraul 2010-04-18 17:07 ` [PATCH 3/3] [PATCH] ipc/sem.c: cacheline align the ipc spinlock for semaphores Manfred Spraul 0 siblings, 1 reply; 4+ messages in thread From: Manfred Spraul @ 2010-04-18 17:07 UTC (permalink / raw) To: LKML; +Cc: Chris Mason, Zach Brown, Jens Axboe, Nick Piggin, Manfred Spraul For the sysv sem code, waking up a tasks consists out of two steps: - the right tasks must be identified. - they must be woken up. Right now, both steps run while the array spinlock is held. This patch reorders the code and moves the actual wake_up_process() behind the point where the spinlock is dropped. --- ipc/sem.c | 111 +++++++++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 79 insertions(+), 32 deletions(-) diff --git a/ipc/sem.c b/ipc/sem.c index efadb6d..34ae151 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -381,7 +381,6 @@ static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops, sop--; } - sma->sem_otime = get_seconds(); return 0; out_of_range: @@ -404,25 +403,41 @@ undo: return result; } -/* - * Wake up a process waiting on the sem queue with a given error. - * The queue is invalid (may not be accessed) after the function returns. +/** wake_up_sem_queue_prepare(q, error): Prepare wake-up + * @q: queue entry that must be signaled + * @error: Error value for the signal + * + * Prepare the wake-up of the queue entry q. */ -static void wake_up_sem_queue(struct sem_queue *q, int error) +static void wake_up_sem_queue_prepare(struct list_head *pt, struct sem_queue *q, int error) { - /* - * Hold preempt off so that we don't get preempted and have the - * wakee busy-wait until we're scheduled back on. We're holding - * locks here so it may not strictly be needed, however if the - * locks become preemptible then this prevents such a problem. - */ - preempt_disable(); + if (list_empty(pt)) { + /* + * Hold preempt off so that we don't get preempted and have the + * wakee busy-wait until we're scheduled back on. + */ + preempt_disable(); + } q->status = IN_WAKEUP; - wake_up_process(q->sleeper); - /* hands-off: q can disappear immediately after writing q->status. */ - smp_wmb(); - q->status = error; - preempt_enable(); + q->pid = error; + + list_add_tail(&q->simple_list, pt); +} + +static void wake_up_sem_queue_do(struct list_head *pt) +{ + struct sem_queue *q, *t; + int did_something; + + did_something = !list_empty(pt); + list_for_each_entry_safe(q, t, pt, simple_list) { + wake_up_process(q->sleeper); + /* hands-off: q can disappear immediately after writing q->status. */ + smp_wmb(); + q->status = q->pid; + } + if (did_something) + preempt_enable(); } static void unlink_queue(struct sem_array *sma, struct sem_queue *q) @@ -502,17 +517,22 @@ static int check_restart(struct sem_array *sma, struct sem_queue *q) * update_queue(sma, semnum): Look for tasks that can be completed. * @sma: semaphore array. * @semnum: semaphore that was modified. + * @pt: list head for the tasks that must be woken up. * * update_queue must be called after a semaphore in a semaphore array * was modified. If multiple semaphore were modified, then @semnum * must be set to -1. + * The tasks that must be woken up are added to @pt. The return code + * is stored in q->pid. + * The function return 1 if at least one array variable was modified. */ -static void update_queue(struct sem_array *sma, int semnum) +static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt) { struct sem_queue *q; struct list_head *walk; struct list_head *pending_list; int offset; + int retval; /* if there are complex operations around, then knowing the semaphore * that was modified doesn't help us. Assume that multiple semaphores @@ -556,40 +576,54 @@ again: continue; unlink_queue(sma, q); + if (q->alter) + retval = 1; if (error) restart = 0; else restart = check_restart(sma, q); - wake_up_sem_queue(q, error); + wake_up_sem_queue_prepare(pt, q, error); if (restart) goto again; } + return retval; } -/** do_smart_update(sma, sops, nsops): Optimized update_queue +/** do_smart_update(sma, sops, nsops, otime, pt): Optimized update_queue * @sma: semaphore array * @sops: operations that were performed * @nsops: number of operations + * @otime: force setting otime + * @pt: list head of the tasks that must be woken up. * * do_smart_update() does the required called to update_queue, based on the * actual changes that were performed on the semaphore array. + * Note that the function does not do the actual wake-up: the caller is + * responsible for calling wake_up_sem_queue_do(@pt). + * It is safe to perform this call after dropping all locks. */ -void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsops) +void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsops, + int otime, struct list_head *pt) { int i; if (sma->complex_count) { - update_queue(sma, -1); - return; + if (update_queue(sma, -1, pt)) + otime = 1; + goto done; } for (i=0;i<nsops;i++) { if (sops[i].sem_op > 0 || (sops[i].sem_op < 0 && sma->sem_base[sops[i].sem_num].semval == 0)) - update_queue(sma, sops[i].sem_num); + if (update_queue(sma, sops[i].sem_num, pt)) + otime = 1; } +done: + if (otime) + sma->sem_otime = get_seconds(); } @@ -655,6 +689,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) struct sem_undo *un, *tu; struct sem_queue *q, *tq; struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); + struct list_head tasks; /* Free the existing undo structures for this semaphore set. */ assert_spin_locked(&sma->sem_perm.lock); @@ -668,15 +703,17 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) } /* Wake up all pending processes and let them fail with EIDRM. */ + INIT_LIST_HEAD(&tasks); list_for_each_entry_safe(q, tq, &sma->sem_pending, list) { unlink_queue(sma, q); - wake_up_sem_queue(q, -EIDRM); + wake_up_sem_queue_prepare(&tasks, q, -EIDRM); } /* Remove the semaphore set from the IDR */ sem_rmid(ns, sma); sem_unlock(sma); + wake_up_sem_queue_do(&tasks); ns->used_sems -= sma->sem_nsems; security_sem_free(sma); ipc_rcu_putref(sma); @@ -798,11 +835,13 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, ushort fast_sem_io[SEMMSL_FAST]; ushort* sem_io = fast_sem_io; int nsems; + struct list_head tasks; sma = sem_lock_check(ns, semid); if (IS_ERR(sma)) return PTR_ERR(sma); + INIT_LIST_HEAD(&tasks); nsems = sma->sem_nsems; err = -EACCES; @@ -890,7 +929,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, } sma->sem_ctime = get_seconds(); /* maybe some queued-up processes were waiting for this */ - update_queue(sma, -1); + do_smart_update(sma, NULL, 0, 0, &tasks); err = 0; goto out_unlock; } @@ -932,13 +971,15 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, curr->sempid = task_tgid_vnr(current); sma->sem_ctime = get_seconds(); /* maybe some queued-up processes were waiting for this */ - update_queue(sma, semnum); + do_smart_update(sma, NULL, 0, 0, &tasks); err = 0; goto out_unlock; } } out_unlock: sem_unlock(sma); + wake_up_sem_queue_do(&tasks); + out_free: if(sem_io != fast_sem_io) ipc_free(sem_io, sizeof(ushort)*nsems); @@ -1212,6 +1253,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, struct sem_queue queue; unsigned long jiffies_left = 0; struct ipc_namespace *ns; + struct list_head tasks; ns = current->nsproxy->ipc_ns; @@ -1260,6 +1302,8 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, } else un = NULL; + INIT_LIST_HEAD(&tasks); + sma = sem_lock_check(ns, semid); if (IS_ERR(sma)) { if (un) @@ -1307,9 +1351,8 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current)); if (error <= 0) { - if (alter && error == 0) { - do_smart_update(sma, sops, nsops); - } + if (alter && error == 0) + do_smart_update(sma, sops, nsops, 1, &tasks); goto out_unlock_free; } @@ -1386,6 +1429,8 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, out_unlock_free: sem_unlock(sma); + + wake_up_sem_queue_do(&tasks); out_free: if(sops != fast_sops) kfree(sops); @@ -1446,6 +1491,7 @@ void exit_sem(struct task_struct *tsk) for (;;) { struct sem_array *sma; struct sem_undo *un; + struct list_head tasks; int semid; int i; @@ -1509,10 +1555,11 @@ void exit_sem(struct task_struct *tsk) semaphore->sempid = task_tgid_vnr(current); } } - sma->sem_otime = get_seconds(); /* maybe some queued-up processes were waiting for this */ - update_queue(sma, -1); + INIT_LIST_HEAD(&tasks); + do_smart_update(sma, NULL, 0, 1, &tasks); sem_unlock(sma); + wake_up_sem_queue_do(&tasks); call_rcu(&un->rcu, free_un); } -- 1.6.6.1 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 3/3] [PATCH] ipc/sem.c: cacheline align the ipc spinlock for semaphores 2010-04-18 17:07 ` [PATCH 2/3] [PATCH] ipc/sem.c: move wake_up_process out of the spinlock section Manfred Spraul @ 2010-04-18 17:07 ` Manfred Spraul 0 siblings, 0 replies; 4+ messages in thread From: Manfred Spraul @ 2010-04-18 17:07 UTC (permalink / raw) To: LKML; +Cc: Chris Mason, Zach Brown, Jens Axboe, Nick Piggin, Manfred Spraul This patch cacheline aligns the spinlock for sysv semaphores: Without the patch, the spinlock and sem_otime [written by every semop that modified the array] and sem_base [read in the hot path of try_atomic_semop()] can be in the same cacheline. --- include/linux/sem.h | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/include/linux/sem.h b/include/linux/sem.h index 8a4adbe..f2961af 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -79,6 +79,7 @@ struct seminfo { #ifdef __KERNEL__ #include <asm/atomic.h> #include <linux/rcupdate.h> +#include <linux/cache.h> struct task_struct; @@ -91,7 +92,8 @@ struct sem { /* One sem_array data structure for each set of semaphores in the system. */ struct sem_array { - struct kern_ipc_perm sem_perm; /* permissions .. see ipc.h */ + struct kern_ipc_perm ____cacheline_aligned_in_smp + sem_perm; /* permissions .. see ipc.h */ time_t sem_otime; /* last semop time */ time_t sem_ctime; /* last change time */ struct sem *sem_base; /* ptr to first semaphore in array */ -- 1.6.6.1 ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2010-04-18 17:08 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2010-04-18 17:07 [RFC PATCH 0/3] ipc/sem.c: Optimization for reducing spinlock contention Manfred Spraul 2010-04-18 17:07 ` [PATCH 1/3] [PATCH] ipc/sem.c: Optimize update_queue() for bulk wakeup calls Manfred Spraul 2010-04-18 17:07 ` [PATCH 2/3] [PATCH] ipc/sem.c: move wake_up_process out of the spinlock section Manfred Spraul 2010-04-18 17:07 ` [PATCH 3/3] [PATCH] ipc/sem.c: cacheline align the ipc spinlock for semaphores Manfred Spraul
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®