diff options
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/base.c | 104 |
1 files changed, 35 insertions, 69 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 89c20d9d50b..b18f3773dd4 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -2142,72 +2142,43 @@ out_no_task: | |||
2142 | } | 2142 | } |
2143 | 2143 | ||
2144 | /* | 2144 | /* |
2145 | * Find the first tgid to return to user space. | 2145 | * Find the first task with tgid >= tgid |
2146 | * | 2146 | * |
2147 | * Usually this is just whatever follows &init_task, but if the users | ||
2148 | * buffer was too small to hold the full list or there was a seek into | ||
2149 | * the middle of the directory we have more work to do. | ||
2150 | * | ||
2151 | * In the case of a short read we start with find_task_by_pid. | ||
2152 | * | ||
2153 | * In the case of a seek we start with &init_task and walk nr | ||
2154 | * threads past it. | ||
2155 | */ | 2147 | */ |
2156 | static struct task_struct *first_tgid(int tgid, unsigned int nr) | 2148 | static struct task_struct *next_tgid(unsigned int tgid) |
2157 | { | 2149 | { |
2158 | struct task_struct *pos; | 2150 | struct task_struct *task; |
2159 | rcu_read_lock(); | 2151 | struct pid *pid; |
2160 | if (tgid && nr) { | ||
2161 | pos = find_task_by_pid(tgid); | ||
2162 | if (pos && thread_group_leader(pos)) | ||
2163 | goto found; | ||
2164 | } | ||
2165 | /* If nr exceeds the number of processes get out quickly */ | ||
2166 | pos = NULL; | ||
2167 | if (nr && nr >= nr_processes()) | ||
2168 | goto done; | ||
2169 | |||
2170 | /* If we haven't found our starting place yet start with | ||
2171 | * the init_task and walk nr tasks forward. | ||
2172 | */ | ||
2173 | for (pos = next_task(&init_task); nr > 0; --nr) { | ||
2174 | pos = next_task(pos); | ||
2175 | if (pos == &init_task) { | ||
2176 | pos = NULL; | ||
2177 | goto done; | ||
2178 | } | ||
2179 | } | ||
2180 | found: | ||
2181 | get_task_struct(pos); | ||
2182 | done: | ||
2183 | rcu_read_unlock(); | ||
2184 | return pos; | ||
2185 | } | ||
2186 | 2152 | ||
2187 | /* | ||
2188 | * Find the next task in the task list. | ||
2189 | * Return NULL if we loop or there is any error. | ||
2190 | * | ||
2191 | * The reference to the input task_struct is released. | ||
2192 | */ | ||
2193 | static struct task_struct *next_tgid(struct task_struct *start) | ||
2194 | { | ||
2195 | struct task_struct *pos; | ||
2196 | rcu_read_lock(); | 2153 | rcu_read_lock(); |
2197 | pos = start; | 2154 | retry: |
2198 | if (pid_alive(start)) | 2155 | task = NULL; |
2199 | pos = next_task(start); | 2156 | pid = find_ge_pid(tgid); |
2200 | if (pid_alive(pos) && (pos != &init_task)) { | 2157 | if (pid) { |
2201 | get_task_struct(pos); | 2158 | tgid = pid->nr + 1; |
2202 | goto done; | 2159 | task = pid_task(pid, PIDTYPE_PID); |
2160 | /* What we to know is if the pid we have find is the | ||
2161 | * pid of a thread_group_leader. Testing for task | ||
2162 | * being a thread_group_leader is the obvious thing | ||
2163 | * todo but there is a window when it fails, due to | ||
2164 | * the pid transfer logic in de_thread. | ||
2165 | * | ||
2166 | * So we perform the straight forward test of seeing | ||
2167 | * if the pid we have found is the pid of a thread | ||
2168 | * group leader, and don't worry if the task we have | ||
2169 | * found doesn't happen to be a thread group leader. | ||
2170 | * As we don't care in the case of readdir. | ||
2171 | */ | ||
2172 | if (!task || !has_group_leader_pid(task)) | ||
2173 | goto retry; | ||
2174 | get_task_struct(task); | ||
2203 | } | 2175 | } |
2204 | pos = NULL; | ||
2205 | done: | ||
2206 | rcu_read_unlock(); | 2176 | rcu_read_unlock(); |
2207 | put_task_struct(start); | 2177 | return task; |
2208 | return pos; | ||
2209 | } | 2178 | } |
2210 | 2179 | ||
2180 | #define TGID_OFFSET (FIRST_PROCESS_ENTRY + (1 /* /proc/self */)) | ||
2181 | |||
2211 | /* for the /proc/ directory itself, after non-process stuff has been done */ | 2182 | /* for the /proc/ directory itself, after non-process stuff has been done */ |
2212 | int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) | 2183 | int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) |
2213 | { | 2184 | { |
@@ -2223,29 +2194,24 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
2223 | filp->f_pos++; | 2194 | filp->f_pos++; |
2224 | nr++; | 2195 | nr++; |
2225 | } | 2196 | } |
2226 | nr -= 1; | ||
2227 | 2197 | ||
2228 | /* f_version caches the tgid value that the last readdir call couldn't | 2198 | tgid = filp->f_pos - TGID_OFFSET; |
2229 | * return. lseek aka telldir automagically resets f_version to 0. | 2199 | for (task = next_tgid(tgid); |
2230 | */ | ||
2231 | tgid = filp->f_version; | ||
2232 | filp->f_version = 0; | ||
2233 | for (task = first_tgid(tgid, nr); | ||
2234 | task; | 2200 | task; |
2235 | task = next_tgid(task), filp->f_pos++) { | 2201 | put_task_struct(task), task = next_tgid(tgid + 1)) { |
2236 | int len; | 2202 | int len; |
2237 | ino_t ino; | 2203 | ino_t ino; |
2238 | tgid = task->pid; | 2204 | tgid = task->pid; |
2205 | filp->f_pos = tgid + TGID_OFFSET; | ||
2239 | len = snprintf(buf, sizeof(buf), "%d", tgid); | 2206 | len = snprintf(buf, sizeof(buf), "%d", tgid); |
2240 | ino = fake_ino(tgid, PROC_TGID_INO); | 2207 | ino = fake_ino(tgid, PROC_TGID_INO); |
2241 | if (filldir(dirent, buf, len, filp->f_pos, ino, DT_DIR) < 0) { | 2208 | if (filldir(dirent, buf, len, filp->f_pos, ino, DT_DIR) < 0) { |
2242 | /* returning this tgid failed, save it as the first | ||
2243 | * pid for the next readir call */ | ||
2244 | filp->f_version = tgid; | ||
2245 | put_task_struct(task); | 2209 | put_task_struct(task); |
2246 | break; | 2210 | goto out; |
2247 | } | 2211 | } |
2248 | } | 2212 | } |
2213 | filp->f_pos = PID_MAX_LIMIT + TGID_OFFSET; | ||
2214 | out: | ||
2249 | return 0; | 2215 | return 0; |
2250 | } | 2216 | } |
2251 | 2217 | ||