aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2006-03-31 05:31:42 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-31 15:19:00 -0500
commit92476d7fc0326a409ab1d3864a04093a6be9aca7 (patch)
treeea50a5a31522492d9915e0763a7adc6ac87c4fbc /include/linux
parent8c7904a00b06d2ee51149794b619e07369fcf9d4 (diff)
[PATCH] pidhash: Refactor the pid hash table
Simplifies the code, reduces the need for 4 pid hash tables, and makes the code more capable. In the discussions I had with Oleg it was felt that to a large extent the cleanup itself justified the work. With struct pid being dynamically allocated meant we could create the hash table entry when the pid was allocated and free the hash table entry when the pid was freed. Instead of playing with the hash lists when ever a process would attach or detach to a process. For myself the fact that it gave what my previous task_ref patch gave for free with simpler code was a big win. The problem is that if you hold a reference to struct task_struct you lock in 10K of low memory. If you do that in a user controllable way like /proc does, with an unprivileged but hostile user space application with typical resource limits of 1000 fds and 100 processes I can trigger the OOM killer by consuming all of low memory with task structs, on a machine wight 1GB of low memory. If I instead hold a reference to struct pid which holds a pointer to my task_struct, I don't suffer from that problem because struct pid is 2 orders of magnitude smaller. In fact struct pid is small enough that most other kernel data structures dwarf it, so simply limiting the number of referring data structures is enough to prevent exhaustion of low memory. This splits the current struct pid into two structures, struct pid and struct pid_link, and reduces our number of hash tables from PIDTYPE_MAX to just one. struct pid_link is the per process linkage into the hash tables and lives in struct task_struct. struct pid is given an indepedent lifetime, and holds pointers to each of the pid types. The independent life of struct pid simplifies attach_pid, and detach_pid, because we are always manipulating the list of pids and not the hash table. In addition in giving struct pid an indpendent life it makes the concept much more powerful. Kernel data structures can now embed a struct pid * instead of a pid_t and not suffer from pid wrap around problems or from keeping unnecessarily large amounts of memory allocated. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/pid.h96
-rw-r--r--include/linux/sched.h4
2 files changed, 83 insertions, 17 deletions
diff --git a/include/linux/pid.h b/include/linux/pid.h
index 5b9082cc600f..29960b03bef7 100644
--- a/include/linux/pid.h
+++ b/include/linux/pid.h
@@ -1,6 +1,8 @@
1#ifndef _LINUX_PID_H 1#ifndef _LINUX_PID_H
2#define _LINUX_PID_H 2#define _LINUX_PID_H
3 3
4#include <linux/rcupdate.h>
5
4enum pid_type 6enum pid_type
5{ 7{
6 PIDTYPE_PID, 8 PIDTYPE_PID,
@@ -9,45 +11,109 @@ enum pid_type
9 PIDTYPE_MAX 11 PIDTYPE_MAX
10}; 12};
11 13
14/*
15 * What is struct pid?
16 *
17 * A struct pid is the kernel's internal notion of a process identifier.
18 * It refers to individual tasks, process groups, and sessions. While
19 * there are processes attached to it the struct pid lives in a hash
20 * table, so it and then the processes that it refers to can be found
21 * quickly from the numeric pid value. The attached processes may be
22 * quickly accessed by following pointers from struct pid.
23 *
24 * Storing pid_t values in the kernel and refering to them later has a
25 * problem. The process originally with that pid may have exited and the
26 * pid allocator wrapped, and another process could have come along
27 * and been assigned that pid.
28 *
29 * Referring to user space processes by holding a reference to struct
30 * task_struct has a problem. When the user space process exits
31 * the now useless task_struct is still kept. A task_struct plus a
32 * stack consumes around 10K of low kernel memory. More precisely
33 * this is THREAD_SIZE + sizeof(struct task_struct). By comparison
34 * a struct pid is about 64 bytes.
35 *
36 * Holding a reference to struct pid solves both of these problems.
37 * It is small so holding a reference does not consume a lot of
38 * resources, and since a new struct pid is allocated when the numeric
39 * pid value is reused we don't mistakenly refer to new processes.
40 */
41
12struct pid 42struct pid
13{ 43{
44 atomic_t count;
14 /* Try to keep pid_chain in the same cacheline as nr for find_pid */ 45 /* Try to keep pid_chain in the same cacheline as nr for find_pid */
15 int nr; 46 int nr;
16 struct hlist_node pid_chain; 47 struct hlist_node pid_chain;
17 /* list of pids with the same nr, only one of them is in the hash */ 48 /* lists of tasks that use this pid */
18 struct list_head pid_list; 49 struct hlist_head tasks[PIDTYPE_MAX];
50 struct rcu_head rcu;
19}; 51};
20 52
21#define pid_task(elem, type) \ 53struct pid_link
22 list_entry(elem, struct task_struct, pids[type].pid_list) 54{
55 struct hlist_node node;
56 struct pid *pid;
57};
58
59static inline struct pid *get_pid(struct pid *pid)
60{
61 if (pid)
62 atomic_inc(&pid->count);
63 return pid;
64}
65
66extern void FASTCALL(put_pid(struct pid *pid));
67extern struct task_struct *FASTCALL(pid_task(struct pid *pid, enum pid_type));
68extern struct task_struct *FASTCALL(get_pid_task(struct pid *pid,
69 enum pid_type));
23 70
24/* 71/*
25 * attach_pid() and detach_pid() must be called with the tasklist_lock 72 * attach_pid() and detach_pid() must be called with the tasklist_lock
26 * write-held. 73 * write-held.
27 */ 74 */
28extern int FASTCALL(attach_pid(struct task_struct *task, enum pid_type type, int nr)); 75extern int FASTCALL(attach_pid(struct task_struct *task,
76 enum pid_type type, int nr));
29 77
30extern void FASTCALL(detach_pid(struct task_struct *task, enum pid_type)); 78extern void FASTCALL(detach_pid(struct task_struct *task, enum pid_type));
31 79
32/* 80/*
33 * look up a PID in the hash table. Must be called with the tasklist_lock 81 * look up a PID in the hash table. Must be called with the tasklist_lock
34 * held. 82 * or rcu_read_lock() held.
83 */
84extern struct pid *FASTCALL(find_pid(int nr));
85
86/*
87 * Lookup a PID in the hash table, and return with it's count elevated.
35 */ 88 */
36extern struct pid *FASTCALL(find_pid(enum pid_type, int)); 89extern struct pid *find_get_pid(int nr);
37 90
38extern int alloc_pidmap(void); 91extern struct pid *alloc_pid(void);
39extern void FASTCALL(free_pidmap(int)); 92extern void FASTCALL(free_pid(struct pid *pid));
40 93
94#define pid_next(task, type) \
95 ((task)->pids[(type)].node.next)
96
97#define pid_next_task(task, type) \
98 hlist_entry(pid_next(task, type), struct task_struct, \
99 pids[(type)].node)
100
101
102/* We could use hlist_for_each_entry_rcu here but it takes more arguments
103 * than the do_each_task_pid/while_each_task_pid. So we roll our own
104 * to preserve the existing interface.
105 */
41#define do_each_task_pid(who, type, task) \ 106#define do_each_task_pid(who, type, task) \
42 if ((task = find_task_by_pid_type(type, who))) { \ 107 if ((task = find_task_by_pid_type(type, who))) { \
43 prefetch((task)->pids[type].pid_list.next); \ 108 prefetch(pid_next(task, type)); \
44 do { 109 do {
45 110
46#define while_each_task_pid(who, type, task) \ 111#define while_each_task_pid(who, type, task) \
47 } while (task = pid_task((task)->pids[type].pid_list.next,\ 112 } while (pid_next(task, type) && ({ \
48 type), \ 113 task = pid_next_task(task, type); \
49 prefetch((task)->pids[type].pid_list.next), \ 114 rcu_dereference(task); \
50 hlist_unhashed(&(task)->pids[type].pid_chain)); \ 115 prefetch(pid_next(task, type)); \
51 } \ 116 1; }) ); \
117 }
52 118
53#endif /* _LINUX_PID_H */ 119#endif /* _LINUX_PID_H */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 7e0ff5dba986..541f4828f5e7 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -760,7 +760,7 @@ struct task_struct {
760 struct task_struct *group_leader; /* threadgroup leader */ 760 struct task_struct *group_leader; /* threadgroup leader */
761 761
762 /* PID/PID hash table linkage. */ 762 /* PID/PID hash table linkage. */
763 struct pid pids[PIDTYPE_MAX]; 763 struct pid_link pids[PIDTYPE_MAX];
764 struct list_head thread_group; 764 struct list_head thread_group;
765 765
766 struct completion *vfork_done; /* for vfork() */ 766 struct completion *vfork_done; /* for vfork() */
@@ -899,7 +899,7 @@ static inline pid_t process_group(struct task_struct *tsk)
899 */ 899 */
900static inline int pid_alive(struct task_struct *p) 900static inline int pid_alive(struct task_struct *p)
901{ 901{
902 return p->pids[PIDTYPE_PID].nr != 0; 902 return p->pids[PIDTYPE_PID].pid != NULL;
903} 903}
904 904
905extern void free_task(struct task_struct *tsk); 905extern void free_task(struct task_struct *tsk);