diff --git a/android/abi_gki_aarch64.stg b/android/abi_gki_aarch64.stg index 24e0b6b30473..1142c52ebdb2 100644 --- a/android/abi_gki_aarch64.stg +++ b/android/abi_gki_aarch64.stg @@ -112353,10 +112353,10 @@ member { offset: 64 } member { - id: 0xad0a7a1c + id: 0xad0a7aac name: "index" type_id: 0x33756485 - offset: 320 + offset: 448 } member { id: 0xad0a7d70 @@ -126563,6 +126563,12 @@ member { type_id: 0x6d7f5ff6 offset: 45416 } +member { + id: 0x03c71124 + name: "lowlink" + type_id: 0x33756485 + offset: 512 +} member { id: 0x8b23da1a name: "lowmem_reserve" @@ -148453,6 +148459,12 @@ member { type_id: 0xc93e017b offset: 288 } +member { + id: 0x19406067 + name: "on_stack" + type_id: 0x6d7f5ff6 + offset: 576 +} member { id: 0x9de8e020 name: "on_time" @@ -150647,10 +150659,10 @@ member { offset: 128 } member { - id: 0x0fc2a86e + id: 0x0fc2a957 name: "out_degree" type_id: 0x33756485 - offset: 256 + offset: 384 } member { id: 0xbdac191c @@ -180387,6 +180399,12 @@ member { type_id: 0x6d7f5ff6 offset: 40 } +member { + id: 0xd741c1b9 + name: "scc_entry" + type_id: 0xd3c80119 + offset: 256 +} member { id: 0x0ac848fc name: "scdc" @@ -277512,11 +277530,14 @@ struct_union { kind: STRUCT name: "unix_vertex" definition { - bytesize: 48 + bytesize: 80 member_id: 0x76197573 member_id: 0x4d8789fe - member_id: 0x0fc2a86e - member_id: 0xad0a7a1c + member_id: 0xd741c1b9 + member_id: 0x0fc2a957 + member_id: 0xad0a7aac + member_id: 0x03c71124 + member_id: 0x19406067 } } struct_union { diff --git a/android/abi_gki_aarch64.stg.allowed_breaks b/android/abi_gki_aarch64.stg.allowed_breaks index 4ccf9f9a64fd..fa251856e55b 100644 --- a/android/abi_gki_aarch64.stg.allowed_breaks +++ b/android/abi_gki_aarch64.stg.allowed_breaks @@ -171,3 +171,11 @@ type 'struct unix_vertex' changed byte size changed from 40 to 48 member 'unsigned long index' was added +type 'struct unix_vertex' changed + byte size changed from 48 to 80 + member 'struct list_head scc_entry' was added + 2 members ('unsigned long out_degree' .. 'unsigned long index') changed + offset changed by 128 + member 'unsigned long lowlink' was added + member 'bool on_stack' was added + diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 9198735a6acb..37171943fb54 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -32,8 +32,11 @@ void wait_for_unix_gc(struct scm_fp_list *fpl); struct unix_vertex { struct list_head edges; struct list_head entry; + struct list_head scc_entry; unsigned long out_degree; unsigned long index; + unsigned long lowlink; + bool on_stack; }; struct unix_edge { diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 295dd1a7b8e0..cdeff548e130 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -251,11 +251,19 @@ static LIST_HEAD(unix_visited_vertices); static void __unix_walk_scc(struct unix_vertex *vertex) { unsigned long index = UNIX_VERTEX_INDEX_START; + LIST_HEAD(vertex_stack); struct unix_edge *edge; LIST_HEAD(edge_stack); next_vertex: + /* Push vertex to vertex_stack. + * The vertex will be popped when finalising SCC later. + */ + vertex->on_stack = true; + list_add(&vertex->scc_entry, &vertex_stack); + vertex->index = index; + vertex->lowlink = index; index++; /* Explore neighbour vertices (receivers of the current vertex's fd). */ @@ -283,12 +291,46 @@ prev_vertex: edge = list_first_entry(&edge_stack, typeof(*edge), stack_entry); list_del_init(&edge->stack_entry); + next_vertex = vertex; vertex = edge->predecessor->vertex; + + /* If the successor has a smaller lowlink, two vertices + * are in the same SCC, so propagate the smaller lowlink + * to skip SCC finalisation. + */ + vertex->lowlink = min(vertex->lowlink, next_vertex->lowlink); + } else if (next_vertex->on_stack) { + /* Loop detected by a back/cross edge. + * + * The successor is on vertex_stack, so two vertices are + * in the same SCC. If the successor has a smaller index, + * propagate it to skip SCC finalisation. + */ + vertex->lowlink = min(vertex->lowlink, next_vertex->index); + } else { + /* The successor was already grouped as another SCC */ } } - /* Don't restart DFS from this vertex in unix_walk_scc(). */ - list_move_tail(&vertex->entry, &unix_visited_vertices); + if (vertex->index == vertex->lowlink) { + struct list_head scc; + + /* SCC finalised. + * + * If the lowlink was not updated, all the vertices above on + * vertex_stack are in the same SCC. Group them using scc_entry. + */ + __list_cut_position(&scc, &vertex_stack, &vertex->scc_entry); + + list_for_each_entry_reverse(vertex, &scc, scc_entry) { + /* Don't restart DFS from this vertex in unix_walk_scc(). */ + list_move_tail(&vertex->entry, &unix_visited_vertices); + + vertex->on_stack = false; + } + + list_del(&scc); + } /* Need backtracking ? */ if (!list_empty(&edge_stack))