static LIST_HEAD(unix_unvisited_vertices);
 
+enum unix_vertex_index {
+       UNIX_VERTEX_INDEX_UNVISITED,
+       UNIX_VERTEX_INDEX_START,
+};
+
 static void unix_add_edge(struct scm_fp_list *fpl, struct unix_edge *edge)
 {
        struct unix_vertex *vertex = edge->predecessor->vertex;
        unix_free_vertices(fpl);
 }
 
+static LIST_HEAD(unix_visited_vertices);
+
+static void __unix_walk_scc(struct unix_vertex *vertex)
+{
+       unsigned long index = UNIX_VERTEX_INDEX_START;
+       struct unix_edge *edge;
+       LIST_HEAD(edge_stack);
+
+next_vertex:
+       vertex->index = index;
+       index++;
+
+       /* Explore neighbour vertices (receivers of the current vertex's fd). */
+       list_for_each_entry(edge, &vertex->edges, vertex_entry) {
+               struct unix_vertex *next_vertex = edge->successor->vertex;
+
+               if (!next_vertex)
+                       continue;
+
+               if (next_vertex->index == UNIX_VERTEX_INDEX_UNVISITED) {
+                       /* Iterative deepening depth first search
+                        *
+                        *   1. Push a forward edge to edge_stack and set
+                        *      the successor to vertex for the next iteration.
+                        */
+                       list_add(&edge->stack_entry, &edge_stack);
+
+                       vertex = next_vertex;
+                       goto next_vertex;
+
+                       /*   2. Pop the edge directed to the current vertex
+                        *      and restore the ancestor for backtracking.
+                        */
+prev_vertex:
+                       edge = list_first_entry(&edge_stack, typeof(*edge), stack_entry);
+                       list_del_init(&edge->stack_entry);
+
+                       vertex = edge->predecessor->vertex;
+               }
+       }
+
+       /* Don't restart DFS from this vertex in unix_walk_scc(). */
+       list_move_tail(&vertex->entry, &unix_visited_vertices);
+
+       /* Need backtracking ? */
+       if (!list_empty(&edge_stack))
+               goto prev_vertex;
+}
+
+static void unix_walk_scc(void)
+{
+       struct unix_vertex *vertex;
+
+       list_for_each_entry(vertex, &unix_unvisited_vertices, entry)
+               vertex->index = UNIX_VERTEX_INDEX_UNVISITED;
+
+       /* Visit every vertex exactly once.
+        * __unix_walk_scc() moves visited vertices to unix_visited_vertices.
+        */
+       while (!list_empty(&unix_unvisited_vertices)) {
+               vertex = list_first_entry(&unix_unvisited_vertices, typeof(*vertex), entry);
+               __unix_walk_scc(vertex);
+       }
+
+       list_replace_init(&unix_visited_vertices, &unix_unvisited_vertices);
+}
+
 static LIST_HEAD(gc_candidates);
 static LIST_HEAD(gc_inflight_list);
 
 
        spin_lock(&unix_gc_lock);
 
+       unix_walk_scc();
+
        /* First, select candidates for garbage collection.  Only
         * in-flight sockets are considered, and from those only ones
         * which don't have any external reference.