diff options
| author | Rik van Riel <riel@surriel.com> | 2013-04-30 22:15:44 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-05-01 11:12:58 -0400 |
| commit | 6062a8dc0517bce23e3c2f7d2fea5e22411269a3 (patch) | |
| tree | e1dd1553167fccb726a8aa9352b27ba14f188374 /ipc | |
| parent | 9f1bc2c9022c1d4944c4a1a44c2f365487420aca (diff) | |
ipc,sem: fine grained locking for semtimedop
Introduce finer grained locking for semtimedop, to handle the common case
of a program wanting to manipulate one semaphore from an array with
multiple semaphores.
If the call is a semop manipulating just one semaphore in an array with
multiple semaphores, only take the lock for that semaphore itself.
If the call needs to manipulate multiple semaphores, or another caller is
in a transaction that manipulates multiple semaphores, the sem_array lock
is taken, as well as all the locks for the individual semaphores.
On a 24 CPU system, performance numbers with the semop-multi
test with N threads and N semaphores, look like this:
vanilla Davidlohr's Davidlohr's + Davidlohr's +
threads patches rwlock patches v3 patches
10 610652 726325 1783589 2142206
20 341570 365699 1520453 1977878
30 288102 307037 1498167 2037995
40 290714 305955 1612665 2256484
50 288620 312890 1733453 2650292
60 289987 306043 1649360 2388008
70 291298 306347 1723167 2717486
80 290948 305662 1729545 2763582
90 290996 306680 1736021 2757524
100 292243 306700 1773700 3059159
[davidlohr.bueso@hp.com: do not call sem_lock when bogus sma]
[davidlohr.bueso@hp.com: make refcounter atomic]
Signed-off-by: Rik van Riel <riel@redhat.com>
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Cc: Chegu Vinod <chegu_vinod@hp.com>
Cc: Jason Low <jason.low2@hp.com>
Reviewed-by: Michel Lespinasse <walken@google.com>
Cc: Peter Hurley <peter@hurleysoftware.com>
Cc: Stanislav Kinsbursky <skinsbursky@parallels.com>
Tested-by: Emmanuel Benisty <benisty.e@gmail.com>
Tested-by: Sedat Dilek <sedat.dilek@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
| -rw-r--r-- | ipc/msg.c | 7 | ||||
| -rw-r--r-- | ipc/sem.c | 271 | ||||
| -rw-r--r-- | ipc/util.c | 48 | ||||
| -rw-r--r-- | ipc/util.h | 2 |
4 files changed, 203 insertions, 125 deletions
| @@ -687,7 +687,12 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext, | |||
| 687 | goto out_unlock_free; | 687 | goto out_unlock_free; |
| 688 | } | 688 | } |
| 689 | ss_add(msq, &s); | 689 | ss_add(msq, &s); |
| 690 | ipc_rcu_getref(msq); | 690 | |
| 691 | if (!ipc_rcu_getref(msq)) { | ||
| 692 | err = -EIDRM; | ||
| 693 | goto out_unlock_free; | ||
| 694 | } | ||
| 695 | |||
| 691 | msg_unlock(msq); | 696 | msg_unlock(msq); |
| 692 | schedule(); | 697 | schedule(); |
| 693 | 698 | ||
| @@ -94,6 +94,7 @@ | |||
| 94 | struct sem { | 94 | struct sem { |
| 95 | int semval; /* current value */ | 95 | int semval; /* current value */ |
| 96 | int sempid; /* pid of last operation */ | 96 | int sempid; /* pid of last operation */ |
| 97 | spinlock_t lock; /* spinlock for fine-grained semtimedop */ | ||
| 97 | struct list_head sem_pending; /* pending single-sop operations */ | 98 | struct list_head sem_pending; /* pending single-sop operations */ |
| 98 | }; | 99 | }; |
| 99 | 100 | ||
| @@ -137,7 +138,6 @@ struct sem_undo_list { | |||
| 137 | 138 | ||
| 138 | #define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS]) | 139 | #define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS]) |
| 139 | 140 | ||
| 140 | #define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm) | ||
| 141 | #define sem_checkid(sma, semid) ipc_checkid(&sma->sem_perm, semid) | 141 | #define sem_checkid(sma, semid) ipc_checkid(&sma->sem_perm, semid) |
| 142 | 142 | ||
| 143 | static int newary(struct ipc_namespace *, struct ipc_params *); | 143 | static int newary(struct ipc_namespace *, struct ipc_params *); |
| @@ -190,10 +190,89 @@ void __init sem_init (void) | |||
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | /* | 192 | /* |
| 193 | * If the request contains only one semaphore operation, and there are | ||
| 194 | * no complex transactions pending, lock only the semaphore involved. | ||
| 195 | * Otherwise, lock the entire semaphore array, since we either have | ||
| 196 | * multiple semaphores in our own semops, or we need to look at | ||
| 197 | * semaphores from other pending complex operations. | ||
| 198 | * | ||
| 199 | * Carefully guard against sma->complex_count changing between zero | ||
| 200 | * and non-zero while we are spinning for the lock. The value of | ||
| 201 | * sma->complex_count cannot change while we are holding the lock, | ||
| 202 | * so sem_unlock should be fine. | ||
| 203 | * | ||
| 204 | * The global lock path checks that all the local locks have been released, | ||
| 205 | * checking each local lock once. This means that the local lock paths | ||
| 206 | * cannot start their critical sections while the global lock is held. | ||
| 207 | */ | ||
| 208 | static inline int sem_lock(struct sem_array *sma, struct sembuf *sops, | ||
| 209 | int nsops) | ||
| 210 | { | ||
| 211 | int locknum; | ||
| 212 | again: | ||
| 213 | if (nsops == 1 && !sma->complex_count) { | ||
| 214 | struct sem *sem = sma->sem_base + sops->sem_num; | ||
| 215 | |||
| 216 | /* Lock just the semaphore we are interested in. */ | ||
| 217 | spin_lock(&sem->lock); | ||
| 218 | |||
| 219 | /* | ||
| 220 | * If sma->complex_count was set while we were spinning, | ||
| 221 | * we may need to look at things we did not lock here. | ||
| 222 | */ | ||
| 223 | if (unlikely(sma->complex_count)) { | ||
| 224 | spin_unlock(&sem->lock); | ||
| 225 | goto lock_array; | ||
| 226 | } | ||
| 227 | |||
| 228 | /* | ||
| 229 | * Another process is holding the global lock on the | ||
| 230 | * sem_array; we cannot enter our critical section, | ||
| 231 | * but have to wait for the global lock to be released. | ||
| 232 | */ | ||
| 233 | if (unlikely(spin_is_locked(&sma->sem_perm.lock))) { | ||
| 234 | spin_unlock(&sem->lock); | ||
| 235 | spin_unlock_wait(&sma->sem_perm.lock); | ||
| 236 | goto again; | ||
| 237 | } | ||
| 238 | |||
| 239 | locknum = sops->sem_num; | ||
| 240 | } else { | ||
| 241 | int i; | ||
| 242 | /* | ||
| 243 | * Lock the semaphore array, and wait for all of the | ||
| 244 | * individual semaphore locks to go away. The code | ||
| 245 | * above ensures no new single-lock holders will enter | ||
| 246 | * their critical section while the array lock is held. | ||
| 247 | */ | ||
| 248 | lock_array: | ||
| 249 | spin_lock(&sma->sem_perm.lock); | ||
| 250 | for (i = 0; i < sma->sem_nsems; i++) { | ||
| 251 | struct sem *sem = sma->sem_base + i; | ||
| 252 | spin_unlock_wait(&sem->lock); | ||
| 253 | } | ||
| 254 | locknum = -1; | ||
| 255 | } | ||
| 256 | return locknum; | ||
| 257 | } | ||
| 258 | |||
| 259 | static inline void sem_unlock(struct sem_array *sma, int locknum) | ||
| 260 | { | ||
| 261 | if (locknum == -1) { | ||
| 262 | spin_unlock(&sma->sem_perm.lock); | ||
| 263 | } else { | ||
| 264 | struct sem *sem = sma->sem_base + locknum; | ||
| 265 | spin_unlock(&sem->lock); | ||
| 266 | } | ||
| 267 | rcu_read_unlock(); | ||
| 268 | } | ||
| 269 | |||
| 270 | /* | ||
| 193 | * sem_lock_(check_) routines are called in the paths where the rw_mutex | 271 | * sem_lock_(check_) routines are called in the paths where the rw_mutex |
| 194 | * is not held. | 272 | * is not held. |
| 195 | */ | 273 | */ |
| 196 | static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns, int id) | 274 | static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns, |
| 275 | int id, struct sembuf *sops, int nsops, int *locknum) | ||
| 197 | { | 276 | { |
| 198 | struct kern_ipc_perm *ipcp; | 277 | struct kern_ipc_perm *ipcp; |
| 199 | struct sem_array *sma; | 278 | struct sem_array *sma; |
| @@ -205,7 +284,8 @@ static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns, int id | |||
| 205 | goto err; | 284 | goto err; |
| 206 | } | 285 | } |
| 207 | 286 | ||
| 208 | spin_lock(&ipcp->lock); | 287 | sma = container_of(ipcp, struct sem_array, sem_perm); |
| 288 | *locknum = sem_lock(sma, sops, nsops); | ||
| 209 | 289 | ||
| 210 | /* ipc_rmid() may have already freed the ID while sem_lock | 290 | /* ipc_rmid() may have already freed the ID while sem_lock |
| 211 | * was spinning: verify that the structure is still valid | 291 | * was spinning: verify that the structure is still valid |
| @@ -213,7 +293,7 @@ static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns, int id | |||
| 213 | if (!ipcp->deleted) | 293 | if (!ipcp->deleted) |
| 214 | return container_of(ipcp, struct sem_array, sem_perm); | 294 | return container_of(ipcp, struct sem_array, sem_perm); |
| 215 | 295 | ||
| 216 | spin_unlock(&ipcp->lock); | 296 | sem_unlock(sma, *locknum); |
| 217 | sma = ERR_PTR(-EINVAL); | 297 | sma = ERR_PTR(-EINVAL); |
| 218 | err: | 298 | err: |
| 219 | rcu_read_unlock(); | 299 | rcu_read_unlock(); |
| @@ -230,17 +310,6 @@ static inline struct sem_array *sem_obtain_object(struct ipc_namespace *ns, int | |||
| 230 | return container_of(ipcp, struct sem_array, sem_perm); | 310 | return container_of(ipcp, struct sem_array, sem_perm); |
| 231 | } | 311 | } |
| 232 | 312 | ||
| 233 | static inline struct sem_array *sem_lock_check(struct ipc_namespace *ns, | ||
| 234 | int id) | ||
| 235 | { | ||
| 236 | struct kern_ipc_perm *ipcp = ipc_lock_check(&sem_ids(ns), id); | ||
| 237 | |||
| 238 | if (IS_ERR(ipcp)) | ||
| 239 | return ERR_CAST(ipcp); | ||
| 240 | |||
| 241 | return container_of(ipcp, struct sem_array, sem_perm); | ||
| 242 | } | ||
| 243 | |||
| 244 | static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns, | 313 | static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns, |
| 245 | int id) | 314 | int id) |
| 246 | { | 315 | { |
| @@ -254,21 +323,21 @@ static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns | |||
| 254 | 323 | ||
| 255 | static inline void sem_lock_and_putref(struct sem_array *sma) | 324 | static inline void sem_lock_and_putref(struct sem_array *sma) |
| 256 | { | 325 | { |
| 257 | ipc_lock_by_ptr(&sma->sem_perm); | 326 | rcu_read_lock(); |
| 327 | sem_lock(sma, NULL, -1); | ||
| 258 | ipc_rcu_putref(sma); | 328 | ipc_rcu_putref(sma); |
| 259 | } | 329 | } |
| 260 | 330 | ||
| 261 | static inline void sem_getref_and_unlock(struct sem_array *sma) | 331 | static inline void sem_getref_and_unlock(struct sem_array *sma) |
| 262 | { | 332 | { |
| 263 | ipc_rcu_getref(sma); | 333 | WARN_ON_ONCE(!ipc_rcu_getref(sma)); |
| 264 | ipc_unlock(&(sma)->sem_perm); | ||
