[RFC][PATCH] ps command race fix take 4 [3/4] callback subroutine

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



For remembering 'what process should be read in the next time',
uses adaptive_pointer and records pointer to the next task.

While task is being stale, adaptive_pointer is invalidated and
registered callback ,'proc_pid_readdir_callback' is invoked.

proc_pid_readdir_callback moves pointer to the next (alive) task.

Signed-Off-By: KAMEZAWA Hiroyuki <[email protected]>


 fs/proc/base.c |  127 +++++++++++++++++++++++++++++++--------------------------
 1 files changed, 70 insertions(+), 57 deletions(-)

Index: linux-2.6.18-rc4/fs/proc/base.c
===================================================================
--- linux-2.6.18-rc4.orig/fs/proc/base.c
+++ linux-2.6.18-rc4/fs/proc/base.c
@@ -71,6 +71,8 @@
 #include <linux/cpuset.h>
 #include <linux/audit.h>
 #include <linux/poll.h>
+#include <linux/adaptive_pointer.h>
+
 #include "internal.h"
 
 /* NOTE:
@@ -2140,70 +2142,68 @@ out_no_task:
 	return result;
 }
 
-/*
- * Find the first tgid to return to user space.
- *
- * Usually this is just whatever follows &init_task, but if the users
- * buffer was too small to hold the full list or there was a seek into
- * the middle of the directory we have more work to do.
- *
- * In the case of a short read we start with find_task_by_pid.
- *
- * In the case of a seek we start with &init_task and walk nr
- * threads past it.
- */
-static struct task_struct *first_tgid(int tgid, unsigned int nr)
+/* caller must take rcu_read_lock() */
+static struct task_struct *first_alive_process(struct task_struct *pos)
 {
-	struct task_struct *pos;
-	rcu_read_lock();
-	if (tgid && nr) {
-		pos = find_task_by_pid(tgid);
-		if (pos && thread_group_leader(pos))
-			goto found;
-	}
-	/* If nr exceeds the number of processes get out quickly */
-	pos = NULL;
-	if (nr && nr >= nr_processes())
-		goto done;
-
-	/* If we haven't found our starting place yet start with
-	 * the init_task and walk nr tasks forward.
-	 */
-	for (pos = next_task(&init_task); nr > 0; --nr) {
+	while (!pid_alive(pos)) {
+		if (pos == &init_task)
+			break;
 		pos = next_task(pos);
-		if (pos == &init_task) {
-			pos = NULL;
-			goto done;
-		}
 	}
-found:
-	get_task_struct(pos);
-done:
+	if (pos == &init_task)
+		return NULL;
+	return pos;
+}
+
+static struct task_struct *next_tgid(struct task_struct *task)
+{
+	struct task_struct *pos;
+	rcu_read_lock();
+	pos = next_task(task);
+	pos = first_alive_process(pos);
+	put_task_struct(task);
+	if (pos)
+		get_task_struct(pos);
 	rcu_read_unlock();
 	return pos;
 }
 
 /*
- * Find the next task in the task list.
- * Return NULL if we loop or there is any error.
- *
- * The reference to the input task_struct is released.
+ * the task next to be read is removed.
+ * then, adjust pointer to next task.
+ * this callback is called under write_lock(tasklist_lock);
+ * next_task() is alive and all ops are safe.
  */
-static struct task_struct *next_tgid(struct task_struct *start)
+void proc_pid_readdir_callback(
+			struct adaptive_pointer *ap,
+			struct adaptive_pointer *obj)
 {
-	struct task_struct *pos;
+	struct task_struct *task;
+	task = container_of(obj, struct task_struct, ap_target);
+	task = next_task(task);
+	ap_attach(ap, &task->ap_target);
+}
+
+/*
+ * return the first process pointer by head.
+ * if head points no where, returns next_alive_task(init_task)
+ * if head points to init_task, returns NULL.
+ */
+static struct task_struct *first_tgid(struct adaptive_pointer *head)
+{
+	struct task_struct *pos = NULL;
 	rcu_read_lock();
-	pos = start;
-	if (pid_alive(start))
-		pos = next_task(start);
-	if (pid_alive(pos) && (pos != &init_task)) {
-		get_task_struct(pos);
-		goto done;
+	pos = ap_get_remove_pointer(head,struct task_struct,ap_target);
+	if (!pos) {
+		pos = first_alive_process(next_task(&init_task));
+	} else if (pos == &init_task) {
+		pos = NULL;
+	} else {
+		pos = first_alive_process(pos);
 	}
-	pos = NULL;
-done:
+	if (pos)
+		get_task_struct(pos);
 	rcu_read_unlock();
-	put_task_struct(start);
 	return pos;
 }
 
@@ -2213,6 +2213,7 @@ int proc_pid_readdir(struct file * filp,
 	char buf[PROC_NUMBUF];
 	unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY;
 	struct task_struct *task;
+	struct adaptive_pointer *head;
 	int tgid;
 
 	if (!nr) {
@@ -2227,9 +2228,11 @@ int proc_pid_readdir(struct file * filp,
 	/* f_version caches the tgid value that the last readdir call couldn't
 	 * return. lseek aka telldir automagically resets f_version to 0.
 	 */
-	tgid = filp->f_version;
-	filp->f_version = 0;
-	for (task = first_tgid(tgid, nr);
+	head = filp->private_data;
+	/* if we reached to the end of task_list once, head is invalidated */
+	if (is_ap_invalid(head))
+		return 0;
+	for (task = first_tgid(head);
 	     task;
 	     task = next_tgid(task), filp->f_pos++) {
 		int len;
@@ -2238,13 +2241,26 @@ int proc_pid_readdir(struct file * filp,
 		len = snprintf(buf, sizeof(buf), "%d", tgid);
 		ino = fake_ino(tgid, PROC_TGID_INO);
 		if (filldir(dirent, buf, len, filp->f_pos, ino, DT_DIR) < 0) {
-			/* returning this tgid failed, save it as the first
-			 * pid for the next readir call */
-			filp->f_version = tgid;
+			/* this task is failed to be returned. remember this */
+			struct task_struct *pos = task;
+			head->callback = proc_pid_readdir_callback;
+			rcu_read_lock();
+			do {
+				pos = first_alive_process(pos);
+				if (pos && ap_attach(head, &pos->ap_target))
+					break;
+				if (pos)
+					pos = next_task(pos);
+			} while (pos && pos != &init_task);
+			rcu_read_unlock();
 			put_task_struct(task);
+			task = pos;
 			break;
 		}
 	}
+	/* reached to the end of task_list */
+	if (!task)
+		invalidate_ap(head);
 	return 0;
 }
 

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux