aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc
diff options
context:
space:
mode:
Diffstat (limited to 'fs/proc')
-rw-r--r--fs/proc/base.c104
1 files changed, 35 insertions, 69 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 89c20d9d50bf..b18f3773dd43 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 */
2156static struct task_struct *first_tgid(int tgid, unsigned int nr) 2148static 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 }
2180found:
2181 get_task_struct(pos);
2182done:
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 */
2193static 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; 2154retry:
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;
2205done:
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 */
2212int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) 2183int 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;
2214out:
2249 return 0; 2215 return 0;
2250} 2216}
2251 2217