>From b063c9edbb264cfcbca6c23eee3c85f90cd77ae1 Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Mon, 5 Sep 2016 20:45:38 +0200 Subject: [PATCH] ipc/sem.c: Avoid spin_unlock_wait() experimental, not fully tested! spin_unlock_wait() may be expensive, because it must ensure memory ordering. Test: Would it be faster if an explicit is_locked flag is used? For large arrays, only 1 barrier would be required. Signed-off-by: Manfred Spraul --- ipc/sem.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/ipc/sem.c b/ipc/sem.c index 5e318c5..062ece2d 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -101,6 +101,7 @@ struct sem { */ int sempid; spinlock_t lock; /* spinlock for fine-grained semtimedop */ + int is_locked; /* locked flag */ struct list_head pending_alter; /* pending single-sop operations */ /* that alter the semaphore */ struct list_head pending_const; /* pending single-sop operations */ @@ -282,17 +283,22 @@ static void complexmode_enter(struct sem_array *sma) /* We need a full barrier after seting complex_mode: * The write to complex_mode must be visible - * before we read the first sem->lock spinlock state. + * before we read the first sem->is_locked state. */ smp_store_mb(sma->complex_mode, true); for (i = 0; i < sma->sem_nsems; i++) { sem = sma->sem_base + i; - spin_unlock_wait(&sem->lock); + if (sem->is_locked) { + spin_lock(&sem->lock); + spin_unlock(&sem->lock); + } } /* - * spin_unlock_wait() is not a memory barriers, it is only a - * control barrier. The code must pair with spin_unlock(&sem->lock), + * If spin_lock(); spin_unlock() is used, then everything is + * ordered. Otherwise: Reading sem->is_locked is only a control + * barrier. + * The code must pair with smp_store_release(&sem->is_locked), * thus just the control barrier is insufficient. * * smp_rmb() is sufficient, as writes cannot pass the control barrier. @@ -364,17 +370,16 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops, spin_lock(&sem->lock); /* - * See 51d7d5205d33 - * ("powerpc: Add smp_mb() to arch_spin_is_locked()"): - * A full barrier is required: the write of sem->lock - * must be visible before the read is executed + * set is_locked. It must be ordered before + * reading sma->complex_mode. */ - smp_mb(); + smp_store_mb(sem->is_locked, true); if (!smp_load_acquire(&sma->complex_mode)) { /* fast path successful! */ return sops->sem_num; } + smp_store_release(&sem->is_locked, false); spin_unlock(&sem->lock); } @@ -387,6 +392,8 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops, * back to the fast path. */ spin_lock(&sem->lock); + /* no need for smp_mb, we own the global lock */ + sem->is_locked = true; ipc_unlock_object(&sma->sem_perm); return sops->sem_num; } else { @@ -406,6 +413,7 @@ static inline void sem_unlock(struct sem_array *sma, int locknum) ipc_unlock_object(&sma->sem_perm); } else { struct sem *sem = sma->sem_base + locknum; + smp_store_release(&sem->is_locked, false); spin_unlock(&sem->lock); } } @@ -551,6 +559,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) INIT_LIST_HEAD(&sma->sem_base[i].pending_alter); INIT_LIST_HEAD(&sma->sem_base[i].pending_const); spin_lock_init(&sma->sem_base[i].lock); + sma->sem_base[i].is_locked = false; } sma->complex_count = 0; -- 2.7.4