diff options
Diffstat (limited to 'ipc/msg.c')
-rw-r--r-- | ipc/msg.c | 115 |
1 files changed, 70 insertions, 45 deletions
diff --git a/ipc/msg.c b/ipc/msg.c index 319468609b76..08591a0278ce 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -75,13 +75,12 @@ static struct ipc_ids init_msg_ids; #define msg_lock(ns, id) ((struct msg_queue*)ipc_lock(&msg_ids(ns), id)) #define msg_unlock(msq) ipc_unlock(&(msq)->q_perm) -#define msg_rmid(ns, id) ((struct msg_queue*)ipc_rmid(&msg_ids(ns), id)) #define msg_checkid(ns, msq, msgid) \ ipc_checkid(&msg_ids(ns), &msq->q_perm, msgid) #define msg_buildid(ns, id, seq) \ ipc_buildid(&msg_ids(ns), id, seq) -static void freeque (struct ipc_namespace *ns, struct msg_queue *msq, int id); +static void freeque(struct ipc_namespace *, struct msg_queue *); static int newque (struct ipc_namespace *ns, key_t key, int msgflg); #ifdef CONFIG_PROC_FS static int sysvipc_msg_proc_show(struct seq_file *s, void *it); @@ -93,7 +92,7 @@ static void __msg_init_ns(struct ipc_namespace *ns, struct ipc_ids *ids) ns->msg_ctlmax = MSGMAX; ns->msg_ctlmnb = MSGMNB; ns->msg_ctlmni = MSGMNI; - ipc_init_ids(ids, ns->msg_ctlmni); + ipc_init_ids(ids); } int msg_init_ns(struct ipc_namespace *ns) @@ -110,20 +109,24 @@ int msg_init_ns(struct ipc_namespace *ns) void msg_exit_ns(struct ipc_namespace *ns) { - int i; struct msg_queue *msq; + int next_id; + int total, in_use; mutex_lock(&msg_ids(ns).mutex); - for (i = 0; i <= msg_ids(ns).max_id; i++) { - msq = msg_lock(ns, i); + + in_use = msg_ids(ns).in_use; + + for (total = 0, next_id = 0; total < in_use; next_id++) { + msq = idr_find(&msg_ids(ns).ipcs_idr, next_id); if (msq == NULL) continue; - - freeque(ns, msq, i); + ipc_lock_by_ptr(&msq->q_perm); + freeque(ns, msq); + total++; } mutex_unlock(&msg_ids(ns).mutex); - ipc_fini_ids(ns->ids[IPC_MSG_IDS]); kfree(ns->ids[IPC_MSG_IDS]); ns->ids[IPC_MSG_IDS] = NULL; } @@ -136,6 +139,11 @@ void __init msg_init(void) IPC_MSG_IDS, sysvipc_msg_proc_show); } +static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s) +{ + ipc_rmid(&msg_ids(ns), &s->q_perm); +} + static int newque (struct ipc_namespace *ns, key_t key, int msgflg) { struct msg_queue *msq; @@ -155,6 +163,9 @@ static int newque (struct ipc_namespace *ns, key_t key, int msgflg) return retval; } + /* + * ipc_addid() locks msq + */ id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); if (id == -1) { security_msg_queue_free(msq); @@ -162,7 +173,7 @@ static int newque (struct ipc_namespace *ns, key_t key, int msgflg) return -ENOSPC; } - msq->q_id = msg_buildid(ns, id, msq->q_perm.seq); + msq->q_perm.id = msg_buildid(ns, id, msq->q_perm.seq); msq->q_stime = msq->q_rtime = 0; msq->q_ctime = get_seconds(); msq->q_cbytes = msq->q_qnum = 0; @@ -171,9 +182,10 @@ static int newque (struct ipc_namespace *ns, key_t key, int msgflg) INIT_LIST_HEAD(&msq->q_messages); INIT_LIST_HEAD(&msq->q_receivers); INIT_LIST_HEAD(&msq->q_senders); + msg_unlock(msq); - return msq->q_id; + return msq->q_perm.id; } static inline void ss_add(struct msg_queue *msq, struct msg_sender *mss) @@ -225,18 +237,18 @@ static void expunge_all(struct msg_queue *msq, int res) /* * freeque() wakes up waiters on the sender and receiver waiting queue, * removes the message queue from message queue ID - * array, and cleans up all the messages associated with this queue. + * IDR, and cleans up all the messages associated with this queue. * - * msg_ids.mutex and the spinlock for this message queue is hold + * msg_ids.mutex and the spinlock for this message queue are held * before freeque() is called. msg_ids.mutex remains locked on exit. */ -static void freeque(struct ipc_namespace *ns, struct msg_queue *msq, int id) +static void freeque(struct ipc_namespace *ns, struct msg_queue *msq) { struct list_head *tmp; expunge_all(msq, -EIDRM); ss_wakeup(&msq->q_senders, 1); - msq = msg_rmid(ns, id); + msg_rmid(ns, msq); msg_unlock(msq); tmp = msq->q_messages.next; @@ -255,36 +267,51 @@ static void freeque(struct ipc_namespace *ns, struct msg_queue *msq, int id) asmlinkage long sys_msgget(key_t key, int msgflg) { struct msg_queue *msq; - int id, ret = -EPERM; + int ret; struct ipc_namespace *ns; ns = current->nsproxy->ipc_ns; - - mutex_lock(&msg_ids(ns).mutex); - if (key == IPC_PRIVATE) - ret = newque(ns, key, msgflg); - else if ((id = ipc_findkey(&msg_ids(ns), key)) == -1) { /* key not used */ - if (!(msgflg & IPC_CREAT)) - ret = -ENOENT; - else + + ret = idr_pre_get(&msg_ids(ns).ipcs_idr, GFP_KERNEL); + + if (key == IPC_PRIVATE) { + if (!ret) + ret = -ENOMEM; + else { + mutex_lock(&msg_ids(ns).mutex); ret = newque(ns, key, msgflg); - } else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) { - ret = -EEXIST; + mutex_unlock(&msg_ids(ns).mutex); + } } else { - msq = msg_lock(ns, id); - BUG_ON(msq == NULL); - if (ipcperms(&msq->q_perm, msgflg)) - ret = -EACCES; - else { - int qid = msg_buildid(ns, id, msq->q_perm.seq); - - ret = security_msg_queue_associate(msq, msgflg); - if (!ret) - ret = qid; + mutex_lock(&msg_ids(ns).mutex); + msq = (struct msg_queue *) ipc_findkey(&msg_ids(ns), key); + if (msq == NULL) { + /* key not used */ + if (!(msgflg & IPC_CREAT)) + ret = -ENOENT; + else if (!ret) + ret = -ENOMEM; + else + ret = newque(ns, key, msgflg); + } else { + /* msq has been locked by ipc_findkey() */ + + if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) + ret = -EEXIST; + else { + if (ipcperms(&msq->q_perm, msgflg)) + ret = -EACCES; + else { + ret = security_msg_queue_associate( + msq, msgflg); + if (!ret) + ret = msq->q_perm.id; + } + } + msg_unlock(msq); } - msg_unlock(msq); + mutex_unlock(&msg_ids(ns).mutex); } - mutex_unlock(&msg_ids(ns).mutex); return ret; } @@ -430,13 +457,13 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) msginfo.msgpool = MSGPOOL; msginfo.msgtql = MSGTQL; } - max_id = msg_ids(ns).max_id; + max_id = ipc_get_maxid(&msg_ids(ns)); mutex_unlock(&msg_ids(ns).mutex); if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) return -EFAULT; return (max_id < 0) ? 0 : max_id; } - case MSG_STAT: + case MSG_STAT: /* msqid is an index rather than a msg queue id */ case IPC_STAT: { struct msqid64_ds tbuf; @@ -444,8 +471,6 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) if (!buf) return -EFAULT; - if (cmd == MSG_STAT && msqid >= msg_ids(ns).entries->size) - return -EINVAL; memset(&tbuf, 0, sizeof(tbuf)); @@ -454,7 +479,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) return -EINVAL; if (cmd == MSG_STAT) { - success_return = msg_buildid(ns, msqid, msq->q_perm.seq); + success_return = msq->q_perm.id; } else { err = -EIDRM; if (msg_checkid(ns, msq, msqid)) @@ -552,7 +577,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) break; } case IPC_RMID: - freeque(ns, msq, msqid); + freeque(ns, msq); break; } err = 0; @@ -926,7 +951,7 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it) return seq_printf(s, "%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n", msq->q_perm.key, - msq->q_id, + msq->q_perm.id, msq->q_perm.mode, msq->q_cbytes, msq->q_qnum, |