Merge branch 'ipc-scalability'
Merge commit. Changes below are compared with the first parent: 149b3060.
Changes
7 files changed · +540 −341
| File | Status | Add | Del | Content |
|---|---|---|---|---|
| include/linux/ipc_namespace.h | modified | +1 | −1 | View file |
| ipc/msg.c | modified | +50 | −74 | View file |
| ipc/msgutil.c | modified | +53 | −57 | View file |
| ipc/sem.c | modified | +315 | −159 | View file |
| ipc/shm.c | modified | +1 | −1 | View file |
| ipc/util.c | modified | +108 | −42 | View file |
| ipc/util.h | modified | +12 | −7 | View file |
Only the first 600 diff lines are shown. Download the complete patch.
include/linux/ipc_namespace.h
View filediff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h
index ae221a7..c4d870b 100644
--- a/include/linux/ipc_namespace.h
+++ b/include/linux/ipc_namespace.h
@@ -43,8 +43,8 @@ struct ipc_namespace {
size_t shm_ctlmax;
size_t shm_ctlall;
+ unsigned long shm_tot;
int shm_ctlmni;
- int shm_tot;
/*
* Defines whether IPC_RMID is forced for _all_ shm segments regardless
* of shmctl()
ipc/msg.c
View filediff --git a/ipc/msg.c b/ipc/msg.c
index fede1d0..d0c6d96 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -66,6 +66,7 @@ struct msg_sender {
#define SEARCH_EQUAL 2
#define SEARCH_NOTEQUAL 3
#define SEARCH_LESSEQUAL 4
+#define SEARCH_NUMBER 5
#define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS])
@@ -237,14 +238,9 @@ static inline void ss_del(struct msg_sender *mss)
static void ss_wakeup(struct list_head *h, int kill)
{
- struct list_head *tmp;
+ struct msg_sender *mss, *t;
- tmp = h->next;
- while (tmp != h) {
- struct msg_sender *mss;
-
- mss = list_entry(tmp, struct msg_sender, list);
- tmp = tmp->next;
+ list_for_each_entry_safe(mss, t, h, list) {
if (kill)
mss->list.next = NULL;
wake_up_process(mss->tsk);
@@ -253,14 +249,9 @@ static void ss_wakeup(struct list_head *h, int kill)
static void expunge_all(struct msg_queue *msq, int res)
{
- struct list_head *tmp;
-
- tmp = msq->q_receivers.next;
- while (tmp != &msq->q_receivers) {
- struct msg_receiver *msr;
+ struct msg_receiver *msr, *t;
- msr = list_entry(tmp, struct msg_receiver, r_list);
- tmp = tmp->next;
+ list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) {
msr->r_msg = NULL;
wake_up_process(msr->r_tsk);
smp_mb();
@@ -278,7 +269,7 @@ static void expunge_all(struct msg_queue *msq, int res)
*/
static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
{
- struct list_head *tmp;
+ struct msg_msg *msg, *t;
struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
expunge_all(msq, -EIDRM);
@@ -286,11 +277,7 @@ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
msg_rmid(ns, msq);
msg_unlock(msq);
- tmp = msq->q_messages.next;
- while (tmp != &msq->q_messages) {
- struct msg_msg *msg = list_entry(tmp, struct msg_msg, m_list);
-
- tmp = tmp->next;
+ list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) {
atomic_dec(&ns->msg_hdrs);
free_msg(msg);
}
@@ -583,6 +570,7 @@ static int testmsg(struct msg_msg *msg, long type, int mode)
switch(mode)
{
case SEARCH_ANY:
+ case SEARCH_NUMBER:
return 1;
case SEARCH_LESSEQUAL:
if (msg->m_type <=type)
@@ -602,14 +590,9 @@ static int testmsg(struct msg_msg *msg, long type, int mode)
static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg)
{
- struct list_head *tmp;
+ struct msg_receiver *msr, *t;
- tmp = msq->q_receivers.next;
- while (tmp != &msq->q_receivers) {
- struct msg_receiver *msr;
-
- msr = list_entry(tmp, struct msg_receiver, r_list);
- tmp = tmp->next;
+ list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) {
if (testmsg(msg, msr->r_msgtype, msr->r_mode) &&
!security_msg_queue_msgrcv(msq, msg, msr->r_tsk,
msr->r_msgtype, msr->r_mode)) {
@@ -685,7 +668,12 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
goto out_unlock_free;
}
ss_add(msq, &s);
- ipc_rcu_getref(msq);
+
+ if (!ipc_rcu_getref(msq)) {
+ err = -EIDRM;
+ goto out_unlock_free;
+ }
+
msg_unlock(msq);
schedule();
@@ -738,6 +726,8 @@ SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
static inline int convert_mode(long *msgtyp, int msgflg)
{
+ if (msgflg & MSG_COPY)
+ return SEARCH_NUMBER;
/*
* find message of correct type.
* msgtyp = 0 => get first.
@@ -774,14 +764,10 @@ static long do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz)
* This function creates new kernel message structure, large enough to store
* bufsz message bytes.
*/
-static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz,
- int msgflg, long *msgtyp,
- unsigned long *copy_number)
+static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz)
{
struct msg_msg *copy;
- *copy_number = *msgtyp;
- *msgtyp = 0;
/*
* Create dummy message to copy real message to.
*/
@@ -797,9 +783,7 @@ static inline void free_copy(struct msg_msg *copy)
free_msg(copy);
}
#else
-static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz,
- int msgflg, long *msgtyp,
- unsigned long *copy_number)
+static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz)
{
return ERR_PTR(-ENOSYS);
}
@@ -809,6 +793,30 @@ static inline void free_copy(struct msg_msg *copy)
}
#endif
+static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode)
+{
+ struct msg_msg *msg;
+ long count = 0;
+
+ list_for_each_entry(msg, &msq->q_messages, m_list) {
+ if (testmsg(msg, *msgtyp, mode) &&
+ !security_msg_queue_msgrcv(msq, msg, current,
+ *msgtyp, mode)) {
+ if (mode == SEARCH_LESSEQUAL && msg->m_type != 1) {
+ *msgtyp = msg->m_type - 1;
+ } else if (mode == SEARCH_NUMBER) {
+ if (*msgtyp == count)
+ return msg;
+ } else
+ return msg;
+ count++;
+ }
+ }
+
+ return ERR_PTR(-EAGAIN);
+}
+
+
long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
int msgflg,
long (*msg_handler)(void __user *, struct msg_msg *, size_t))
@@ -818,15 +826,13 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
int mode;
struct ipc_namespace *ns;
struct msg_msg *copy = NULL;
- unsigned long copy_number = 0;
ns = current->nsproxy->ipc_ns;
if (msqid < 0 || (long) bufsz < 0)
return -EINVAL;
if (msgflg & MSG_COPY) {
- copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax),
- msgflg, &msgtyp, ©_number);
+ copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax));
if (IS_ERR(copy))
return PTR_ERR(copy);
}
@@ -840,45 +846,13 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
for (;;) {
struct msg_receiver msr_d;
- struct list_head *tmp;
- long msg_counter = 0;
msg = ERR_PTR(-EACCES);
if (ipcperms(ns, &msq->q_perm, S_IRUGO))
goto out_unlock;
- msg = ERR_PTR(-EAGAIN);
- tmp = msq->q_messages.next;
- while (tmp != &msq->q_messages) {
- struct msg_msg *walk_msg;
-
- walk_msg = list_entry(tmp, struct msg_msg, m_list);
- if (testmsg(walk_msg, msgtyp, mode) &&
- !security_msg_queue_msgrcv(msq, walk_msg, current,
- msgtyp, mode)) {
-
- msg = walk_msg;
- if (mode == SEARCH_LESSEQUAL &&
- walk_msg->m_type != 1) {
- msgtyp = walk_msg->m_type - 1;
- } else if (msgflg & MSG_COPY) {
- if (copy_number == msg_counter) {
- /*
- * Found requested message.
- * Copy it.
- */
- msg = copy_msg(msg, copy);
- if (IS_ERR(msg))
- goto out_unlock;
- break;
- }
- msg = ERR_PTR(-EAGAIN);
- } else
- break;
- msg_counter++;
- }
- tmp = tmp->next;
- }
+ msg = find_msg(msq, &msgtyp, mode);
+
if (!IS_ERR(msg)) {
/*
* Found a suitable message.
@@ -892,8 +866,10 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
* If we are copying, then do not unlink message and do
* not update queue parameters.
*/
- if (msgflg & MSG_COPY)
+ if (msgflg & MSG_COPY) {
+ msg = copy_msg(msg, copy);
goto out_unlock;
+ }
list_del(&msg->m_list);
msq->q_qnum--;
msq->q_rtime = get_seconds();
ipc/msgutil.c
View filediff --git a/ipc/msgutil.c b/ipc/msgutil.c
index 5df8e4b..d43439e 100644
--- a/ipc/msgutil.c
+++ b/ipc/msgutil.c
@@ -17,7 +17,7 @@
#include <linux/ipc_namespace.h>
#include <linux/utsname.h>
#include <linux/proc_fs.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "util.h"
@@ -37,59 +37,70 @@ struct ipc_namespace init_ipc_ns = {
atomic_t nr_ipc_ns = ATOMIC_INIT(1);
struct msg_msgseg {
- struct msg_msgseg* next;
+ struct msg_msgseg *next;
/* the next part of the message follows immediately */
};
-#define DATALEN_MSG (PAGE_SIZE-sizeof(struct msg_msg))
-#define DATALEN_SEG (PAGE_SIZE-sizeof(struct msg_msgseg))
+#define DATALEN_MSG (int)(PAGE_SIZE-sizeof(struct msg_msg))
+#define DATALEN_SEG (int)(PAGE_SIZE-sizeof(struct msg_msgseg))
-struct msg_msg *load_msg(const void __user *src, int len)
+
+static struct msg_msg *alloc_msg(int len)
{
struct msg_msg *msg;
struct msg_msgseg **pseg;
- int err;
int alen;
- alen = len;
- if (alen > DATALEN_MSG)
- alen = DATALEN_MSG;
-
+ alen = min(len, DATALEN_MSG);
msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL);
if (msg == NULL)
- return ERR_PTR(-ENOMEM);
+ return NULL;
msg->next = NULL;
msg->security = NULL;
- if (copy_from_user(msg + 1, src, alen)) {
- err = -EFAULT;
- goto out_err;
- }
-
len -= alen;
- src = ((char __user *)src) + alen;
pseg = &msg->next;
while (len > 0) {
struct msg_msgseg *seg;
- alen = len;
- if (alen > DATALEN_SEG)
- alen = DATALEN_SEG;
- seg = kmalloc(sizeof(*seg) + alen,
- GFP_KERNEL);
- if (seg == NULL) {
- err = -ENOMEM;
+ alen = min(len, DATALEN_SEG);
+ seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL);
+ if (seg == NULL)
goto out_err;
- }
*pseg = seg;
seg->next = NULL;
- if (copy_from_user(seg + 1, src, alen)) {
- err = -EFAULT;
- goto out_err;
- }
pseg = &seg->next;
len -= alen;
- src = ((char __user *)src) + alen;
+ }
+
+ return msg;
+
+out_err:
+ free_msg(msg);
+ return NULL;
+}
+
+struct msg_msg *load_msg(const void __user *src, int len)
+{
+ struct msg_msg *msg;
+ struct msg_msgseg *seg;
+ int err = -EFAULT;
+ int alen;
+
+ msg = alloc_msg(len);
+ if (msg == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ alen = min(len, DATALEN_MSG);
+ if (copy_from_user(msg + 1, src, alen))
+ goto out_err;
+
+ for (seg = msg->next; seg != NULL; seg = seg->next) {
+ len -= alen;
+ src = (char __user *)src + alen;
+ alen = min(len, DATALEN_SEG);
+ if (copy_from_user(seg + 1, src, alen))
+ goto out_err;
}
err = security_msg_msg_alloc(msg);
@@ -113,23 +124,16 @@ struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
if (src->m_ts > dst->m_ts)
return ERR_PTR(-EINVAL);
- alen = len;
- if (alen > DATALEN_MSG)
- alen = DATALEN_MSG;
-
+ alen = min(len, DATALEN_MSG);
memcpy(dst + 1, src + 1, alen);
- len -= alen;
- dst_pseg = dst->next;
- src_pseg = src->next;
- while (len > 0) {
- alen = len;
- if (alen > DATALEN_SEG)
- alen = DATALEN_SEG;
- memcpy(dst_pseg + 1, src_pseg + 1, alen);
- dst_pseg = dst_pseg->next;
+ for (dst_pseg = dst->next, src_pseg = src->next;
+ src_pseg != NULL;
+ dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
+
len -= alen;
- src_pseg = src_pseg->next;
+ alen = min(len, DATALEN_SEG);
+ memcpy(dst_pseg + 1, src_pseg + 1, alen);
}
dst->m_type = src->m_type;
@@ -148,24 +152,16 @@ int store_msg(void __user *dest, struct msg_msg *msg, int len)
int alen;
struct msg_msgseg *seg;
- alen = len;
- if (alen > DATALEN_MSG)
- alen = DATALEN_MSG;
+ alen = min(len, DATALEN_MSG);
if (copy_to_user(dest, msg + 1, alen))
return -1;
- len -= alen;
- dest = ((char __user *)dest) + alen;
- seg = msg->next;
- while (len > 0) {
- alen = len;
- if (alen > DATALEN_SEG)
- alen = DATALEN_SEG;
+ for (seg = msg->next; seg != NULL; seg = seg->next) {
+ len -= alen;
+ dest = (char __user *)dest + alen;
+ alen = min(len, DATALEN_SEG);
if (copy_to_user(dest, seg + 1, alen))
return -1;
- len -= alen;
- dest = ((char __user *)dest) + alen;
- seg = seg->next;
}
return 0;
}
ipc/sem.c
View filediff --git a/ipc/sem.c b/ipc/sem.c
index 5b167d0..e78ee31 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -94,12 +94,12 @@
struct sem {
int semval; /* current value */
int sempid; /* pid of last operation */
+ spinlock_t lock; /* spinlock for fine-grained semtimedop */
struct list_head sem_pending; /* pending single-sop operations */
};
/* One queue for each sleeping process in the system. */
struct sem_queue {
- struct list_head simple_list; /* queue of pending operations */
struct list_head list; /* queue of pending operations */
struct task_struct *sleeper; /* this process */
struct sem_undo *undo; /* undo structure */
@@ -138,7 +138,6 @@ struct sem_undo_list {
#define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS])
-#define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm)
#define sem_checkid(sma, semid) ipc_checkid(&sma->sem_perm, semid)
static int newary(struct ipc_namespace *, struct ipc_params *);
@@ -191,47 +190,164 @@ void __init sem_init (void)
}
/*
+ * If the request contains only one semaphore operation, and there are
+ * no complex transactions pending, lock only the semaphore involved.
+ * Otherwise, lock the entire semaphore array, since we either have
+ * multiple semaphores in our own semops, or we need to look at
+ * semaphores from other pending complex operations.
+ *
+ * Carefully guard against sma->complex_count changing between zero
+ * and non-zero while we are spinning for the lock. The value of
+ * sma->complex_count cannot change while we are holding the lock,
+ * so sem_unlock should be fine.
+ *
+ * The global lock path checks that all the local locks have been released,
+ * checking each local lock once. This means that the local lock paths
+ * cannot start their critical sections while the global lock is held.
+ */
+static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
+ int nsops)
+{
+ int locknum;
+ again:
+ if (nsops == 1 && !sma->complex_count) {
+ struct sem *sem = sma->sem_base + sops->sem_num;
+
+ /* Lock just the semaphore we are interested in. */
+ spin_lock(&sem->lock);
+
+ /*
+ * If sma->complex_count was set while we were spinning,
+ * we may need to look at things we did not lock here.
+ */
+ if (unlikely(sma->complex_count)) {
+ spin_unlock(&sem->lock);
+ goto lock_array;
+ }
+
+ /*
+ * Another process is holding the global lock on the
+ * sem_array; we cannot enter our critical section,
+ * but have to wait for the global lock to be released.
+ */
+ if (unlikely(spin_is_locked(&sma->sem_perm.lock))) {
+ spin_unlock(&sem->lock);
+ spin_unlock_wait(&sma->sem_perm.lock);
+ goto again;
+ }
+
+ locknum = sops->sem_num;
+ } else {
+ int i;
+ /*
+ * Lock the semaphore array, and wait for all of the
+ * individual semaphore locks to go away. The code
+ * above ensures no new single-lock holders will enter
+ * their critical section while the array lock is held.
+ */
+ lock_array:
+ spin_lock(&sma->sem_perm.lock);
+ for (i = 0; i < sma->sem_nsems; i++) {
+ struct sem *sem = sma->sem_base + i;
+ spin_unlock_wait(&sem->lock);
+ }
+ locknum = -1;
+ }
+ return locknum;
+}
+
+static inline void sem_unlock(struct sem_array *sma, int locknum)
+{
+ if (locknum == -1) {
+ spin_unlock(&sma->sem_perm.lock);
+ } else {
+ 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.
*/
-static inline struct sem_array *sem_lock(struct ipc_namespace *ns, int id)
+static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns,
+ int id, struct sembuf *sops, int nsops, int *locknum)
+{
+ 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;
+ }
+
+ sma = container_of(ipcp, struct sem_array, sem_perm);
+ *locknum = sem_lock(sma, sops, nsops);
+
+ /* ipc_rmid() may have already freed the ID while sem_lock
+ * was spinning: verify that the structure is still valid
+ */
+ if (!ipcp->deleted)
+ return container_of(ipcp, struct sem_array, sem_perm);
+
+ sem_unlock(sma, *locknum);
+ sma = ERR_PTR(-EINVAL);
+err:
+ rcu_read_unlock();
+ return sma;
+}
+
+static inline struct sem_array *sem_obtain_object(struct ipc_namespace *ns, int id)
{
- struct kern_ipc_perm *ipcp = ipc_lock(&sem_ids(ns), id);
+ struct kern_ipc_perm *ipcp = ipc_obtain_object(&sem_ids(ns), id);
if (IS_ERR(ipcp))
- return (struct sem_array *)ipcp;
+ return ERR_CAST(ipcp);
return container_of(ipcp, struct sem_array, sem_perm);
}
-static inline struct sem_array *sem_lock_check(struct ipc_namespace *ns,
- int id)
+static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns,
+ int id)
{
- struct kern_ipc_perm *ipcp = ipc_lock_check(&sem_ids(ns), id);
+ struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&sem_ids(ns), id);
This diff is incomplete. Download the complete patch.
ipc/shm.c
View fileThis diff is incomplete. Download the complete patch.
ipc/util.c
View fileThis diff is incomplete. Download the complete patch.
ipc/util.h
View fileThis diff is incomplete. Download the complete patch.