aboutsummaryrefslogtreecommitdiffstats
path: root/net/unix/garbage.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/unix/garbage.c')
-rw-r--r--net/unix/garbage.c62
1 files changed, 47 insertions, 15 deletions
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index 2a27b84f740..abb3ab34cb1 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -80,6 +80,7 @@
80#include <linux/file.h> 80#include <linux/file.h>
81#include <linux/proc_fs.h> 81#include <linux/proc_fs.h>
82#include <linux/mutex.h> 82#include <linux/mutex.h>
83#include <linux/wait.h>
83 84
84#include <net/sock.h> 85#include <net/sock.h>
85#include <net/af_unix.h> 86#include <net/af_unix.h>
@@ -91,6 +92,7 @@
91static LIST_HEAD(gc_inflight_list); 92static LIST_HEAD(gc_inflight_list);
92static LIST_HEAD(gc_candidates); 93static LIST_HEAD(gc_candidates);
93static DEFINE_SPINLOCK(unix_gc_lock); 94static DEFINE_SPINLOCK(unix_gc_lock);
95static DECLARE_WAIT_QUEUE_HEAD(unix_gc_wait);
94 96
95unsigned int unix_tot_inflight; 97unsigned int unix_tot_inflight;
96 98
@@ -186,8 +188,17 @@ static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *),
186 */ 188 */
187 struct sock *sk = unix_get_socket(*fp++); 189 struct sock *sk = unix_get_socket(*fp++);
188 if (sk) { 190 if (sk) {
189 hit = true; 191 struct unix_sock *u = unix_sk(sk);
190 func(unix_sk(sk)); 192
193 /*
194 * Ignore non-candidates, they could
195 * have been added to the queues after
196 * starting the garbage collection
197 */
198 if (u->gc_candidate) {
199 hit = true;
200 func(u);
201 }
191 } 202 }
192 } 203 }
193 if (hit && hitlist != NULL) { 204 if (hit && hitlist != NULL) {
@@ -249,24 +260,29 @@ static void inc_inflight_move_tail(struct unix_sock *u)
249{ 260{
250 atomic_long_inc(&u->inflight); 261 atomic_long_inc(&u->inflight);
251 /* 262 /*
252 * If this is still a candidate, move it to the end of the 263 * If this still might be part of a cycle, move it to the end
253 * list, so that it's checked even if it was already passed 264 * of the list, so that it's checked even if it was already
254 * over 265 * passed over
255 */ 266 */
256 if (u->gc_candidate) 267 if (u->gc_maybe_cycle)
257 list_move_tail(&u->link, &gc_candidates); 268 list_move_tail(&u->link, &gc_candidates);
258} 269}
259 270
260/* The external entry point: unix_gc() */ 271static bool gc_in_progress = false;
261 272
262void unix_gc(void) 273void wait_for_unix_gc(void)
263{ 274{
264 static bool gc_in_progress = false; 275 wait_event(unix_gc_wait, gc_in_progress == false);
276}
265 277
278/* The external entry point: unix_gc() */
279void unix_gc(void)
280{
266 struct unix_sock *u; 281 struct unix_sock *u;
267 struct unix_sock *next; 282 struct unix_sock *next;
268 struct sk_buff_head hitlist; 283 struct sk_buff_head hitlist;
269 struct list_head cursor; 284 struct list_head cursor;
285 LIST_HEAD(not_cycle_list);
270 286
271 spin_lock(&unix_gc_lock); 287 spin_lock(&unix_gc_lock);
272 288
@@ -282,10 +298,14 @@ void unix_gc(void)
282 * 298 *
283 * Holding unix_gc_lock will protect these candidates from 299 * Holding unix_gc_lock will protect these candidates from
284 * being detached, and hence from gaining an external 300 * being detached, and hence from gaining an external
285 * reference. This also means, that since there are no 301 * reference. Since there are no possible receivers, all
286 * possible receivers, the receive queues of these sockets are 302 * buffers currently on the candidates' queues stay there
287 * static during the GC, even though the dequeue is done 303 * during the garbage collection.
288 * before the detach without atomicity guarantees. 304 *
305 * We also know that no new candidate can be added onto the
306 * receive queues. Other, non candidate sockets _can_ be
307 * added to queue, so we must make sure only to touch
308 * candidates.
289 */ 309 */
290 list_for_each_entry_safe(u, next, &gc_inflight_list, link) { 310 list_for_each_entry_safe(u, next, &gc_inflight_list, link) {
291 long total_refs; 311 long total_refs;
@@ -299,6 +319,7 @@ void unix_gc(void)
299 if (total_refs == inflight_refs) { 319 if (total_refs == inflight_refs) {
300 list_move_tail(&u->link, &gc_candidates); 320 list_move_tail(&u->link, &gc_candidates);
301 u->gc_candidate = 1; 321 u->gc_candidate = 1;
322 u->gc_maybe_cycle = 1;
302 } 323 }
303 } 324 }
304 325
@@ -325,14 +346,24 @@ void unix_gc(void)
325 list_move(&cursor, &u->link); 346 list_move(&cursor, &u->link);
326 347
327 if (atomic_long_read(&u->inflight) > 0) { 348 if (atomic_long_read(&u->inflight) > 0) {
328 list_move_tail(&u->link, &gc_inflight_list); 349 list_move_tail(&u->link, &not_cycle_list);
329 u->gc_candidate = 0; 350 u->gc_maybe_cycle = 0;
330 scan_children(&u->sk, inc_inflight_move_tail, NULL); 351 scan_children(&u->sk, inc_inflight_move_tail, NULL);
331 } 352 }
332 } 353 }
333 list_del(&cursor); 354 list_del(&cursor);
334 355
335 /* 356 /*
357 * not_cycle_list contains those sockets which do not make up a
358 * cycle. Restore these to the inflight list.
359 */
360 while (!list_empty(&not_cycle_list)) {
361 u = list_entry(not_cycle_list.next, struct unix_sock, link);
362 u->gc_candidate = 0;
363 list_move_tail(&u->link, &gc_inflight_list);
364 }
365
366 /*
336 * Now gc_candidates contains only garbage. Restore original 367 * Now gc_candidates contains only garbage. Restore original
337 * inflight counters for these as well, and remove the skbuffs 368 * inflight counters for these as well, and remove the skbuffs
338 * which are creating the cycle(s). 369 * which are creating the cycle(s).
@@ -351,6 +382,7 @@ void unix_gc(void)
351 /* All candidates should have been detached by now. */ 382 /* All candidates should have been detached by now. */
352 BUG_ON(!list_empty(&gc_candidates)); 383 BUG_ON(!list_empty(&gc_candidates));
353 gc_in_progress = false; 384 gc_in_progress = false;
385 wake_up(&unix_gc_wait);
354 386
355 out: 387 out:
356 spin_unlock(&unix_gc_lock); 388 spin_unlock(&unix_gc_lock);