ipc,msg: introduce lockless functions to obtain the ipc object
[platform/adaptation/renesas_rcar/renesas_kernel.git] / ipc / msg.c
index d0c6d96..c53c137 100644 (file)
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -166,6 +166,27 @@ static inline struct msg_queue *msg_lock_check(struct ipc_namespace *ns,
        return container_of(ipcp, struct msg_queue, q_perm);
 }
 
+static inline struct msg_queue *msq_obtain_object(struct ipc_namespace *ns, int id)
+{
+       struct kern_ipc_perm *ipcp = ipc_obtain_object(&msg_ids(ns), id);
+
+       if (IS_ERR(ipcp))
+               return ERR_CAST(ipcp);
+
+       return container_of(ipcp, struct msg_queue, q_perm);
+}
+
+static inline struct msg_queue *msq_obtain_object_check(struct ipc_namespace *ns,
+                                                       int id)
+{
+       struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&msg_ids(ns), id);
+
+       if (IS_ERR(ipcp))
+               return ERR_CAST(ipcp);
+
+       return container_of(ipcp, struct msg_queue, q_perm);
+}
+
 static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
 {
        ipc_rmid(&msg_ids(ns), &s->q_perm);
@@ -199,9 +220,7 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
                return retval;
        }
 
-       /*
-        * ipc_addid() locks msq
-        */
+       /* ipc_addid() locks msq upon success. */
        id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
        if (id < 0) {
                security_msg_queue_free(msq);
@@ -218,7 +237,8 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
        INIT_LIST_HEAD(&msq->q_receivers);
        INIT_LIST_HEAD(&msq->q_senders);
 
-       msg_unlock(msq);
+       ipc_unlock_object(&msq->q_perm);
+       rcu_read_unlock();
 
        return msq->q_perm.id;
 }
@@ -408,31 +428,39 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
                        return -EFAULT;
        }
 
-       ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd,
-                              &msqid64.msg_perm, msqid64.msg_qbytes);
-       if (IS_ERR(ipcp))
-               return PTR_ERR(ipcp);
+       down_write(&msg_ids(ns).rw_mutex);
+       rcu_read_lock();
+
+       ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd,
+                                     &msqid64.msg_perm, msqid64.msg_qbytes);
+       if (IS_ERR(ipcp)) {
+               err = PTR_ERR(ipcp);
+               goto out_unlock1;
+       }
 
        msq = container_of(ipcp, struct msg_queue, q_perm);
 
        err = security_msg_queue_msgctl(msq, cmd);
        if (err)
-               goto out_unlock;
+               goto out_unlock1;
 
        switch (cmd) {
        case IPC_RMID:
+               ipc_lock_object(&msq->q_perm);
+               /* freeque unlocks the ipc object and rcu */
                freeque(ns, ipcp);
                goto out_up;
        case IPC_SET:
                if (msqid64.msg_qbytes > ns->msg_ctlmnb &&
                    !capable(CAP_SYS_RESOURCE)) {
                        err = -EPERM;
-                       goto out_unlock;
+                       goto out_unlock1;
                }
 
+               ipc_lock_object(&msq->q_perm);
                err = ipc_update_perm(&msqid64.msg_perm, ipcp);
                if (err)
-                       goto out_unlock;
+                       goto out_unlock0;
 
                msq->q_qbytes = msqid64.msg_qbytes;
 
@@ -448,25 +476,23 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
                break;
        default:
                err = -EINVAL;
+               goto out_unlock1;
        }
-out_unlock:
-       msg_unlock(msq);
+
+out_unlock0:
+       ipc_unlock_object(&msq->q_perm);
+out_unlock1:
+       rcu_read_unlock();
 out_up:
        up_write(&msg_ids(ns).rw_mutex);
        return err;
 }
 
-SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
+static int msgctl_nolock(struct ipc_namespace *ns, int msqid,
+                        int cmd, int version, void __user *buf)
 {
+       int err;
        struct msg_queue *msq;
-       int err, version;
-       struct ipc_namespace *ns;
-
-       if (msqid < 0 || cmd < 0)
-               return -EINVAL;
-
-       version = ipc_parse_version(&cmd);
-       ns = current->nsproxy->ipc_ns;
 
        switch (cmd) {
        case IPC_INFO:
@@ -477,6 +503,7 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
 
                if (!buf)
                        return -EFAULT;
+
                /*
                 * We must not return kernel stack data.
                 * due to padding, it's not enough
@@ -508,7 +535,8 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
                        return -EFAULT;
                return (max_id < 0) ? 0 : max_id;
        }
-       case MSG_STAT:  /* msqid is an index rather than a msg queue id */
+
+       case MSG_STAT:
        case IPC_STAT:
        {
                struct msqid64_ds tbuf;
@@ -552,19 +580,42 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
                        return -EFAULT;
                return success_return;
        }
-       case IPC_SET:
-       case IPC_RMID:
-               err = msgctl_down(ns, msqid, cmd, buf, version);
-               return err;
+
        default:
-               return  -EINVAL;
+               return -EINVAL;
        }
 
+       return err;
 out_unlock:
        msg_unlock(msq);
        return err;
 }
 
+SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
+{
+       int version;
+       struct ipc_namespace *ns;
+
+       if (msqid < 0 || cmd < 0)
+               return -EINVAL;
+
+       version = ipc_parse_version(&cmd);
+       ns = current->nsproxy->ipc_ns;
+
+       switch (cmd) {
+       case IPC_INFO:
+       case MSG_INFO:
+       case MSG_STAT:  /* msqid is an index rather than a msg queue id */
+       case IPC_STAT:
+               return msgctl_nolock(ns, msqid, cmd, version, buf);
+       case IPC_SET:
+       case IPC_RMID:
+               return msgctl_down(ns, msqid, cmd, buf, version);
+       default:
+               return  -EINVAL;
+       }
+}
+
 static int testmsg(struct msg_msg *msg, long type, int mode)
 {
        switch(mode)