mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/7] ipc/sem.c: Performance updates
@ 2009-10-03 17:19 manfred
  2009-10-03 17:19 ` [PATCH 4/7] ipc/sem.c: optimize if semops fail manfred
  0 siblings, 1 reply; 5+ messages in thread
From: manfred @ 2009-10-03 17:19 UTC (permalink / raw)
  To: linux-kernel, linux-kernel; +Cc: Manfred Spraul

From: Manfred Spraul <manfred@colorfullife.com>

[2nd sending due to hitting TABOO. I've switched to git-send-email now.
The patches 1, 2, 3 went to the list on thursday/friday]

Around a month ago, there was some discussion about an improvement
of the sysv sem algorithm:
Most (at least: some important) users only use simple semaphore operations,
therefore it's worthwile to optimize this use case.

The first three patches are from Nick, the remaining 4 patches are from
myself.

The patches are tested, and IMHO ready widespread testing/merging.

Andrew: As far as I can see, there is no maintainer for ipc.
Could you add the patches to -mm and forward them to Linus
afterwards?

--
	Manfred

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 4/7] ipc/sem.c: optimize if semops fail
  2009-10-03 17:19 [PATCH 0/7] ipc/sem.c: Performance updates manfred
@ 2009-10-03 17:19 ` manfred
  2009-10-03 17:19   ` [PATCH 5/7] ipc/sem.c: add a per-semaphore pending list manfred
  0 siblings, 1 reply; 5+ messages in thread
From: manfred @ 2009-10-03 17:19 UTC (permalink / raw)
  To: linux-kernel, linux-kernel; +Cc: Manfred Spraul

From: Manfred Spraul <manfred@colorfullife.com>

Reduce the amount of scanning of the list of pending semaphore operations:
If try_atomic_semop failed, then no changes were applied. Thus no need to
restart.
Additionally, this patch correct an incorrect comment:
It's possible to wait for arbitrary semaphore values (do a dec by <x>,
wait-for-zero, inc by <x> in one atomic operation)

Both changes are from Nick Piggin, the patch is the result of a different
split of the individual changes.

Signed-off-by: Manfred Spraul <manfred@colorfullife.com>
---
 ipc/sem.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/ipc/sem.c b/ipc/sem.c
index 3629ef8..9425817 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -444,13 +444,13 @@ again:
 		 * of the completed operation:
 		 * - if the operation modified the array, then restart from the
 		 *   head of the queue and check for threads that might be
-		 *   waiting for semaphore values to become 0.
+		 *   waiting for the new semaphore values.
 		 * - if the operation didn't modify the array, then just
 		 *   continue.
 		 */
 		alter = q->alter;
 		wake_up_sem_queue(q, error);
-		if (alter)
+		if (alter && !error)
 			goto again;
 	}
 }
-- 
1.6.2.5


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 5/7] ipc/sem.c: add a per-semaphore pending list
  2009-10-03 17:19 ` [PATCH 4/7] ipc/sem.c: optimize if semops fail manfred
@ 2009-10-03 17:19   ` manfred
  2009-10-03 17:19     ` [PATCH 6/7] ipc/sem.c: optimize single semop operations manfred
  0 siblings, 1 reply; 5+ messages in thread
From: manfred @ 2009-10-03 17:19 UTC (permalink / raw)
  To: linux-kernel, linux-kernel; +Cc: Manfred Spraul

From: Manfred Spraul <manfred@colorfullife.com>

Based on Nick's findings:

sysv sem has the concept of semaphore arrays that consist out of multiple
semaphores. Atomic operations that affect multiple semaphores are supported.

The patch is the first step for optimizing simple, single semaphore operations:
In addition to the global list of all pending operations, a 2nd, per-semaphore
list with the simple operations is added.

Note: this patch does not make sense by itself, the new list is used nowhere.

Signed-off-by: Manfred Spraul <manfred@colorfullife.com>
---
 include/linux/sem.h |    5 ++++-
 ipc/sem.c           |   37 ++++++++++++++++++++++++++++++++-----
 2 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/include/linux/sem.h b/include/linux/sem.h
index 1b191c1..8a4adbe 100644
--- a/include/linux/sem.h
+++ b/include/linux/sem.h
@@ -86,6 +86,7 @@ struct task_struct;
 struct sem {
 	int	semval;		/* current value */
 	int	sempid;		/* pid of last operation */
+	struct list_head sem_pending; /* pending single-sop operations */
 };
 
 /* One sem_array data structure for each set of semaphores in the system. */
@@ -96,11 +97,13 @@ struct sem_array {
 	struct sem		*sem_base;	/* ptr to first semaphore in array */
 	struct list_head	sem_pending;	/* pending operations to be processed */
 	struct list_head	list_id;	/* undo requests on this array */
-	unsigned long		sem_nsems;	/* no. of semaphores in array */
+	int			sem_nsems;	/* no. of semaphores in array */
+	int			complex_count;	/* pending complex 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 */
diff --git a/ipc/sem.c b/ipc/sem.c
index 9425817..0cad759 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -240,6 +240,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
 	key_t key = params->key;
 	int nsems = params->u.nsems;
 	int semflg = params->flg;
+	int i;
 
 	if (!nsems)
 		return -EINVAL;
@@ -272,6 +273,11 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
 	ns->used_sems += nsems;
 
 	sma->sem_base = (struct sem *) &sma[1];
+
+	for (i = 0; i < nsems; i++)
+		INIT_LIST_HEAD(&sma->sem_base[i].sem_pending);
+
+	sma->complex_count = 0;
 	INIT_LIST_HEAD(&sma->sem_pending);
 	INIT_LIST_HEAD(&sma->list_id);
 	sma->sem_nsems = nsems;
@@ -418,6 +424,15 @@ static void wake_up_sem_queue(struct sem_queue *q, int error)
 	preempt_enable();
 }
 
+static void unlink_queue(struct sem_array *sma, struct sem_queue *q)
+{
+	list_del(&q->list);
+	if (q->nsops == 1)
+		list_del(&q->simple_list);
+	else
+		sma->complex_count--;
+}
+
 /* Go through the pending queue for the indicated semaphore
  * looking for tasks that can be completed.
  */
@@ -437,7 +452,7 @@ again:
 		if (error > 0)
 			continue;
 
-		list_del(&q->list);
+		unlink_queue(sma, q);
 
 		/*
 		 * The next operation that must be checked depends on the type
@@ -531,8 +546,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
 
 	/* Wake up all pending processes and let them fail with EIDRM. */
 	list_for_each_entry_safe(q, tq, &sma->sem_pending, list) {
-		list_del(&q->list);
-
+		unlink_queue(sma, q);
 		wake_up_sem_queue(q, -EIDRM);
 	}
 
@@ -1190,6 +1204,19 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
 	else
 		list_add(&queue.list, &sma->sem_pending);
 
+	if (nsops == 1) {
+		struct sem *curr;
+		curr = &sma->sem_base[sops->sem_num];
+
+		if (alter)
+			list_add_tail(&queue.simple_list, &curr->sem_pending);
+		else
+			list_add(&queue.simple_list, &curr->sem_pending);
+	} else {
+		INIT_LIST_HEAD(&queue.simple_list);
+		sma->complex_count++;
+	}
+
 	queue.status = -EINTR;
 	queue.sleeper = current;
 	current->state = TASK_INTERRUPTIBLE;
@@ -1231,7 +1258,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
 	 */
 	if (timeout && jiffies_left == 0)
 		error = -EAGAIN;
-	list_del(&queue.list);
+	unlink_queue(sma, &queue);
 
 out_unlock_free:
 	sem_unlock(sma);
@@ -1374,7 +1401,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
 	struct sem_array *sma = it;
 
 	return seq_printf(s,
-			  "%10d %10d  %4o %10lu %5u %5u %5u %5u %10lu %10lu\n",
+			  "%10d %10d  %4o %10u %5u %5u %5u %5u %10lu %10lu\n",
 			  sma->sem_perm.key,
 			  sma->sem_perm.id,
 			  sma->sem_perm.mode,
-- 
1.6.2.5


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 6/7] ipc/sem.c: optimize single semop operations
  2009-10-03 17:19   ` [PATCH 5/7] ipc/sem.c: add a per-semaphore pending list manfred
@ 2009-10-03 17:19     ` manfred
  2009-10-03 17:19       ` [PATCH 7/7] ipc/sem.c: optimize single sops when semval is zero manfred
  0 siblings, 1 reply; 5+ messages in thread
From: manfred @ 2009-10-03 17:19 UTC (permalink / raw)
  To: linux-kernel, linux-kernel; +Cc: Manfred Spraul

From: Manfred Spraul <manfred@colorfullife.com>

sysv sem has the concept of semaphore arrays that consist out of multiple
semaphores. Atomic operations that affect multiple semaphores are supported.

The patch optimizes single semaphore operation calls that affect only one
semaphore: It's not necessary to scan all pending operations, it is sufficient
to scan the per-semaphore list.

The idea is from Nick Piggin version of an ipc sem improvement, the
implementation is different:
The code tries to keep as much common code as possible.

As the result, the patch is simpler, but optimizes fewer cases.

Signed-off-by: Manfred Spraul <manfred@colorfullife.com>
---
 ipc/sem.c |   51 ++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 40 insertions(+), 11 deletions(-)

diff --git a/ipc/sem.c b/ipc/sem.c
index 0cad759..66192c2 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -433,17 +433,45 @@ static void unlink_queue(struct sem_array *sma, struct sem_queue *q)
 		sma->complex_count--;
 }
 
-/* Go through the pending queue for the indicated semaphore
- * looking for tasks that can be completed.
+
+/**
+ * update_queue(sma, semnum): Look for tasks that can be completed.
+ * @sma: semaphore array.
+ * @semnum: semaphore that was modified.
+ *
+ * update_queue must be called after a semaphore in a semaphore array
+ * was modified. If multiple semaphore were modified, then @semnum
+ * must be set to -1.
  */
-static void update_queue (struct sem_array * sma)
+static void update_queue(struct sem_array *sma, int semnum)
 {
-	struct sem_queue *q, *tq;
+	struct sem_queue *q;
+	struct list_head *walk;
+	struct list_head *pending_list;
+	int offset;
+
+	/* if there are complex operations around, then knowing the semaphore
+	 * that was modified doesn't help us. Assume that multiple semaphores
+	 * were modified.
+	 */
+	if (sma->complex_count)
+		semnum = -1;
+
+	if (semnum == -1) {
+		pending_list = &sma->sem_pending;
+		offset = offsetof(struct sem_queue, list);
+	} else {
+		pending_list = &sma->sem_base[semnum].sem_pending;
+		offset = offsetof(struct sem_queue, simple_list);
+	}
 
 again:
-	list_for_each_entry_safe(q, tq, &sma->sem_pending, list) {
-		int error;
-		int alter;
+	walk = pending_list->next;
+	while (walk != pending_list) {
+		int error, alter;
+
+		q = (struct sem_queue *)((char *)walk - offset);
+		walk = walk->next;
 
 		error = try_atomic_semop(sma, q->sops, q->nsops,
 					 q->undo, q->pid);
@@ -768,7 +796,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
 		}
 		sma->sem_ctime = get_seconds();
 		/* maybe some queued-up processes were waiting for this */
-		update_queue(sma);
+		update_queue(sma, -1);
 		err = 0;
 		goto out_unlock;
 	}
@@ -810,7 +838,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
 		curr->sempid = task_tgid_vnr(current);
 		sma->sem_ctime = get_seconds();
 		/* maybe some queued-up processes were waiting for this */
-		update_queue(sma);
+		update_queue(sma, semnum);
 		err = 0;
 		goto out_unlock;
 	}
@@ -1186,7 +1214,8 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
 	error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current));
 	if (error <= 0) {
 		if (alter && error == 0)
-			update_queue (sma);
+			update_queue(sma, (nsops == 1) ? sops[0].sem_num : -1);
+
 		goto out_unlock_free;
 	}
 
@@ -1387,7 +1416,7 @@ void exit_sem(struct task_struct *tsk)
 		}
 		sma->sem_otime = get_seconds();
 		/* maybe some queued-up processes were waiting for this */
-		update_queue(sma);
+		update_queue(sma, -1);
 		sem_unlock(sma);
 
 		call_rcu(&un->rcu, free_un);
-- 
1.6.2.5


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 7/7] ipc/sem.c: optimize single sops when semval is zero
  2009-10-03 17:19     ` [PATCH 6/7] ipc/sem.c: optimize single semop operations manfred
@ 2009-10-03 17:19       ` manfred
  0 siblings, 0 replies; 5+ messages in thread
From: manfred @ 2009-10-03 17:19 UTC (permalink / raw)
  To: linux-kernel, linux-kernel; +Cc: Manfred Spraul

From: Manfred Spraul <manfred@colorfullife.com>

If multiple simple decrements on the same semaphore are pending, then the
current code scans all decrement operations, even if the semaphore value is
already 0.
The patch optimizes that: if the semaphore value is 0, then there is no need
to scan the q->alter entries.
Note that this is a common case: It happens if 100 decrements by one are
pending and now an increment by one increases the semaphore value from 0 to 1.
Without this patch, all 100 entries are scanned. With the patch, only
one entry is scanned, then woken up. Then the new rule triggers and the
scanning is aborted, without looking at the remaining 99 tasks.

With this patch, single sop increment/decrement by 1 are now O(1).
(same as with Nick's patch)

Signed-off-by: Manfred Spraul <manfred@colorfullife.com>
---
 ipc/sem.c |   11 +++++++++++
 1 files changed, 11 insertions(+), 0 deletions(-)

diff --git a/ipc/sem.c b/ipc/sem.c
index 66192c2..aab39fb 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -473,6 +473,17 @@ again:
 		q = (struct sem_queue *)((char *)walk - offset);
 		walk = walk->next;
 
+		/* If we are scanning the single sop, per-semaphore list of
+		 * one semaphore and that semaphore is 0, then it is not
+		 * necessary to scan the "alter" entries: simple increments
+		 * that affect only one entry succeed immediately and cannot
+		 * 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 &&
+				q->alter)
+			break;
+
 		error = try_atomic_semop(sma, q->sops, q->nsops,
 					 q->undo, q->pid);
 
-- 
1.6.2.5


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2009-10-03 17:20 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-10-03 17:19 [PATCH 0/7] ipc/sem.c: Performance updates manfred
2009-10-03 17:19 ` [PATCH 4/7] ipc/sem.c: optimize if semops fail manfred
2009-10-03 17:19   ` [PATCH 5/7] ipc/sem.c: add a per-semaphore pending list manfred
2009-10-03 17:19     ` [PATCH 6/7] ipc/sem.c: optimize single semop operations manfred
2009-10-03 17:19       ` [PATCH 7/7] ipc/sem.c: optimize single sops when semval is zero manfred

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®