Blame view

net/unix/garbage.c 10.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  /*
   * NET3:	Garbage Collector For AF_UNIX sockets
   *
   * Garbage Collector:
   *	Copyright (C) Barak A. Pearlmutter.
   *	Released under the GPL version 2 or later.
   *
   * Chopped about by Alan Cox 22/3/96 to make it fit the AF_UNIX socket problem.
   * If it doesn't work blame me, it worked when Barak sent it.
   *
   * Assumptions:
   *
   *  - object w/ a bit
   *  - free list
   *
   * Current optimizations:
   *
   *  - explicit stack instead of recursion
   *  - tail recurse on first born instead of immediate push/pop
   *  - we gather the stuff that should not be killed into tree
   *    and stack is just a path from root to the current pointer.
   *
   *  Future optimizations:
   *
   *  - don't just push entire root set; process in place
   *
   *	This program is free software; you can redistribute it and/or
   *	modify it under the terms of the GNU General Public License
   *	as published by the Free Software Foundation; either version
   *	2 of the License, or (at your option) any later version.
   *
   *  Fixes:
   *	Alan Cox	07 Sept	1997	Vmalloc internal stack as needed.
   *					Cope with changing max_files.
   *	Al Viro		11 Oct 1998
   *		Graph may have cycles. That is, we can send the descriptor
   *		of foo to bar and vice versa. Current code chokes on that.
   *		Fix: move SCM_RIGHTS ones into the separate list and then
   *		skb_free() them all instead of doing explicit fput's.
   *		Another problem: since fput() may block somebody may
   *		create a new unix_socket when we are in the middle of sweep
   *		phase. Fix: revert the logic wrt MARKED. Mark everything
   *		upon the beginning and unmark non-junk ones.
   *
   *		[12 Oct 1998] AAARGH! New code purges all SCM_RIGHTS
   *		sent to connect()'ed but still not accept()'ed sockets.
   *		Fixed. Old code had slightly different problem here:
   *		extra fput() in situation when we passed the descriptor via
   *		such socket and closed it (descriptor). That would happen on
   *		each unix_gc() until the accept(). Since the struct file in
   *		question would go to the free list and might be reused...
   *		That might be the reason of random oopses on filp_close()
   *		in unrelated processes.
   *
   *	AV		28 Feb 1999
   *		Kill the explicit allocation of stack. Now we keep the tree
   *		with root in dummy + pointer (gc_current) to one of the nodes.
   *		Stack is represented as path from gc_current to dummy. Unmark
   *		now means "add to tree". Push == "make it a son of gc_current".
   *		Pop == "move gc_current to parent". We keep only pointers to
   *		parents (->gc_tree).
   *	AV		1 Mar 1999
   *		Damn. Added missing check for ->dead in listen queues scanning.
   *
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
65
66
67
68
   *	Miklos Szeredi 25 Jun 2007
   *		Reimplement with a cycle collecting algorithm. This should
   *		solve several problems with the previous code, like being racy
   *		wrt receive and holding up unrelated socket operations.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
   */
ac7bfa62f   YOSHIFUJI Hideaki   [NET] UNIX: Fix w...
70

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
  #include <linux/string.h>
  #include <linux/socket.h>
  #include <linux/un.h>
  #include <linux/net.h>
  #include <linux/fs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
  #include <linux/skbuff.h>
  #include <linux/netdevice.h>
  #include <linux/file.h>
  #include <linux/proc_fs.h>
4a3e2f711   Arjan van de Ven   [NET] sem2mutex: ...
81
  #include <linux/mutex.h>
5f23b7349   dann frazier   net: Fix soft loc...
82
  #include <linux/wait.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
86
  
  #include <net/sock.h>
  #include <net/af_unix.h>
  #include <net/scm.h>
c752f0739   Arnaldo Carvalho de Melo   [TCP]: Move the t...
87
  #include <net/tcp_states.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
  
  /* Internal data structures and random procedures: */
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
90
91
92
  static LIST_HEAD(gc_inflight_list);
  static LIST_HEAD(gc_candidates);
  static DEFINE_SPINLOCK(unix_gc_lock);
5f23b7349   dann frazier   net: Fix soft loc...
93
  static DECLARE_WAIT_QUEUE_HEAD(unix_gc_wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94

9305cfa44   Pavel Emelyanov   [AF_UNIX]: Make u...
95
  unsigned int unix_tot_inflight;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96

25888e303   Eric Dumazet   af_unix: limit re...
97
  struct sock *unix_get_socket(struct file *filp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
  {
  	struct sock *u_sock = NULL;
592ccbf9f   Josef Sipek   [PATCH] struct pa...
100
  	struct inode *inode = filp->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
  
  	/*
  	 *	Socket ?
  	 */
326be7b48   Al Viro   Allow passing O_P...
105
  	if (S_ISSOCK(inode->i_mode) && !(filp->f_mode & FMODE_PATH)) {
e27dfcea4   Jianjun Kong   af_unix: clean up...
106
107
  		struct socket *sock = SOCKET_I(inode);
  		struct sock *s = sock->sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  
  		/*
  		 *	PF_UNIX ?
  		 */
  		if (s && sock->ops && sock->ops->family == PF_UNIX)
  			u_sock = s;
  	}
  	return u_sock;
  }
  
  /*
   *	Keep the number of times in flight count for the file
   *	descriptor if it is for an AF_UNIX socket.
   */
ac7bfa62f   YOSHIFUJI Hideaki   [NET] UNIX: Fix w...
122

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
125
  void unix_inflight(struct file *fp)
  {
  	struct sock *s = unix_get_socket(fp);
e27dfcea4   Jianjun Kong   af_unix: clean up...
126
  	if (s) {
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
127
128
  		struct unix_sock *u = unix_sk(s);
  		spin_lock(&unix_gc_lock);
516e0cc56   Al Viro   [PATCH] f_count m...
129
  		if (atomic_long_inc_return(&u->inflight) == 1) {
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
130
131
132
133
134
  			BUG_ON(!list_empty(&u->link));
  			list_add_tail(&u->link, &gc_inflight_list);
  		} else {
  			BUG_ON(list_empty(&u->link));
  		}
9305cfa44   Pavel Emelyanov   [AF_UNIX]: Make u...
135
  		unix_tot_inflight++;
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
136
  		spin_unlock(&unix_gc_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
140
141
142
  	}
  }
  
  void unix_notinflight(struct file *fp)
  {
  	struct sock *s = unix_get_socket(fp);
e27dfcea4   Jianjun Kong   af_unix: clean up...
143
  	if (s) {
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
144
145
146
  		struct unix_sock *u = unix_sk(s);
  		spin_lock(&unix_gc_lock);
  		BUG_ON(list_empty(&u->link));
516e0cc56   Al Viro   [PATCH] f_count m...
147
  		if (atomic_long_dec_and_test(&u->inflight))
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
148
  			list_del_init(&u->link);
9305cfa44   Pavel Emelyanov   [AF_UNIX]: Make u...
149
  		unix_tot_inflight--;
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
150
  		spin_unlock(&unix_gc_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
  	}
  }
5c80f1ae9   Pavel Emelyanov   [AF_UNIX]: Conver...
153
  static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *),
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
154
  			  struct sk_buff_head *hitlist)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  {
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
156
157
158
159
  	struct sk_buff *skb;
  	struct sk_buff *next;
  
  	spin_lock(&x->sk_receive_queue.lock);
a2f3be17c   Ilpo Järvinen   unix/garbage: kil...
160
  	skb_queue_walk_safe(&x->sk_receive_queue, skb, next) {
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  		/*
  		 *	Do we have file descriptors ?
  		 */
  		if (UNIXCB(skb).fp) {
  			bool hit = false;
  			/*
  			 *	Process the descriptors of this socket
  			 */
  			int nfd = UNIXCB(skb).fp->count;
  			struct file **fp = UNIXCB(skb).fp->fp;
  			while (nfd--) {
  				/*
  				 *	Get the socket the fd matches
  				 *	if it indeed does so
  				 */
  				struct sock *sk = unix_get_socket(*fp++);
5c80f1ae9   Pavel Emelyanov   [AF_UNIX]: Conver...
177
  				if (sk) {
6209344f5   Miklos Szeredi   net: unix: fix in...
178
179
180
181
182
183
184
185
186
187
188
  					struct unix_sock *u = unix_sk(sk);
  
  					/*
  					 * Ignore non-candidates, they could
  					 * have been added to the queues after
  					 * starting the garbage collection
  					 */
  					if (u->gc_candidate) {
  						hit = true;
  						func(u);
  					}
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
189
190
191
192
193
194
195
196
197
  				}
  			}
  			if (hit && hitlist != NULL) {
  				__skb_unlink(skb, &x->sk_receive_queue);
  				__skb_queue_tail(hitlist, skb);
  			}
  		}
  	}
  	spin_unlock(&x->sk_receive_queue.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
  }
5c80f1ae9   Pavel Emelyanov   [AF_UNIX]: Conver...
199
  static void scan_children(struct sock *x, void (*func)(struct unix_sock *),
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
200
  			  struct sk_buff_head *hitlist)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  {
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
202
203
204
205
206
207
208
209
210
211
212
213
214
  	if (x->sk_state != TCP_LISTEN)
  		scan_inflight(x, func, hitlist);
  	else {
  		struct sk_buff *skb;
  		struct sk_buff *next;
  		struct unix_sock *u;
  		LIST_HEAD(embryos);
  
  		/*
  		 * For a listening socket collect the queued embryos
  		 * and perform a scan on them as well.
  		 */
  		spin_lock(&x->sk_receive_queue.lock);
a2f3be17c   Ilpo Järvinen   unix/garbage: kil...
215
  		skb_queue_walk_safe(&x->sk_receive_queue, skb, next) {
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
  			u = unix_sk(skb->sk);
  
  			/*
  			 * An embryo cannot be in-flight, so it's safe
  			 * to use the list link.
  			 */
  			BUG_ON(!list_empty(&u->link));
  			list_add_tail(&u->link, &embryos);
  		}
  		spin_unlock(&x->sk_receive_queue.lock);
  
  		while (!list_empty(&embryos)) {
  			u = list_entry(embryos.next, struct unix_sock, link);
  			scan_inflight(&u->sk, func, hitlist);
  			list_del_init(&u->link);
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
  }
5c80f1ae9   Pavel Emelyanov   [AF_UNIX]: Conver...
234
  static void dec_inflight(struct unix_sock *usk)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
  {
516e0cc56   Al Viro   [PATCH] f_count m...
236
  	atomic_long_dec(&usk->inflight);
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
237
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238

5c80f1ae9   Pavel Emelyanov   [AF_UNIX]: Conver...
239
  static void inc_inflight(struct unix_sock *usk)
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
240
  {
516e0cc56   Al Viro   [PATCH] f_count m...
241
  	atomic_long_inc(&usk->inflight);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
  }
5c80f1ae9   Pavel Emelyanov   [AF_UNIX]: Conver...
243
  static void inc_inflight_move_tail(struct unix_sock *u)
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
244
  {
516e0cc56   Al Viro   [PATCH] f_count m...
245
  	atomic_long_inc(&u->inflight);
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
246
  	/*
6209344f5   Miklos Szeredi   net: unix: fix in...
247
248
249
  	 * If this still might be part of a cycle, move it to the end
  	 * of the list, so that it's checked even if it was already
  	 * passed over
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
250
  	 */
6209344f5   Miklos Szeredi   net: unix: fix in...
251
  	if (u->gc_maybe_cycle)
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
252
253
  		list_move_tail(&u->link, &gc_candidates);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254

5f23b7349   dann frazier   net: Fix soft loc...
255
  static bool gc_in_progress = false;
9915672d4   Eric Dumazet   af_unix: limit un...
256
  #define UNIX_INFLIGHT_TRIGGER_GC 16000
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257

5f23b7349   dann frazier   net: Fix soft loc...
258
  void wait_for_unix_gc(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
  {
9915672d4   Eric Dumazet   af_unix: limit un...
260
261
262
263
264
265
  	/*
  	 * If number of inflight sockets is insane,
  	 * force a garbage collect right now.
  	 */
  	if (unix_tot_inflight > UNIX_INFLIGHT_TRIGGER_GC && !gc_in_progress)
  		unix_gc();
5f23b7349   dann frazier   net: Fix soft loc...
266
267
  	wait_event(unix_gc_wait, gc_in_progress == false);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268

5f23b7349   dann frazier   net: Fix soft loc...
269
270
271
  /* The external entry point: unix_gc() */
  void unix_gc(void)
  {
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
272
273
274
275
  	struct unix_sock *u;
  	struct unix_sock *next;
  	struct sk_buff_head hitlist;
  	struct list_head cursor;
6209344f5   Miklos Szeredi   net: unix: fix in...
276
  	LIST_HEAD(not_cycle_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277

1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
278
  	spin_lock(&unix_gc_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279

1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
280
281
282
  	/* Avoid a recursive GC. */
  	if (gc_in_progress)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283

1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
284
  	gc_in_progress = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  	/*
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
286
287
288
289
290
291
  	 * First, select candidates for garbage collection.  Only
  	 * in-flight sockets are considered, and from those only ones
  	 * which don't have any external reference.
  	 *
  	 * Holding unix_gc_lock will protect these candidates from
  	 * being detached, and hence from gaining an external
6209344f5   Miklos Szeredi   net: unix: fix in...
292
293
294
295
296
297
298
299
  	 * reference.  Since there are no possible receivers, all
  	 * buffers currently on the candidates' queues stay there
  	 * during the garbage collection.
  	 *
  	 * We also know that no new candidate can be added onto the
  	 * receive queues.  Other, non candidate sockets _can_ be
  	 * added to queue, so we must make sure only to touch
  	 * candidates.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
  	 */
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
301
  	list_for_each_entry_safe(u, next, &gc_inflight_list, link) {
516e0cc56   Al Viro   [PATCH] f_count m...
302
303
  		long total_refs;
  		long inflight_refs;
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
304
305
  
  		total_refs = file_count(u->sk.sk_socket->file);
516e0cc56   Al Viro   [PATCH] f_count m...
306
  		inflight_refs = atomic_long_read(&u->inflight);
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
307
308
309
310
311
312
  
  		BUG_ON(inflight_refs < 1);
  		BUG_ON(total_refs < inflight_refs);
  		if (total_refs == inflight_refs) {
  			list_move_tail(&u->link, &gc_candidates);
  			u->gc_candidate = 1;
6209344f5   Miklos Szeredi   net: unix: fix in...
313
  			u->gc_maybe_cycle = 1;
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
314
315
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
  
  	/*
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
318
319
  	 * Now remove all internal in-flight reference to children of
  	 * the candidates.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
  	 */
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
321
322
  	list_for_each_entry(u, &gc_candidates, link)
  		scan_children(&u->sk, dec_inflight, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
  
  	/*
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
325
326
327
328
329
330
  	 * Restore the references for children of all candidates,
  	 * which have remaining references.  Do this recursively, so
  	 * only those remain, which form cyclic references.
  	 *
  	 * Use a "cursor" link, to make the list traversal safe, even
  	 * though elements might be moved about.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
  	 */
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
332
333
334
  	list_add(&cursor, &gc_candidates);
  	while (cursor.next != &gc_candidates) {
  		u = list_entry(cursor.next, struct unix_sock, link);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335

1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
336
337
  		/* Move cursor to after the current position. */
  		list_move(&cursor, &u->link);
ac7bfa62f   YOSHIFUJI Hideaki   [NET] UNIX: Fix w...
338

516e0cc56   Al Viro   [PATCH] f_count m...
339
  		if (atomic_long_read(&u->inflight) > 0) {
6209344f5   Miklos Szeredi   net: unix: fix in...
340
341
  			list_move_tail(&u->link, &not_cycle_list);
  			u->gc_maybe_cycle = 0;
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
342
  			scan_children(&u->sk, inc_inflight_move_tail, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
  	}
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
345
  	list_del(&cursor);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346

1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
347
  	/*
6209344f5   Miklos Szeredi   net: unix: fix in...
348
349
350
351
352
353
354
355
356
357
  	 * not_cycle_list contains those sockets which do not make up a
  	 * cycle.  Restore these to the inflight list.
  	 */
  	while (!list_empty(&not_cycle_list)) {
  		u = list_entry(not_cycle_list.next, struct unix_sock, link);
  		u->gc_candidate = 0;
  		list_move_tail(&u->link, &gc_inflight_list);
  	}
  
  	/*
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
358
359
360
361
  	 * Now gc_candidates contains only garbage.  Restore original
  	 * inflight counters for these as well, and remove the skbuffs
  	 * which are creating the cycle(s).
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
  	skb_queue_head_init(&hitlist);
1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
363
  	list_for_each_entry(u, &gc_candidates, link)
e27dfcea4   Jianjun Kong   af_unix: clean up...
364
  	scan_children(&u->sk, inc_inflight, &hitlist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365

1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
366
  	spin_unlock(&unix_gc_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367

1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
368
369
  	/* Here we are. Hitlist is filled. Die. */
  	__skb_queue_purge(&hitlist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370

1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
371
  	spin_lock(&unix_gc_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372

1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
373
374
375
  	/* All candidates should have been detached by now. */
  	BUG_ON(!list_empty(&gc_candidates));
  	gc_in_progress = false;
5f23b7349   dann frazier   net: Fix soft loc...
376
  	wake_up(&unix_gc_wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377

1fd05ba5a   Miklos Szeredi   [AF_UNIX]: Rewrit...
378
379
   out:
  	spin_unlock(&unix_gc_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  }