|
@@ -186,8 +186,17 @@ static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *),
|
|
*/
|
|
*/
|
|
struct sock *sk = unix_get_socket(*fp++);
|
|
struct sock *sk = unix_get_socket(*fp++);
|
|
if (sk) {
|
|
if (sk) {
|
|
- hit = true;
|
|
|
|
- func(unix_sk(sk));
|
|
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (hit && hitlist != NULL) {
|
|
if (hit && hitlist != NULL) {
|
|
@@ -249,11 +258,11 @@ static void inc_inflight_move_tail(struct unix_sock *u)
|
|
{
|
|
{
|
|
atomic_long_inc(&u->inflight);
|
|
atomic_long_inc(&u->inflight);
|
|
/*
|
|
/*
|
|
- * If this is still a candidate, move it to the end of the
|
|
|
|
- * list, so that it's checked even if it was already passed
|
|
|
|
- * over
|
|
|
|
|
|
+ * 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
|
|
*/
|
|
*/
|
|
- if (u->gc_candidate)
|
|
|
|
|
|
+ if (u->gc_maybe_cycle)
|
|
list_move_tail(&u->link, &gc_candidates);
|
|
list_move_tail(&u->link, &gc_candidates);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -267,6 +276,7 @@ void unix_gc(void)
|
|
struct unix_sock *next;
|
|
struct unix_sock *next;
|
|
struct sk_buff_head hitlist;
|
|
struct sk_buff_head hitlist;
|
|
struct list_head cursor;
|
|
struct list_head cursor;
|
|
|
|
+ LIST_HEAD(not_cycle_list);
|
|
|
|
|
|
spin_lock(&unix_gc_lock);
|
|
spin_lock(&unix_gc_lock);
|
|
|
|
|
|
@@ -282,10 +292,14 @@ void unix_gc(void)
|
|
*
|
|
*
|
|
* Holding unix_gc_lock will protect these candidates from
|
|
* Holding unix_gc_lock will protect these candidates from
|
|
* being detached, and hence from gaining an external
|
|
* being detached, and hence from gaining an external
|
|
- * reference. This also means, that since there are no
|
|
|
|
- * possible receivers, the receive queues of these sockets are
|
|
|
|
- * static during the GC, even though the dequeue is done
|
|
|
|
- * before the detach without atomicity guarantees.
|
|
|
|
|
|
+ * 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.
|
|
*/
|
|
*/
|
|
list_for_each_entry_safe(u, next, &gc_inflight_list, link) {
|
|
list_for_each_entry_safe(u, next, &gc_inflight_list, link) {
|
|
long total_refs;
|
|
long total_refs;
|
|
@@ -299,6 +313,7 @@ void unix_gc(void)
|
|
if (total_refs == inflight_refs) {
|
|
if (total_refs == inflight_refs) {
|
|
list_move_tail(&u->link, &gc_candidates);
|
|
list_move_tail(&u->link, &gc_candidates);
|
|
u->gc_candidate = 1;
|
|
u->gc_candidate = 1;
|
|
|
|
+ u->gc_maybe_cycle = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -325,13 +340,23 @@ void unix_gc(void)
|
|
list_move(&cursor, &u->link);
|
|
list_move(&cursor, &u->link);
|
|
|
|
|
|
if (atomic_long_read(&u->inflight) > 0) {
|
|
if (atomic_long_read(&u->inflight) > 0) {
|
|
- list_move_tail(&u->link, &gc_inflight_list);
|
|
|
|
- u->gc_candidate = 0;
|
|
|
|
|
|
+ list_move_tail(&u->link, ¬_cycle_list);
|
|
|
|
+ u->gc_maybe_cycle = 0;
|
|
scan_children(&u->sk, inc_inflight_move_tail, NULL);
|
|
scan_children(&u->sk, inc_inflight_move_tail, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
list_del(&cursor);
|
|
list_del(&cursor);
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * not_cycle_list contains those sockets which do not make up a
|
|
|
|
+ * cycle. Restore these to the inflight list.
|
|
|
|
+ */
|
|
|
|
+ while (!list_empty(¬_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);
|
|
|
|
+ }
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Now gc_candidates contains only garbage. Restore original
|
|
* Now gc_candidates contains only garbage. Restore original
|
|
* inflight counters for these as well, and remove the skbuffs
|
|
* inflight counters for these as well, and remove the skbuffs
|