summaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'ipc')
-rw-r--r--ipc/Makefile3
-rw-r--r--ipc/compat_mq.c138
-rw-r--r--ipc/mqueue.c353
-rw-r--r--ipc/msg.c28
-rw-r--r--ipc/sem.c155
-rw-r--r--ipc/shm.c28
-rw-r--r--ipc/util.c65
-rw-r--r--ipc/util.h23
8 files changed, 380 insertions, 413 deletions
diff --git a/ipc/Makefile b/ipc/Makefile
index 86c7300ecdf5..9c200e544434 100644
--- a/ipc/Makefile
+++ b/ipc/Makefile
@@ -5,8 +5,7 @@
obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o
obj-$(CONFIG_SYSVIPC) += util.o msgutil.o msg.o sem.o shm.o syscall.o
obj-$(CONFIG_SYSVIPC_SYSCTL) += ipc_sysctl.o
-obj_mq-$(CONFIG_COMPAT) += compat_mq.o
-obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o $(obj_mq-y)
+obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o
obj-$(CONFIG_IPC_NS) += namespace.o
obj-$(CONFIG_POSIX_MQUEUE_SYSCTL) += mq_sysctl.o
diff --git a/ipc/compat_mq.c b/ipc/compat_mq.c
deleted file mode 100644
index ef6f91cc4490..000000000000
--- a/ipc/compat_mq.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * ipc/compat_mq.c
- * 32 bit emulation for POSIX message queue system calls
- *
- * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author: Arnd Bergmann <arnd@arndb.de>
- */
-
-#include <linux/compat.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/mqueue.h>
-#include <linux/syscalls.h>
-
-#include <linux/uaccess.h>
-
-struct compat_mq_attr {
- compat_long_t mq_flags; /* message queue flags */
- compat_long_t mq_maxmsg; /* maximum number of messages */
- compat_long_t mq_msgsize; /* maximum message size */
- compat_long_t mq_curmsgs; /* number of messages currently queued */
- compat_long_t __reserved[4]; /* ignored for input, zeroed for output */
-};
-
-static inline int get_compat_mq_attr(struct mq_attr *attr,
- const struct compat_mq_attr __user *uattr)
-{
- if (!access_ok(VERIFY_READ, uattr, sizeof *uattr))
- return -EFAULT;
-
- return __get_user(attr->mq_flags, &uattr->mq_flags)
- | __get_user(attr->mq_maxmsg, &uattr->mq_maxmsg)
- | __get_user(attr->mq_msgsize, &uattr->mq_msgsize)
- | __get_user(attr->mq_curmsgs, &uattr->mq_curmsgs);
-}
-
-static inline int put_compat_mq_attr(const struct mq_attr *attr,
- struct compat_mq_attr __user *uattr)
-{
- if (clear_user(uattr, sizeof *uattr))
- return -EFAULT;
-
- return __put_user(attr->mq_flags, &uattr->mq_flags)
- | __put_user(attr->mq_maxmsg, &uattr->mq_maxmsg)
- | __put_user(attr->mq_msgsize, &uattr->mq_msgsize)
- | __put_user(attr->mq_curmsgs, &uattr->mq_curmsgs);
-}
-
-COMPAT_SYSCALL_DEFINE4(mq_open, const char __user *, u_name,
- int, oflag, compat_mode_t, mode,
- struct compat_mq_attr __user *, u_attr)
-{
- void __user *p = NULL;
- if (u_attr && oflag & O_CREAT) {
- struct mq_attr attr;
-
- memset(&attr, 0, sizeof(attr));
-
- p = compat_alloc_user_space(sizeof(attr));
- if (get_compat_mq_attr(&attr, u_attr) ||
- copy_to_user(p, &attr, sizeof(attr)))
- return -EFAULT;
- }
- return sys_mq_open(u_name, oflag, mode, p);
-}
-
-COMPAT_SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes,
- const char __user *, u_msg_ptr,
- compat_size_t, msg_len, unsigned int, msg_prio,
- const struct compat_timespec __user *, u_abs_timeout)
-{
- struct timespec __user *u_ts;
-
- if (compat_convert_timespec(&u_ts, u_abs_timeout))
- return -EFAULT;
-
- return sys_mq_timedsend(mqdes, u_msg_ptr, msg_len,
- msg_prio, u_ts);
-}
-
-COMPAT_SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes,
- char __user *, u_msg_ptr,
- compat_size_t, msg_len, unsigned int __user *, u_msg_prio,
- const struct compat_timespec __user *, u_abs_timeout)
-{
- struct timespec __user *u_ts;
-
- if (compat_convert_timespec(&u_ts, u_abs_timeout))
- return -EFAULT;
-
- return sys_mq_timedreceive(mqdes, u_msg_ptr, msg_len,
- u_msg_prio, u_ts);
-}
-
-COMPAT_SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
- const struct compat_sigevent __user *, u_notification)
-{
- struct sigevent __user *p = NULL;
- if (u_notification) {
- struct sigevent n;
- p = compat_alloc_user_space(sizeof(*p));
- if (get_compat_sigevent(&n, u_notification))
- return -EFAULT;
- if (n.sigev_notify == SIGEV_THREAD)
- n.sigev_value.sival_ptr = compat_ptr(n.sigev_value.sival_int);
- if (copy_to_user(p, &n, sizeof(*p)))
- return -EFAULT;
- }
- return sys_mq_notify(mqdes, p);
-}
-
-COMPAT_SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
- const struct compat_mq_attr __user *, u_mqstat,
- struct compat_mq_attr __user *, u_omqstat)
-{
- struct mq_attr mqstat;
- struct mq_attr __user *p = compat_alloc_user_space(2 * sizeof(*p));
- long ret;
-
- memset(&mqstat, 0, sizeof(mqstat));
-
- if (u_mqstat) {
- if (get_compat_mq_attr(&mqstat, u_mqstat) ||
- copy_to_user(p, &mqstat, sizeof(mqstat)))
- return -EFAULT;
- }
- ret = sys_mq_getsetattr(mqdes,
- u_mqstat ? p : NULL,
- u_omqstat ? p + 1 : NULL);
- if (ret)
- return ret;
- if (u_omqstat) {
- if (copy_from_user(&mqstat, p + 1, sizeof(mqstat)) ||
- put_compat_mq_attr(&mqstat, u_omqstat))
- return -EFAULT;
- }
- return 0;
-}
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index e8d41ff57241..eb1391b52c6f 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -668,14 +668,12 @@ static void __do_notify(struct mqueue_inode_info *info)
}
static int prepare_timeout(const struct timespec __user *u_abs_timeout,
- ktime_t *expires, struct timespec *ts)
+ struct timespec *ts)
{
if (copy_from_user(ts, u_abs_timeout, sizeof(struct timespec)))
return -EFAULT;
if (!timespec_valid(ts))
return -EINVAL;
-
- *expires = timespec_to_ktime(*ts);
return 0;
}
@@ -770,23 +768,19 @@ static struct file *do_open(struct path *path, int oflag)
return dentry_open(path, oflag, current_cred());
}
-SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
- struct mq_attr __user *, u_attr)
+static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
+ struct mq_attr *attr)
{
struct path path;
struct file *filp;
struct filename *name;
- struct mq_attr attr;
int fd, error;
struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
struct vfsmount *mnt = ipc_ns->mq_mnt;
struct dentry *root = mnt->mnt_root;
int ro;
- if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr)))
- return -EFAULT;
-
- audit_mq_open(oflag, mode, u_attr ? &attr : NULL);
+ audit_mq_open(oflag, mode, attr);
if (IS_ERR(name = getname(u_name)))
return PTR_ERR(name);
@@ -819,9 +813,8 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
goto out;
}
audit_inode_parent_hidden(name, root);
- filp = do_create(ipc_ns, d_inode(root),
- &path, oflag, mode,
- u_attr ? &attr : NULL);
+ filp = do_create(ipc_ns, d_inode(root), &path,
+ oflag, mode, attr);
}
} else {
if (d_really_is_negative(path.dentry)) {
@@ -851,6 +844,16 @@ out_putname:
return fd;
}
+SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
+ struct mq_attr __user *, u_attr)
+{
+ struct mq_attr attr;
+ if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr)))
+ return -EFAULT;
+
+ return do_mq_open(u_name, oflag, mode, u_attr ? &attr : NULL);
+}
+
SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
{
int err;
@@ -957,9 +960,9 @@ static inline void pipelined_receive(struct wake_q_head *wake_q,
sender->state = STATE_READY;
}
-SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
- size_t, msg_len, unsigned int, msg_prio,
- const struct timespec __user *, u_abs_timeout)
+static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
+ size_t msg_len, unsigned int msg_prio,
+ struct timespec *ts)
{
struct fd f;
struct inode *inode;
@@ -968,22 +971,19 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
struct msg_msg *msg_ptr;
struct mqueue_inode_info *info;
ktime_t expires, *timeout = NULL;
- struct timespec ts;
struct posix_msg_tree_node *new_leaf = NULL;
int ret = 0;
DEFINE_WAKE_Q(wake_q);
- if (u_abs_timeout) {
- int res = prepare_timeout(u_abs_timeout, &expires, &ts);
- if (res)
- return res;
- timeout = &expires;
- }
-
if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX))
return -EINVAL;
- audit_mq_sendrecv(mqdes, msg_len, msg_prio, timeout ? &ts : NULL);
+ if (ts) {
+ expires = timespec_to_ktime(*ts);
+ timeout = &expires;
+ }
+
+ audit_mq_sendrecv(mqdes, msg_len, msg_prio, ts);
f = fdget(mqdes);
if (unlikely(!f.file)) {
@@ -1078,9 +1078,9 @@ out:
return ret;
}
-SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
- size_t, msg_len, unsigned int __user *, u_msg_prio,
- const struct timespec __user *, u_abs_timeout)
+static int do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
+ size_t msg_len, unsigned int __user *u_msg_prio,
+ struct timespec *ts)
{
ssize_t ret;
struct msg_msg *msg_ptr;
@@ -1089,17 +1089,14 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
struct mqueue_inode_info *info;
struct ext_wait_queue wait;
ktime_t expires, *timeout = NULL;
- struct timespec ts;
struct posix_msg_tree_node *new_leaf = NULL;
- if (u_abs_timeout) {
- int res = prepare_timeout(u_abs_timeout, &expires, &ts);
- if (res)
- return res;
+ if (ts) {
+ expires = timespec_to_ktime(*ts);
timeout = &expires;
}
- audit_mq_sendrecv(mqdes, msg_len, 0, timeout ? &ts : NULL);
+ audit_mq_sendrecv(mqdes, msg_len, 0, ts);
f = fdget(mqdes);
if (unlikely(!f.file)) {
@@ -1183,42 +1180,62 @@ out:
return ret;
}
+SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
+ size_t, msg_len, unsigned int, msg_prio,
+ const struct timespec __user *, u_abs_timeout)
+{
+ struct timespec ts, *p = NULL;
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+ if (res)
+ return res;
+ p = &ts;
+ }
+ return do_mq_timedsend(mqdes, u_msg_ptr, msg_len, msg_prio, p);
+}
+
+SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
+ size_t, msg_len, unsigned int __user *, u_msg_prio,
+ const struct timespec __user *, u_abs_timeout)
+{
+ struct timespec ts, *p = NULL;
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+ if (res)
+ return res;
+ p = &ts;
+ }
+ return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, p);
+}
+
/*
* Notes: the case when user wants us to deregister (with NULL as pointer)
* and he isn't currently owner of notification, will be silently discarded.
* It isn't explicitly defined in the POSIX.
*/
-SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
- const struct sigevent __user *, u_notification)
+static int do_mq_notify(mqd_t mqdes, const struct sigevent *notification)
{
int ret;
struct fd f;
struct sock *sock;
struct inode *inode;
- struct sigevent notification;
struct mqueue_inode_info *info;
struct sk_buff *nc;
- if (u_notification) {
- if (copy_from_user(&notification, u_notification,
- sizeof(struct sigevent)))
- return -EFAULT;
- }
-
- audit_mq_notify(mqdes, u_notification ? &notification : NULL);
+ audit_mq_notify(mqdes, notification);
nc = NULL;
sock = NULL;
- if (u_notification != NULL) {
- if (unlikely(notification.sigev_notify != SIGEV_NONE &&
- notification.sigev_notify != SIGEV_SIGNAL &&
- notification.sigev_notify != SIGEV_THREAD))
+ if (notification != NULL) {
+ if (unlikely(notification->sigev_notify != SIGEV_NONE &&
+ notification->sigev_notify != SIGEV_SIGNAL &&
+ notification->sigev_notify != SIGEV_THREAD))
return -EINVAL;
- if (notification.sigev_notify == SIGEV_SIGNAL &&
- !valid_signal(notification.sigev_signo)) {
+ if (notification->sigev_notify == SIGEV_SIGNAL &&
+ !valid_signal(notification->sigev_signo)) {
return -EINVAL;
}
- if (notification.sigev_notify == SIGEV_THREAD) {
+ if (notification->sigev_notify == SIGEV_THREAD) {
long timeo;
/* create the notify skb */
@@ -1228,7 +1245,7 @@ SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
goto out;
}
if (copy_from_user(nc->data,
- notification.sigev_value.sival_ptr,
+ notification->sigev_value.sival_ptr,
NOTIFY_COOKIE_LEN)) {
ret = -EFAULT;
goto out;
@@ -1238,7 +1255,7 @@ SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
skb_put(nc, NOTIFY_COOKIE_LEN);
/* and attach it to the socket */
retry:
- f = fdget(notification.sigev_signo);
+ f = fdget(notification->sigev_signo);
if (!f.file) {
ret = -EBADF;
goto out;
@@ -1253,8 +1270,10 @@ retry:
timeo = MAX_SCHEDULE_TIMEOUT;
ret = netlink_attachskb(sock, nc, &timeo, NULL);
- if (ret == 1)
+ if (ret == 1) {
+ sock = NULL;
goto retry;
+ }
if (ret) {
sock = NULL;
nc = NULL;
@@ -1278,7 +1297,7 @@ retry:
ret = 0;
spin_lock(&info->lock);
- if (u_notification == NULL) {
+ if (notification == NULL) {
if (info->notify_owner == task_tgid(current)) {
remove_notification(info);
inode->i_atime = inode->i_ctime = current_time(inode);
@@ -1286,7 +1305,7 @@ retry:
} else if (info->notify_owner != NULL) {
ret = -EBUSY;
} else {
- switch (notification.sigev_notify) {
+ switch (notification->sigev_notify) {
case SIGEV_NONE:
info->notify.sigev_notify = SIGEV_NONE;
break;
@@ -1298,8 +1317,8 @@ retry:
info->notify.sigev_notify = SIGEV_THREAD;
break;
case SIGEV_SIGNAL:
- info->notify.sigev_signo = notification.sigev_signo;
- info->notify.sigev_value = notification.sigev_value;
+ info->notify.sigev_signo = notification->sigev_signo;
+ info->notify.sigev_value = notification->sigev_value;
info->notify.sigev_notify = SIGEV_SIGNAL;
break;
}
@@ -1320,44 +1339,49 @@ out:
return ret;
}
-SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
- const struct mq_attr __user *, u_mqstat,
- struct mq_attr __user *, u_omqstat)
+SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
+ const struct sigevent __user *, u_notification)
+{
+ struct sigevent n, *p = NULL;
+ if (u_notification) {
+ if (copy_from_user(&n, u_notification, sizeof(struct sigevent)))
+ return -EFAULT;
+ p = &n;
+ }
+ return do_mq_notify(mqdes, p);
+}
+
+static int do_mq_getsetattr(int mqdes, struct mq_attr *new, struct mq_attr *old)
{
- int ret;
- struct mq_attr mqstat, omqstat;
struct fd f;
struct inode *inode;
struct mqueue_inode_info *info;
- if (u_mqstat != NULL) {
- if (copy_from_user(&mqstat, u_mqstat, sizeof(struct mq_attr)))
- return -EFAULT;
- if (mqstat.mq_flags & (~O_NONBLOCK))
- return -EINVAL;
- }
+ if (new && (new->mq_flags & (~O_NONBLOCK)))
+ return -EINVAL;
f = fdget(mqdes);
- if (!f.file) {
- ret = -EBADF;
- goto out;
- }
+ if (!f.file)
+ return -EBADF;
- inode = file_inode(f.file);
if (unlikely(f.file->f_op != &mqueue_file_operations)) {
- ret = -EBADF;
- goto out_fput;
+ fdput(f);
+ return -EBADF;
}
+
+ inode = file_inode(f.file);
info = MQUEUE_I(inode);
spin_lock(&info->lock);
- omqstat = info->attr;
- omqstat.mq_flags = f.file->f_flags & O_NONBLOCK;
- if (u_mqstat) {
- audit_mq_getsetattr(mqdes, &mqstat);
+ if (old) {
+ *old = info->attr;
+ old->mq_flags = f.file->f_flags & O_NONBLOCK;
+ }
+ if (new) {
+ audit_mq_getsetattr(mqdes, new);
spin_lock(&f.file->f_lock);
- if (mqstat.mq_flags & O_NONBLOCK)
+ if (new->mq_flags & O_NONBLOCK)
f.file->f_flags |= O_NONBLOCK;
else
f.file->f_flags &= ~O_NONBLOCK;
@@ -1367,17 +1391,168 @@ SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
}
spin_unlock(&info->lock);
+ fdput(f);
+ return 0;
+}
- ret = 0;
- if (u_omqstat != NULL && copy_to_user(u_omqstat, &omqstat,
- sizeof(struct mq_attr)))
- ret = -EFAULT;
+SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
+ const struct mq_attr __user *, u_mqstat,
+ struct mq_attr __user *, u_omqstat)
+{
+ int ret;
+ struct mq_attr mqstat, omqstat;
+ struct mq_attr *new = NULL, *old = NULL;
-out_fput:
- fdput(f);
-out:
- return ret;
+ if (u_mqstat) {
+ new = &mqstat;
+ if (copy_from_user(new, u_mqstat, sizeof(struct mq_attr)))
+ return -EFAULT;
+ }
+ if (u_omqstat)
+ old = &omqstat;
+
+ ret = do_mq_getsetattr(mqdes, new, old);
+ if (ret || !old)
+ return ret;
+
+ if (copy_to_user(u_omqstat, old, sizeof(struct mq_attr)))
+ return -EFAULT;
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+
+struct compat_mq_attr {
+ compat_long_t mq_flags; /* message queue flags */
+ compat_long_t mq_maxmsg; /* maximum number of messages */
+ compat_long_t mq_msgsize; /* maximum message size */
+ compat_long_t mq_curmsgs; /* number of messages currently queued */
+ compat_long_t __reserved[4]; /* ignored for input, zeroed for output */
+};
+
+static inline int get_compat_mq_attr(struct mq_attr *attr,
+ const struct compat_mq_attr __user *uattr)
+{
+ struct compat_mq_attr v;
+
+ if (copy_from_user(&v, uattr, sizeof(*uattr)))
+ return -EFAULT;
+
+ memset(attr, 0, sizeof(*attr));
+ attr->mq_flags = v.mq_flags;
+ attr->mq_maxmsg = v.mq_maxmsg;
+ attr->mq_msgsize = v.mq_msgsize;
+ attr->mq_curmsgs = v.mq_curmsgs;
+ return 0;
+}
+
+static inline int put_compat_mq_attr(const struct mq_attr *attr,
+ struct compat_mq_attr __user *uattr)
+{
+ struct compat_mq_attr v;
+
+ memset(&v, 0, sizeof(v));
+ v.mq_flags = attr->mq_flags;
+ v.mq_maxmsg = attr->mq_maxmsg;
+ v.mq_msgsize = attr->mq_msgsize;
+ v.mq_curmsgs = attr->mq_curmsgs;
+ if (copy_to_user(uattr, &v, sizeof(*uattr)))
+ return -EFAULT;
+ return 0;
+}
+
+COMPAT_SYSCALL_DEFINE4(mq_open, const char __user *, u_name,
+ int, oflag, compat_mode_t, mode,
+ struct compat_mq_attr __user *, u_attr)
+{
+ struct mq_attr attr, *p = NULL;
+ if (u_attr && oflag & O_CREAT) {
+ p = &attr;
+ if (get_compat_mq_attr(&attr, u_attr))
+ return -EFAULT;
+ }
+ return do_mq_open(u_name, oflag, mode, p);
+}
+
+static int compat_prepare_timeout(const struct compat_timespec __user *p,
+ struct timespec *ts)
+{
+ if (compat_get_timespec(ts, p))
+ return -EFAULT;
+ if (!timespec_valid(ts))
+ return -EINVAL;
+ return 0;
+}
+
+COMPAT_SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes,
+ const char __user *, u_msg_ptr,
+ compat_size_t, msg_len, unsigned int, msg_prio,
+ const struct compat_timespec __user *, u_abs_timeout)
+{
+ struct timespec ts, *p = NULL;
+ if (u_abs_timeout) {
+ int res = compat_prepare_timeout(u_abs_timeout, &ts);
+ if (res)
+ return res;
+ p = &ts;
+ }
+ return do_mq_timedsend(mqdes, u_msg_ptr, msg_len, msg_prio, p);
+}
+
+COMPAT_SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes,
+ char __user *, u_msg_ptr,
+ compat_size_t, msg_len, unsigned int __user *, u_msg_prio,
+ const struct compat_timespec __user *, u_abs_timeout)
+{
+ struct timespec ts, *p = NULL;
+ if (u_abs_timeout) {
+ int res = compat_prepare_timeout(u_abs_timeout, &ts);
+ if (res)
+ return res;
+ p = &ts;
+ }
+ return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, p);
+}
+
+COMPAT_SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
+ const struct compat_sigevent __user *, u_notification)
+{
+ struct sigevent n, *p = NULL;
+ if (u_notification) {
+ if (get_compat_sigevent(&n, u_notification))
+ return -EFAULT;
+ if (n.sigev_notify == SIGEV_THREAD)
+ n.sigev_value.sival_ptr = compat_ptr(n.sigev_value.sival_int);
+ p = &n;
+ }
+ return do_mq_notify(mqdes, p);
+}
+
+COMPAT_SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
+ const struct compat_mq_attr __user *, u_mqstat,
+ struct compat_mq_attr __user *, u_omqstat)
+{
+ int ret;
+ struct mq_attr mqstat, omqstat;
+ struct mq_attr *new = NULL, *old = NULL;
+
+ if (u_mqstat) {
+ new = &mqstat;
+ if (get_compat_mq_attr(new, u_mqstat))
+ return -EFAULT;
+ }
+ if (u_omqstat)
+ old = &omqstat;
+
+ ret = do_mq_getsetattr(mqdes, new, old);
+ if (ret || !old)
+ return ret;
+
+ if (put_compat_mq_attr(old, u_omqstat))
+ return -EFAULT;
+ return 0;
}
+#endif
static const struct inode_operations mqueue_dir_inode_operations = {
.lookup = simple_lookup,
diff --git a/ipc/msg.c b/ipc/msg.c
index 104926dc72be..5b25e0755656 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -97,11 +97,11 @@ static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
static void msg_rcu_free(struct rcu_head *head)
{
- struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
- struct msg_queue *msq = ipc_rcu_to_struct(p);
+ struct kern_ipc_perm *p = container_of(head, struct kern_ipc_perm, rcu);
+ struct msg_queue *msq = container_of(p, struct msg_queue, q_perm);
security_msg_queue_free(msq);
- ipc_rcu_free(head);
+ kvfree(msq);
}
/**
@@ -114,12 +114,12 @@ static void msg_rcu_free(struct rcu_head *head)
static int newque(struct ipc_namespace *ns, struct ipc_params *params)
{
struct msg_queue *msq;
- int id, retval;
+ int retval;
key_t key = params->key;
int msgflg = params->flg;
- msq = ipc_rcu_alloc(sizeof(*msq));
- if (!msq)
+ msq = kvmalloc(sizeof(*msq), GFP_KERNEL);
+ if (unlikely(!msq))
return -ENOMEM;
msq->q_perm.mode = msgflg & S_IRWXUGO;
@@ -128,7 +128,7 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
msq->q_perm.security = NULL;
retval = security_msg_queue_alloc(msq);
if (retval) {
- ipc_rcu_putref(msq, ipc_rcu_free);
+ kvfree(msq);
return retval;
}
@@ -142,10 +142,10 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
INIT_LIST_HEAD(&msq->q_senders);
/* ipc_addid() locks msq upon success. */
- id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
- if (id < 0) {
- ipc_rcu_putref(msq, msg_rcu_free);
- return id;
+ retval = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
+ if (retval < 0) {
+ call_rcu(&msq->q_perm.rcu, msg_rcu_free);
+ return retval;
}
ipc_unlock_object(&msq->q_perm);
@@ -249,7 +249,7 @@ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
free_msg(msg);
}
atomic_sub(msq->q_cbytes, &ns->msg_bytes);
- ipc_rcu_putref(msq, msg_rcu_free);
+ ipc_rcu_putref(&msq->q_perm, msg_rcu_free);
}
/*
@@ -688,7 +688,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
/* enqueue the sender and prepare to block */
ss_add(msq, &s, msgsz);
- if (!ipc_rcu_getref(msq)) {
+ if (!ipc_rcu_getref(&msq->q_perm)) {
err = -EIDRM;
goto out_unlock0;
}
@@ -700,7 +700,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
rcu_read_lock();
ipc_lock_object(&msq->q_perm);
- ipc_rcu_putref(msq, msg_rcu_free);
+ ipc_rcu_putref(&msq->q_perm, msg_rcu_free);
/* raced with RMID? */
if (!ipc_valid_object(&msq->q_perm)) {
err = -EIDRM;
diff --git a/ipc/sem.c b/ipc/sem.c
index 947dc2348271..9e70cd7a17da 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -87,24 +87,6 @@
#include <linux/uaccess.h>
#include "util.h"
-/* One semaphore structure for each semaphore in the system. */
-struct sem {
- int semval; /* current value */
- /*
- * PID of the process that last modified the semaphore. For
- * Linux, specifically these are:
- * - semop
- * - semctl, via SETVAL and SETALL.
- * - at task exit when performing undo adjustments (see exit_sem).
- */
- int sempid;
- spinlock_t lock; /* spinlock for fine-grained semtimedop */
- struct list_head pending_alter; /* pending single-sop operations */
- /* that alter the semaphore */
- struct list_head pending_const; /* pending single-sop operations */
- /* that do not alter the semaphore*/
- time_t sem_otime; /* candidate for sem_otime */
-} ____cacheline_aligned_in_smp;
/* One queue for each sleeping process in the system. */
struct sem_queue {
@@ -175,7 +157,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
* sem_array.sem_undo
*
* b) global or semaphore sem_lock() for read/write:
- * sem_array.sem_base[i].pending_{const,alter}:
+ * sem_array.sems[i].pending_{const,alter}:
*
* c) special:
* sem_undo_list.list_proc:
@@ -250,7 +232,7 @@ static void unmerge_queues(struct sem_array *sma)
*/
list_for_each_entry_safe(q, tq, &sma->pending_alter, list) {
struct sem *curr;
- curr = &sma->sem_base[q->sops[0].sem_num];
+ curr = &sma->sems[q->sops[0].sem_num];
list_add_tail(&q->list, &curr->pending_alter);
}
@@ -270,7 +252,7 @@ static void merge_queues(struct sem_array *sma)
{
int i;
for (i = 0; i < sma->sem_nsems; i++) {
- struct sem *sem = sma->sem_base + i;
+ struct sem *sem = &sma->sems[i];
list_splice_init(&sem->pending_alter, &sma->pending_alter);
}
@@ -278,11 +260,11 @@ static void merge_queues(struct sem_array *sma)
static void sem_rcu_free(struct rcu_head *head)
{
- struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
- struct sem_array *sma = ipc_rcu_to_struct(p);
+ struct kern_ipc_perm *p = container_of(head, struct kern_ipc_perm, rcu);
+ struct sem_array *sma = container_of(p, struct sem_array, sem_perm);
security_sem_free(sma);
- ipc_rcu_free(head);
+ kvfree(sma);
}
/*
@@ -306,7 +288,7 @@ static void complexmode_enter(struct sem_array *sma)
sma->use_global_lock = USE_GLOBAL_LOCK_HYSTERESIS;
for (i = 0; i < sma->sem_nsems; i++) {
- sem = sma->sem_base + i;
+ sem = &sma->sems[i];
spin_lock(&sem->lock);
spin_unlock(&sem->lock);
}
@@ -366,7 +348,7 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
*
* Both facts are tracked by use_global_mode.
*/
- sem = sma->sem_base + sops->sem_num;
+ sem = &sma->sems[sops->sem_num];
/*
* Initial check for use_global_lock. Just an optimization,
@@ -421,7 +403,7 @@ static inline void sem_unlock(struct sem_array *sma, int locknum)
complexmode_tryleave(sma);
ipc_unlock_object(&sma->sem_perm);
} else {
- struct sem *sem = sma->sem_base + locknum;
+ struct sem *sem = &sma->sems[locknum];
spin_unlock(&sem->lock);
}
}
@@ -456,7 +438,7 @@ static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns
static inline void sem_lock_and_putref(struct sem_array *sma)
{
sem_lock(sma, NULL, -1);
- ipc_rcu_putref(sma, sem_rcu_free);
+ ipc_rcu_putref(&sma->sem_perm, sem_rcu_free);
}
static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
@@ -464,6 +446,24 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
ipc_rmid(&sem_ids(ns), &s->sem_perm);
}
+static struct sem_array *sem_alloc(size_t nsems)
+{
+ struct sem_array *sma;
+ size_t size;
+
+ if (nsems > (INT_MAX - sizeof(*sma)) / sizeof(sma->sems[0]))
+ return NULL;
+
+ size = sizeof(*sma) + nsems * sizeof(sma->sems[0]);
+ sma = kvmalloc(size, GFP_KERNEL);
+ if (unlikely(!sma))
+ return NULL;
+
+ memset(sma, 0, size);
+
+ return sma;
+}
+
/**
* newary - Create a new semaphore set
* @ns: namespace
@@ -473,10 +473,8 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
*/
static int newary(struct ipc_namespace *ns, struct ipc_params *params)
{
- int id;
int retval;
struct sem_array *sma;
- int size;
key_t key = params->key;
int nsems = params->u.nsems;
int semflg = params->flg;
@@ -487,29 +485,24 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
if (ns->used_sems + nsems > ns->sc_semmns)
return -ENOSPC;
- size = sizeof(*sma) + nsems * sizeof(struct sem);
- sma = ipc_rcu_alloc(size);
+ sma = sem_alloc(nsems);
if (!sma)
return -ENOMEM;
- memset(sma, 0, size);
-
sma->sem_perm.mode = (semflg & S_IRWXUGO);
sma->sem_perm.key = key;
sma->sem_perm.security = NULL;
retval = security_sem_alloc(sma);
if (retval) {
- ipc_rcu_putref(sma, ipc_rcu_free);
+ kvfree(sma);
return retval;
}
- sma->sem_base = (struct sem *) &sma[1];
-
for (i = 0; i < nsems; i++) {
- INIT_LIST_HEAD(&sma->sem_base[i].pending_alter);
- INIT_LIST_HEAD(&sma->sem_base[i].pending_const);
- spin_lock_init(&sma->sem_base[i].lock);
+ INIT_LIST_HEAD(&sma->sems[i].pending_alter);
+ INIT_LIST_HEAD(&sma->sems[i].pending_const);
+ spin_lock_init(&sma->sems[i].lock);
}
sma->complex_count = 0;
@@ -520,10 +513,10 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
sma->sem_nsems = nsems;
sma->sem_ctime = get_seconds();
- id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
- if (id < 0) {
- ipc_rcu_putref(sma, sem_rcu_free);
- return id;
+ retval = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
+ if (retval < 0) {
+ call_rcu(&sma->sem_perm.rcu, sem_rcu_free);
+ return retval;
}
ns->used_sems += nsems;
@@ -612,7 +605,7 @@ static int perform_atomic_semop_slow(struct sem_array *sma, struct sem_queue *q)
un = q->undo;
for (sop = sops; sop < sops + nsops; sop++) {
- curr = sma->sem_base + sop->sem_num;
+ curr = &sma->sems[sop->sem_num];
sem_op = sop->sem_op;
result = curr->semval;
@@ -639,7 +632,7 @@ static int perform_atomic_semop_slow(struct sem_array *sma, struct sem_queue *q)
sop--;
pid = q->pid;
while (sop >= sops) {
- sma->sem_base[sop->sem_num].sempid = pid;
+ sma->sems[sop->sem_num].sempid = pid;
sop--;
}
@@ -661,7 +654,7 @@ undo:
sop--;
while (sop >= sops) {
sem_op = sop->sem_op;
- sma->sem_base[sop->sem_num].semval -= sem_op;
+ sma->sems[sop->sem_num].semval -= sem_op;
if (sop->sem_flg & SEM_UNDO)
un->semadj[sop->sem_num] += sem_op;
sop--;
@@ -692,7 +685,7 @@ static int perform_atomic_semop(struct sem_array *sma, struct sem_queue *q)
* until the operations can go through.
*/
for (sop = sops; sop < sops + nsops; sop++) {
- curr = sma->sem_base + sop->sem_num;
+ curr = &sma->sems[sop->sem_num];
sem_op = sop->sem_op;
result = curr->semval;
@@ -716,7 +709,7 @@ static int perform_atomic_semop(struct sem_array *sma, struct sem_queue *q)
}
for (sop = sops; sop < sops + nsops; sop++) {
- curr = sma->sem_base + sop->sem_num;
+ curr = &sma->sems[sop->sem_num];
sem_op = sop->sem_op;
result = curr->semval;
@@ -815,7 +808,7 @@ static int wake_const_ops(struct sem_array *sma, int semnum,
if (semnum == -1)
pending_list = &sma->pending_const;
else
- pending_list = &sma->sem_base[semnum].pending_const;
+ pending_list = &sma->sems[semnum].pending_const;
list_for_each_entry_safe(q, tmp, pending_list, list) {
int error = perform_atomic_semop(sma, q);
@@ -856,7 +849,7 @@ static int do_smart_wakeup_zero(struct sem_array *sma, struct sembuf *sops,
for (i = 0; i < nsops; i++) {
int num = sops[i].sem_num;
- if (sma->sem_base[num].semval == 0) {
+ if (sma->sems[num].semval == 0) {
got_zero = 1;
semop_completed |= wake_const_ops(sma, num, wake_q);
}
@@ -867,7 +860,7 @@ static int do_smart_wakeup_zero(struct sem_array *sma, struct sembuf *sops,
* Assume all were changed.
*/
for (i = 0; i < sma->sem_nsems; i++) {
- if (sma->sem_base[i].semval == 0) {
+ if (sma->sems[i].semval == 0) {
got_zero = 1;
semop_completed |= wake_const_ops(sma, i, wake_q);
}
@@ -909,7 +902,7 @@ static int update_queue(struct sem_array *sma, int semnum, struct wake_q_head *w
if (semnum == -1)
pending_list = &sma->pending_alter;
else
- pending_list = &sma->sem_base[semnum].pending_alter;
+ pending_list = &sma->sems[semnum].pending_alter;
again:
list_for_each_entry_safe(q, tmp, pending_list, list) {
@@ -922,7 +915,7 @@ again:
* be in the per semaphore pending queue, and decrements
* cannot be successful if the value is already 0.
*/
- if (semnum != -1 && sma->sem_base[semnum].semval == 0)
+ if (semnum != -1 && sma->sems[semnum].semval == 0)
break;
error = perform_atomic_semop(sma, q);
@@ -959,9 +952,9 @@ again:
static void set_semotime(struct sem_array *sma, struct sembuf *sops)
{
if (sops == NULL) {
- sma->sem_base[0].sem_otime = get_seconds();
+ sma->sems[0].sem_otime = get_seconds();
} else {
- sma->sem_base[sops[0].sem_num].sem_otime =
+ sma->sems[sops[0].sem_num].sem_otime =
get_seconds();
}
}
@@ -1067,9 +1060,9 @@ static int count_semcnt(struct sem_array *sma, ushort semnum,
semcnt = 0;
/* First: check the simple operations. They are easy to evaluate */
if (count_zero)
- l = &sma->sem_base[semnum].pending_const;
+ l = &sma->sems[semnum].pending_const;
else
- l = &sma->sem_base[semnum].pending_alter;
+ l = &sma->sems[semnum].pending_alter;
list_for_each_entry(q, l, list) {
/* all task on a per-semaphore list sleep on exactly
@@ -1124,7 +1117,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
wake_up_sem_queue_prepare(q, -EIDRM, &wake_q);
}
for (i = 0; i < sma->sem_nsems; i++) {
- struct sem *sem = sma->sem_base + i;
+ struct sem *sem = &sma->sems[i];
list_for_each_entry_safe(q, tq, &sem->pending_const, list) {
unlink_queue(sma, q);
wake_up_sem_queue_prepare(q, -EIDRM, &wake_q);
@@ -1142,7 +1135,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
wake_up_q(&wake_q);
ns->used_sems -= sma->sem_nsems;
- ipc_rcu_putref(sma, sem_rcu_free);
+ ipc_rcu_putref(&sma->sem_perm, sem_rcu_free);
}
static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version)
@@ -1174,9 +1167,9 @@ static time_t get_semotime(struct sem_array *sma)
int i;
time_t res;
- res = sma->sem_base[0].sem_otime;
+ res = sma->sems[0].sem_otime;
for (i = 1; i < sma->sem_nsems; i++) {
- time_t to = sma->sem_base[i].sem_otime;
+ time_t to = sma->sems[i].sem_otime;
if (to > res)
res = to;
@@ -1325,7 +1318,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
return -EIDRM;
}
- curr = &sma->sem_base[semnum];
+ curr = &sma->sems[semnum];
ipc_assert_locked_object(&sma->sem_perm);
list_for_each_entry(un, &sma->list_id, list_id)
@@ -1382,15 +1375,16 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
goto out_unlock;
}
if (nsems > SEMMSL_FAST) {
- if (!ipc_rcu_getref(sma)) {
+ if (!ipc_rcu_getref(&sma->sem_perm)) {
err = -EIDRM;
goto out_unlock;
}
sem_unlock(sma, -1);
rcu_read_unlock();
- sem_io = ipc_alloc(sizeof(ushort)*nsems);
+ sem_io = kvmalloc_array(nsems, sizeof(ushort),
+ GFP_KERNEL);
if (sem_io == NULL) {
- ipc_rcu_putref(sma, sem_rcu_free);
+ ipc_rcu_putref(&sma->sem_perm, sem_rcu_free);
return -ENOMEM;
}
@@ -1402,7 +1396,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
}
}
for (i = 0; i < sma->sem_nsems; i++)
- sem_io[i] = sma->sem_base[i].semval;
+ sem_io[i] = sma->sems[i].semval;
sem_unlock(sma, -1);
rcu_read_unlock();
err = 0;
@@ -1415,29 +1409,30 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
int i;
struct sem_undo *un;
- if (!ipc_rcu_getref(sma)) {
+ if (!ipc_rcu_getref(&sma->sem_perm)) {
err = -EIDRM;
goto out_rcu_wakeup;
}
rcu_read_unlock();
if (nsems > SEMMSL_FAST) {
- sem_io = ipc_alloc(sizeof(ushort)*nsems);
+ sem_io = kvmalloc_array(nsems, sizeof(ushort),
+ GFP_KERNEL);
if (sem_io == NULL) {
- ipc_rcu_putref(sma, sem_rcu_free);
+ ipc_rcu_putref(&sma->sem_perm, sem_rcu_free);
return -ENOMEM;
}
}
if (copy_from_user(sem_io, p, nsems*sizeof(ushort))) {
- ipc_rcu_putref(sma, sem_rcu_free);
+ ipc_rcu_putref(&sma->sem_perm, sem_rcu_free);
err = -EFAULT;
goto out_free;
}
for (i = 0; i < nsems; i++) {
if (sem_io[i] > SEMVMX) {
- ipc_rcu_putref(sma, sem_rcu_free);
+ ipc_rcu_putref(&sma->sem_perm, sem_rcu_free);
err = -ERANGE;
goto out_free;
}
@@ -1450,8 +1445,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
}
for (i = 0; i < nsems; i++) {
- sma->sem_base[i].semval = sem_io[i];
- sma->sem_base[i].sempid = task_tgid_vnr(current);
+ sma->sems[i].semval = sem_io[i];
+ sma->sems[i].sempid = task_tgid_vnr(current);
}
ipc_assert_locked_object(&sma->sem_perm);
@@ -1476,7 +1471,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
err = -EIDRM;
goto out_unlock;
}
- curr = &sma->sem_base[semnum];
+ curr = &sma->sems[semnum];
switch (cmd) {
case GETVAL:
@@ -1500,7 +1495,7 @@ out_rcu_wakeup:
wake_up_q(&wake_q);
out_free:
if (sem_io != fast_sem_io)
- ipc_free(sem_io);
+ kvfree(sem_io);
return err;
}
@@ -1719,7 +1714,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
}
nsems = sma->sem_nsems;
- if (!ipc_rcu_getref(sma)) {
+ if (!ipc_rcu_getref(&sma->sem_perm)) {
rcu_read_unlock();
un = ERR_PTR(-EIDRM);
goto out;
@@ -1729,7 +1724,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
/* step 2: allocate new undo structure */
new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
if (!new) {
- ipc_rcu_putref(sma, sem_rcu_free);
+ ipc_rcu_putref(&sma->sem_perm, sem_rcu_free);
return ERR_PTR(-ENOMEM);
}
@@ -1932,7 +1927,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
*/
if (nsops == 1) {
struct sem *curr;
- curr = &sma->sem_base[sops->sem_num];
+ curr = &sma->sems[sops->sem_num];
if (alter) {
if (sma->complex_count) {
@@ -2146,7 +2141,7 @@ void exit_sem(struct task_struct *tsk)
/* perform adjustments registered in un */
for (i = 0; i < sma->sem_nsems; i++) {
- struct sem *semaphore = &sma->sem_base[i];
+ struct sem *semaphore = &sma->sems[i];
if (un->semadj[i]) {
semaphore->semval += un->semadj[i];
/*
diff --git a/ipc/shm.c b/ipc/shm.c
index 34c4344e8d4b..28a444861a8f 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -174,11 +174,12 @@ static inline void shm_lock_by_ptr(struct shmid_kernel *ipcp)
static void shm_rcu_free(struct rcu_head *head)
{
- struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
- struct shmid_kernel *shp = ipc_rcu_to_struct(p);
-
+ struct kern_ipc_perm *ptr = container_of(head, struct kern_ipc_perm,
+ rcu);
+ struct shmid_kernel *shp = container_of(ptr, struct shmid_kernel,
+ shm_perm);
security_shm_free(shp);
- ipc_rcu_free(head);
+ kvfree(shp);
}
static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s)
@@ -241,7 +242,7 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
user_shm_unlock(i_size_read(file_inode(shm_file)),
shp->mlock_user);
fput(shm_file);
- ipc_rcu_putref(shp, shm_rcu_free);
+ ipc_rcu_putref(&shp->shm_perm, shm_rcu_free);
}
/*
@@ -452,7 +453,7 @@ static int shm_fsync(struct file *file, loff_t start, loff_t end, int datasync)
if (!sfd->file->f_op->fsync)
return -EINVAL;
- return call_fsync(sfd->file, start, end, datasync);
+ return sfd->file->f_op->fsync(sfd->file, start, end, datasync);
}
static long shm_fallocate(struct file *file, int mode, loff_t offset,
@@ -529,7 +530,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
size_t numpages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
struct file *file;
char name[13];
- int id;
vm_flags_t acctflag = 0;
if (size < SHMMIN || size > ns->shm_ctlmax)
@@ -542,8 +542,8 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
ns->shm_tot + numpages > ns->shm_ctlall)
return -ENOSPC;
- shp = ipc_rcu_alloc(sizeof(*shp));
- if (!shp)
+ shp = kvmalloc(sizeof(*shp), GFP_KERNEL);
+ if (unlikely(!shp))
return -ENOMEM;
shp->shm_perm.key = key;
@@ -553,7 +553,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
shp->shm_perm.security = NULL;
error = security_shm_alloc(shp);
if (error) {
- ipc_rcu_putref(shp, ipc_rcu_free);
+ kvfree(shp);
return error;
}
@@ -598,11 +598,9 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
shp->shm_file = file;
shp->shm_creator = current;
- id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
- if (id < 0) {
- error = id;
+ error = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
+ if (error < 0)
goto no_id;
- }
list_add(&shp->shm_clist, &current->sysvshm.shm_clist);
@@ -624,7 +622,7 @@ no_id:
user_shm_unlock(size, shp->mlock_user);
fput(file);
no_file:
- ipc_rcu_putref(shp, shm_rcu_free);
+ call_rcu(&shp->shm_perm.rcu, shm_rcu_free);
return error;
}
diff --git a/ipc/util.c b/ipc/util.c
index caec7b1bfaa3..1a2cb02467ab 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -232,6 +232,7 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size)
idr_preload(GFP_KERNEL);
+ atomic_set(&new->refcount, 1);
spin_lock_init(&new->lock);
new->deleted = false;
rcu_read_lock();
@@ -394,70 +395,18 @@ void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
ipcp->deleted = true;
}
-/**
- * ipc_alloc - allocate ipc space
- * @size: size desired
- *
- * Allocate memory from the appropriate pools and return a pointer to it.
- * NULL is returned if the allocation fails
- */
-void *ipc_alloc(int size)
-{
- return kvmalloc(size, GFP_KERNEL);
-}
-
-/**
- * ipc_free - free ipc space
- * @ptr: pointer returned by ipc_alloc
- *
- * Free a block created with ipc_alloc().
- */
-void ipc_free(void *ptr)
+int ipc_rcu_getref(struct kern_ipc_perm *ptr)
{
- kvfree(ptr);
+ return atomic_inc_not_zero(&ptr->refcount);
}
-/**
- * ipc_rcu_alloc - allocate ipc and rcu space
- * @size: size desired
- *
- * Allocate memory for the rcu header structure + the object.
- * Returns the pointer to the object or NULL upon failure.
- */
-void *ipc_rcu_alloc(int size)
+void ipc_rcu_putref(struct kern_ipc_perm *ptr,
+ void (*func)(struct rcu_head *head))
{
- /*
- * We prepend the allocation with the rcu struct
- */
- struct ipc_rcu *out = ipc_alloc(sizeof(struct ipc_rcu) + size);
- if (unlikely(!out))
- return NULL;
- atomic_set(&out->refcount, 1);
- return out + 1;
-}
-
-int ipc_rcu_getref(void *ptr)
-{
- struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
-
- return atomic_inc_not_zero(&p->refcount);
-}
-
-void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head))
-{
- struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
-
- if (!atomic_dec_and_test(&p->refcount))
+ if (!atomic_dec_and_test(&ptr->refcount))
return;
- call_rcu(&p->rcu, func);
-}
-
-void ipc_rcu_free(struct rcu_head *head)
-{
- struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
-
- kvfree(p);
+ call_rcu(&ptr->rcu, func);
}
/**
diff --git a/ipc/util.h b/ipc/util.h
index 60ddccca464d..c692010e6f0a 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -47,13 +47,6 @@ static inline void msg_exit_ns(struct ipc_namespace *ns) { }
static inline void shm_exit_ns(struct ipc_namespace *ns) { }
#endif
-struct ipc_rcu {
- struct rcu_head rcu;
- atomic_t refcount;
-} ____cacheline_aligned_in_smp;
-
-#define ipc_rcu_to_struct(p) ((void *)(p+1))
-
/*
* Structure that holds the parameters needed by the ipc operations
* (see after)
@@ -114,22 +107,18 @@ void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *);
/* must be called with ipcp locked */
int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flg);
-/* for rare, potentially huge allocations.
- * both function can sleep
- */
-void *ipc_alloc(int size);
-void ipc_free(void *ptr);
-
/*
* For allocation that need to be freed by RCU.
* Objects are reference counted, they start with reference count 1.
* getref increases the refcount, the putref call that reduces the recount
* to 0 schedules the rcu destruction. Caller must guarantee locking.
+ *
+ * refcount is initialized by ipc_addid(), before that point call_rcu()
+ * must be used.
*/
-void *ipc_rcu_alloc(int size);
-int ipc_rcu_getref(void *ptr);
-void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head));
-void ipc_rcu_free(struct rcu_head *head);
+int ipc_rcu_getref(struct kern_ipc_perm *ptr);
+void ipc_rcu_putref(struct kern_ipc_perm *ptr,
+ void (*func)(struct rcu_head *head));
struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int);
struct kern_ipc_perm *ipc_obtain_object_idr(struct ipc_ids *ids, int id);