diff options
Diffstat (limited to 'ipc/sem.c')
-rw-r--r-- | ipc/sem.c | 129 |
1 files changed, 64 insertions, 65 deletions
diff --git a/ipc/sem.c b/ipc/sem.c index e78ee3186d1f..a7e40ed8a076 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -264,12 +264,13 @@ static inline void sem_unlock(struct sem_array *sma, int locknum) struct sem *sem = sma->sem_base + locknum; spin_unlock(&sem->lock); } - rcu_read_unlock(); } /* * sem_lock_(check_) routines are called in the paths where the rw_mutex * is not held. + * + * The caller holds the RCU read lock. */ static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns, int id, struct sembuf *sops, int nsops, int *locknum) @@ -277,12 +278,9 @@ static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp; struct sem_array *sma; - rcu_read_lock(); ipcp = ipc_obtain_object(&sem_ids(ns), id); - if (IS_ERR(ipcp)) { - sma = ERR_CAST(ipcp); - goto err; - } + if (IS_ERR(ipcp)) + return ERR_CAST(ipcp); sma = container_of(ipcp, struct sem_array, sem_perm); *locknum = sem_lock(sma, sops, nsops); @@ -294,10 +292,7 @@ static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns, return container_of(ipcp, struct sem_array, sem_perm); sem_unlock(sma, *locknum); - sma = ERR_PTR(-EINVAL); -err: - rcu_read_unlock(); - return sma; + return ERR_PTR(-EINVAL); } static inline struct sem_array *sem_obtain_object(struct ipc_namespace *ns, int id) @@ -323,31 +318,13 @@ static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns static inline void sem_lock_and_putref(struct sem_array *sma) { - rcu_read_lock(); sem_lock(sma, NULL, -1); ipc_rcu_putref(sma); } -static inline void sem_getref_and_unlock(struct sem_array *sma) -{ - WARN_ON_ONCE(!ipc_rcu_getref(sma)); - sem_unlock(sma, -1); -} - static inline void sem_putref(struct sem_array *sma) { - sem_lock_and_putref(sma); - sem_unlock(sma, -1); -} - -/* - * Call inside the rcu read section. - */ -static inline void sem_getref(struct sem_array *sma) -{ - sem_lock(sma, NULL, -1); - WARN_ON_ONCE(!ipc_rcu_getref(sma)); - sem_unlock(sma, -1); + ipc_rcu_putref(sma); } static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) @@ -451,6 +428,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) sma->sem_nsems = nsems; sma->sem_ctime = get_seconds(); sem_unlock(sma, -1); + rcu_read_unlock(); return sma->sem_perm.id; } @@ -818,6 +796,13 @@ static int count_semncnt (struct sem_array * sma, ushort semnum) struct sem_queue * q; semncnt = 0; + list_for_each_entry(q, &sma->sem_base[semnum].sem_pending, list) { + struct sembuf * sops = q->sops; + BUG_ON(sops->sem_num != semnum); + if ((sops->sem_op < 0) && !(sops->sem_flg & IPC_NOWAIT)) + semncnt++; + } + list_for_each_entry(q, &sma->sem_pending, list) { struct sembuf * sops = q->sops; int nsops = q->nsops; @@ -837,6 +822,13 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum) struct sem_queue * q; semzcnt = 0; + list_for_each_entry(q, &sma->sem_base[semnum].sem_pending, list) { + struct sembuf * sops = q->sops; + BUG_ON(sops->sem_num != semnum); + if ((sops->sem_op == 0) && !(sops->sem_flg & IPC_NOWAIT)) + semzcnt++; + } + list_for_each_entry(q, &sma->sem_pending, list) { struct sembuf * sops = q->sops; int nsops = q->nsops; @@ -890,6 +882,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) /* Remove the semaphore set from the IDR */ sem_rmid(ns, sma); sem_unlock(sma, -1); + rcu_read_unlock(); wake_up_sem_queue_do(&tasks); ns->used_sems -= sma->sem_nsems; @@ -969,8 +962,8 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, memset(&tbuf, 0, sizeof(tbuf)); + rcu_read_lock(); if (cmd == SEM_STAT) { - rcu_read_lock(); sma = sem_obtain_object(ns, semid); if (IS_ERR(sma)) { err = PTR_ERR(sma); @@ -978,7 +971,6 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, } id = sma->sem_perm.id; } else { - rcu_read_lock(); sma = sem_obtain_object_check(ns, semid); if (IS_ERR(sma)) { err = PTR_ERR(sma); @@ -1071,6 +1063,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum, /* maybe some queued-up processes were waiting for this */ do_smart_update(sma, NULL, 0, 0, &tasks); sem_unlock(sma, -1); + rcu_read_unlock(); wake_up_sem_queue_do(&tasks); return 0; } @@ -1097,17 +1090,12 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, nsems = sma->sem_nsems; err = -EACCES; - if (ipcperms(ns, &sma->sem_perm, - cmd == SETALL ? S_IWUGO : S_IRUGO)) { - rcu_read_unlock(); - goto out_wakeup; - } + if (ipcperms(ns, &sma->sem_perm, cmd == SETALL ? S_IWUGO : S_IRUGO)) + goto out_rcu_wakeup; err = security_sem_semctl(sma, cmd); - if (err) { - rcu_read_unlock(); - goto out_wakeup; - } + if (err) + goto out_rcu_wakeup; err = -EACCES; switch (cmd) { @@ -1116,27 +1104,35 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, ushort __user *array = p; int i; + sem_lock(sma, NULL, -1); if(nsems > SEMMSL_FAST) { - sem_getref(sma); - + if (!ipc_rcu_getref(sma)) { + sem_unlock(sma, -1); + rcu_read_unlock(); + err = -EIDRM; + goto out_free; + } + sem_unlock(sma, -1); + rcu_read_unlock(); sem_io = ipc_alloc(sizeof(ushort)*nsems); if(sem_io == NULL) { sem_putref(sma); return -ENOMEM; } + rcu_read_lock(); sem_lock_and_putref(sma); if (sma->sem_perm.deleted) { sem_unlock(sma, -1); + rcu_read_unlock(); err = -EIDRM; goto out_free; } - } else - sem_lock(sma, NULL, -1); - + } for (i = 0; i < sma->sem_nsems; i++) sem_io[i] = sma->sem_base[i].semval; sem_unlock(sma, -1); + rcu_read_unlock(); err = 0; if(copy_to_user(array, sem_io, nsems*sizeof(ushort))) err = -EFAULT; @@ -1174,9 +1170,11 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, goto out_free; } } + rcu_read_lock(); sem_lock_and_putref(sma); if (sma->sem_perm.deleted) { sem_unlock(sma, -1); + rcu_read_unlock(); err = -EIDRM; goto out_free; } @@ -1198,10 +1196,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, /* GETVAL, GETPID, GETNCTN, GETZCNT: fall-through */ } err = -EINVAL; - if (semnum < 0 || semnum >= nsems) { - rcu_read_unlock(); - goto out_wakeup; - } + if (semnum < 0 || semnum >= nsems) + goto out_rcu_wakeup; sem_lock(sma, NULL, -1); curr = &sma->sem_base[semnum]; @@ -1223,7 +1219,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, out_unlock: sem_unlock(sma, -1); -out_wakeup: +out_rcu_wakeup: + rcu_read_unlock(); wake_up_sem_queue_do(&tasks); out_free: if(sem_io != fast_sem_io) @@ -1285,7 +1282,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, err = security_sem_semctl(sma, cmd); if (err) { rcu_read_unlock(); - goto out_unlock; + goto out_up; } switch(cmd){ @@ -1308,6 +1305,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, out_unlock: sem_unlock(sma, -1); + rcu_read_unlock(); out_up: up_write(&sem_ids(ns).rw_mutex); return err; @@ -1456,9 +1454,11 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) } /* step 3: Acquire the lock on semaphore array */ + rcu_read_lock(); sem_lock_and_putref(sma); if (sma->sem_perm.deleted) { sem_unlock(sma, -1); + rcu_read_unlock(); kfree(new); un = ERR_PTR(-EIDRM); goto out; @@ -1485,7 +1485,6 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) success: spin_unlock(&ulp->lock); - rcu_read_lock(); sem_unlock(sma, -1); out: return un; @@ -1592,22 +1591,16 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, } error = -EFBIG; - if (max >= sma->sem_nsems) { - rcu_read_unlock(); - goto out_wakeup; - } + if (max >= sma->sem_nsems) + goto out_rcu_wakeup; error = -EACCES; - if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) { - rcu_read_unlock(); - goto out_wakeup; - } + if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) + goto out_rcu_wakeup; error = security_sem_semop(sma, sops, nsops, alter); - if (error) { - rcu_read_unlock(); - goto out_wakeup; - } + if (error) + goto out_rcu_wakeup; /* * semid identifiers are not unique - find_alloc_undo may have @@ -1661,6 +1654,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, sleep_again: current->state = TASK_INTERRUPTIBLE; sem_unlock(sma, locknum); + rcu_read_unlock(); if (timeout) jiffies_left = schedule_timeout(jiffies_left); @@ -1682,6 +1676,7 @@ sleep_again: goto out_free; } + rcu_read_lock(); sma = sem_obtain_lock(ns, semid, sops, nsops, &locknum); /* @@ -1693,6 +1688,7 @@ sleep_again: * Array removed? If yes, leave without sem_unlock(). */ if (IS_ERR(sma)) { + rcu_read_unlock(); goto out_free; } @@ -1722,7 +1718,8 @@ sleep_again: out_unlock_free: sem_unlock(sma, locknum); -out_wakeup: +out_rcu_wakeup: + rcu_read_unlock(); wake_up_sem_queue_do(&tasks); out_free: if(sops != fast_sops) @@ -1814,6 +1811,7 @@ void exit_sem(struct task_struct *tsk) * exactly the same semid. Nothing to do. */ sem_unlock(sma, -1); + rcu_read_unlock(); continue; } @@ -1854,6 +1852,7 @@ void exit_sem(struct task_struct *tsk) INIT_LIST_HEAD(&tasks); do_smart_update(sma, NULL, 0, 1, &tasks); sem_unlock(sma, -1); + rcu_read_unlock(); wake_up_sem_queue_do(&tasks); kfree_rcu(un, rcu); |