aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/sem.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-05-03 18:04:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-05-04 14:24:00 -0400
commit6d49dab8ae06c6d35a4d0967364a9ecbe8fdea2c (patch)
tree7f28ea59e2c52d912faf696d38e9577ffb1c29c3 /ipc/sem.c
parentce857229e0c3adc211944a13a5579ef84fd7b4af (diff)
ipc: move rcu_read_unlock() out of sem_unlock() and into callers
The IPC locking is a mess, and sem_unlock() unlocks not only the semaphore spinlock, it also drops the rcu read lock. Unlike sem_lock(), which just gets the spin-lock, and expects the caller to get the rcu read lock. This all makes things very hard to follow, and it's very confusing when you take the rcu read lock in one function, and then release it in another. And it has caused actual bugs: the sem_obtain_lock() function ended up dropping the RCU read lock twice in one error path, because it first did the sem_unlock(), and then did a rcu_read_unlock() to match the rcu_read_lock() it had done. This is just a totally mindless "remove rcu_read_unlock() from sem_unlock() and add it immediately after each caller" (except for the aforementioned bug where we did too many rcu_read_unlock(), and in find_alloc_undo() where we just got the rcu_read_lock() to correct for the fact that sem_unlock would immediately drop it again). We can (and should) clean things up further, but this fixes the bug with the minimal amount of subtlety. Reviewed-by: Davidlohr Bueso <davidlohr.bueso@hp.com> Cc: Rik van Riel <riel@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc/sem.c')
-rw-r--r--ipc/sem.c19
1 files changed, 17 insertions, 2 deletions
diff --git a/ipc/sem.c b/ipc/sem.c
index 4734e9c2a98a..4b4139f6ad5c 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -264,7 +264,6 @@ static inline void sem_unlock(struct sem_array *sma, int locknum)
264 struct sem *sem = sma->sem_base + locknum; 264 struct sem *sem = sma->sem_base + locknum;
265 spin_unlock(&sem->lock); 265 spin_unlock(&sem->lock);
266 } 266 }
267 rcu_read_unlock();
268} 267}
269 268
270/* 269/*
@@ -332,6 +331,7 @@ static inline void sem_putref(struct sem_array *sma)
332{ 331{
333 sem_lock_and_putref(sma); 332 sem_lock_and_putref(sma);
334 sem_unlock(sma, -1); 333 sem_unlock(sma, -1);
334 rcu_read_unlock();
335} 335}
336 336
337static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) 337static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
@@ -435,6 +435,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
435 sma->sem_nsems = nsems; 435 sma->sem_nsems = nsems;
436 sma->sem_ctime = get_seconds(); 436 sma->sem_ctime = get_seconds();
437 sem_unlock(sma, -1); 437 sem_unlock(sma, -1);
438 rcu_read_unlock();
438 439
439 return sma->sem_perm.id; 440 return sma->sem_perm.id;
440} 441}
@@ -874,6 +875,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
874 /* Remove the semaphore set from the IDR */ 875 /* Remove the semaphore set from the IDR */
875 sem_rmid(ns, sma); 876 sem_rmid(ns, sma);
876 sem_unlock(sma, -1); 877 sem_unlock(sma, -1);
878 rcu_read_unlock();
877 879
878 wake_up_sem_queue_do(&tasks); 880 wake_up_sem_queue_do(&tasks);
879 ns->used_sems -= sma->sem_nsems; 881 ns->used_sems -= sma->sem_nsems;
@@ -1055,6 +1057,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
1055 /* maybe some queued-up processes were waiting for this */ 1057 /* maybe some queued-up processes were waiting for this */
1056 do_smart_update(sma, NULL, 0, 0, &tasks); 1058 do_smart_update(sma, NULL, 0, 0, &tasks);
1057 sem_unlock(sma, -1); 1059 sem_unlock(sma, -1);
1060 rcu_read_unlock();
1058 wake_up_sem_queue_do(&tasks); 1061 wake_up_sem_queue_do(&tasks);
1059 return 0; 1062 return 0;
1060} 1063}
@@ -1104,10 +1107,12 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1104 if(nsems > SEMMSL_FAST) { 1107 if(nsems > SEMMSL_FAST) {
1105 if (!ipc_rcu_getref(sma)) { 1108 if (!ipc_rcu_getref(sma)) {
1106 sem_unlock(sma, -1); 1109 sem_unlock(sma, -1);
1110 rcu_read_unlock();
1107 err = -EIDRM; 1111 err = -EIDRM;
1108 goto out_free; 1112 goto out_free;
1109 } 1113 }
1110 sem_unlock(sma, -1); 1114 sem_unlock(sma, -1);
1115 rcu_read_unlock();
1111 sem_io = ipc_alloc(sizeof(ushort)*nsems); 1116 sem_io = ipc_alloc(sizeof(ushort)*nsems);
1112 if(sem_io == NULL) { 1117 if(sem_io == NULL) {
1113 sem_putref(sma); 1118 sem_putref(sma);
@@ -1117,6 +1122,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1117 sem_lock_and_putref(sma); 1122 sem_lock_and_putref(sma);
1118 if (sma->sem_perm.deleted) { 1123 if (sma->sem_perm.deleted) {
1119 sem_unlock(sma, -1); 1124 sem_unlock(sma, -1);
1125 rcu_read_unlock();
1120 err = -EIDRM; 1126 err = -EIDRM;
1121 goto out_free; 1127 goto out_free;
1122 } 1128 }
@@ -1124,6 +1130,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1124 for (i = 0; i < sma->sem_nsems; i++) 1130 for (i = 0; i < sma->sem_nsems; i++)
1125 sem_io[i] = sma->sem_base[i].semval; 1131 sem_io[i] = sma->sem_base[i].semval;
1126 sem_unlock(sma, -1); 1132 sem_unlock(sma, -1);
1133 rcu_read_unlock();
1127 err = 0; 1134 err = 0;
1128 if(copy_to_user(array, sem_io, nsems*sizeof(ushort))) 1135 if(copy_to_user(array, sem_io, nsems*sizeof(ushort)))
1129 err = -EFAULT; 1136 err = -EFAULT;
@@ -1164,6 +1171,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1164 sem_lock_and_putref(sma); 1171 sem_lock_and_putref(sma);
1165 if (sma->sem_perm.deleted) { 1172 if (sma->sem_perm.deleted) {
1166 sem_unlock(sma, -1); 1173 sem_unlock(sma, -1);
1174 rcu_read_unlock();
1167 err = -EIDRM; 1175 err = -EIDRM;
1168 goto out_free; 1176 goto out_free;
1169 } 1177 }
@@ -1210,6 +1218,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1210 1218
1211out_unlock: 1219out_unlock:
1212 sem_unlock(sma, -1); 1220 sem_unlock(sma, -1);
1221 rcu_read_unlock();
1213out_wakeup: 1222out_wakeup:
1214 wake_up_sem_queue_do(&tasks); 1223 wake_up_sem_queue_do(&tasks);
1215out_free: 1224out_free:
@@ -1295,6 +1304,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
1295 1304
1296out_unlock: 1305out_unlock:
1297 sem_unlock(sma, -1); 1306 sem_unlock(sma, -1);
1307 rcu_read_unlock();
1298out_up: 1308out_up:
1299 up_write(&sem_ids(ns).rw_mutex); 1309 up_write(&sem_ids(ns).rw_mutex);
1300 return err; 1310 return err;
@@ -1443,9 +1453,11 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
1443 } 1453 }
1444 1454
1445 /* step 3: Acquire the lock on semaphore array */ 1455 /* step 3: Acquire the lock on semaphore array */
1456 /* This also does the rcu_read_lock() */
1446 sem_lock_and_putref(sma); 1457 sem_lock_and_putref(sma);
1447 if (sma->sem_perm.deleted) { 1458 if (sma->sem_perm.deleted) {
1448 sem_unlock(sma, -1); 1459 sem_unlock(sma, -1);
1460 rcu_read_unlock();
1449 kfree(new); 1461 kfree(new);
1450 un = ERR_PTR(-EIDRM); 1462 un = ERR_PTR(-EIDRM);
1451 goto out; 1463 goto out;
@@ -1472,7 +1484,6 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
1472 1484
1473success: 1485success:
1474 spin_unlock(&ulp->lock); 1486 spin_unlock(&ulp->lock);
1475 rcu_read_lock();
1476 sem_unlock(sma, -1); 1487 sem_unlock(sma, -1);
1477out: 1488out:
1478 return un; 1489 return un;
@@ -1648,6 +1659,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
1648sleep_again: 1659sleep_again:
1649 current->state = TASK_INTERRUPTIBLE; 1660 current->state = TASK_INTERRUPTIBLE;
1650 sem_unlock(sma, locknum); 1661 sem_unlock(sma, locknum);
1662 rcu_read_unlock();
1651 1663
1652 if (timeout) 1664 if (timeout)
1653 jiffies_left = schedule_timeout(jiffies_left); 1665 jiffies_left = schedule_timeout(jiffies_left);
@@ -1709,6 +1721,7 @@ sleep_again:
1709 1721
1710out_unlock_free: 1722out_unlock_free:
1711 sem_unlock(sma, locknum); 1723 sem_unlock(sma, locknum);
1724 rcu_read_unlock();
1712out_wakeup: 1725out_wakeup:
1713 wake_up_sem_queue_do(&tasks); 1726 wake_up_sem_queue_do(&tasks);
1714out_free: 1727out_free:
@@ -1801,6 +1814,7 @@ void exit_sem(struct task_struct *tsk)
1801 * exactly the same semid. Nothing to do. 1814 * exactly the same semid. Nothing to do.
1802 */ 1815 */
1803 sem_unlock(sma, -1); 1816 sem_unlock(sma, -1);
1817 rcu_read_unlock();
1804 continue; 1818 continue;
1805 } 1819 }
1806 1820
@@ -1841,6 +1855,7 @@ void exit_sem(struct task_struct *tsk)
1841 INIT_LIST_HEAD(&tasks); 1855 INIT_LIST_HEAD(&tasks);
1842 do_smart_update(sma, NULL, 0, 1, &tasks); 1856 do_smart_update(sma, NULL, 0, 1, &tasks);
1843 sem_unlock(sma, -1); 1857 sem_unlock(sma, -1);
1858 rcu_read_unlock();
1844 wake_up_sem_queue_do(&tasks); 1859 wake_up_sem_queue_do(&tasks);
1845 1860
1846 kfree_rcu(un, rcu); 1861 kfree_rcu(un, rcu);