mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-07 13:43:51 +00:00
af_unix: Link struct unix_edge when queuing skb.
Just before queuing skb with inflight fds, we call scm_stat_add(), which is a good place to set up the preallocated struct unix_vertex and struct unix_edge in UNIXCB(skb).fp. Then, we call unix_add_edges() and construct the directed graph as follows: 1. Set the inflight socket's unix_sock to unix_edge.predecessor. 2. Set the receiver's unix_sock to unix_edge.successor. 3. Set the preallocated vertex to inflight socket's unix_sock.vertex. 4. Link inflight socket's unix_vertex.entry to unix_unvisited_vertices. 5. Link unix_edge.vertex_entry to the inflight socket's unix_vertex.edges. Let's say we pass the fd of AF_UNIX socket A to B and the fd of B to C. The graph looks like this: +-------------------------+ | unix_unvisited_vertices | <-------------------------. +-------------------------+ | + | | +--------------+ +--------------+ | +--------------+ | | unix_sock A | <---. .---> | unix_sock B | <-|-. .---> | unix_sock C | | +--------------+ | | +--------------+ | | | +--------------+ | .-+ | vertex | | | .-+ | vertex | | | | | vertex | | | +--------------+ | | | +--------------+ | | | +--------------+ | | | | | | | | | | +--------------+ | | | +--------------+ | | | | '-> | unix_vertex | | | '-> | unix_vertex | | | | | +--------------+ | | +--------------+ | | | `---> | entry | +---------> | entry | +-' | | |--------------| | | |--------------| | | | edges | <-. | | | edges | <-. | | +--------------+ | | | +--------------+ | | | | | | | | | .----------------------' | | .----------------------' | | | | | | | | | +--------------+ | | | +--------------+ | | | | unix_edge | | | | | unix_edge | | | | +--------------+ | | | +--------------+ | | `-> | vertex_entry | | | `-> | vertex_entry | | | |--------------| | | |--------------| | | | predecessor | +---' | | predecessor | +---' | |--------------| | |--------------| | | successor | +-----' | successor | +-----' +--------------+ +--------------+ Henceforth, we denote such a graph as A -> B (-> C). Now, we can express all inflight fd graphs that do not contain embryo sockets. We will support the particular case later. Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com> Acked-by: Paolo Abeni <pabeni@redhat.com> Link: https://lore.kernel.org/r/20240325202425.60930-4-kuniyu@amazon.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
29b64e3540
commit
42f298c06b
@ -22,6 +22,8 @@ extern unsigned int unix_tot_inflight;
|
||||
|
||||
void unix_inflight(struct user_struct *user, struct file *fp);
|
||||
void unix_notinflight(struct user_struct *user, struct file *fp);
|
||||
void unix_add_edges(struct scm_fp_list *fpl, struct unix_sock *receiver);
|
||||
void unix_del_edges(struct scm_fp_list *fpl);
|
||||
int unix_prepare_fpl(struct scm_fp_list *fpl);
|
||||
void unix_destroy_fpl(struct scm_fp_list *fpl);
|
||||
void unix_gc(void);
|
||||
|
@ -32,6 +32,7 @@ struct scm_fp_list {
|
||||
short count_unix;
|
||||
short max;
|
||||
#ifdef CONFIG_UNIX
|
||||
bool inflight;
|
||||
struct list_head vertices;
|
||||
struct unix_edge *edges;
|
||||
#endif
|
||||
|
@ -90,6 +90,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
|
||||
fpl->max = SCM_MAX_FD;
|
||||
fpl->user = NULL;
|
||||
#if IS_ENABLED(CONFIG_UNIX)
|
||||
fpl->inflight = false;
|
||||
fpl->edges = NULL;
|
||||
INIT_LIST_HEAD(&fpl->vertices);
|
||||
#endif
|
||||
@ -384,6 +385,7 @@ struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
|
||||
new_fpl->max = new_fpl->count;
|
||||
new_fpl->user = get_uid(fpl->user);
|
||||
#if IS_ENABLED(CONFIG_UNIX)
|
||||
new_fpl->inflight = false;
|
||||
new_fpl->edges = NULL;
|
||||
INIT_LIST_HEAD(&new_fpl->vertices);
|
||||
#endif
|
||||
|
@ -1943,8 +1943,10 @@ static void scm_stat_add(struct sock *sk, struct sk_buff *skb)
|
||||
struct scm_fp_list *fp = UNIXCB(skb).fp;
|
||||
struct unix_sock *u = unix_sk(sk);
|
||||
|
||||
if (unlikely(fp && fp->count))
|
||||
if (unlikely(fp && fp->count)) {
|
||||
atomic_add(fp->count, &u->scm_stat.nr_fds);
|
||||
unix_add_edges(fp, u);
|
||||
}
|
||||
}
|
||||
|
||||
static void scm_stat_del(struct sock *sk, struct sk_buff *skb)
|
||||
@ -1952,8 +1954,10 @@ static void scm_stat_del(struct sock *sk, struct sk_buff *skb)
|
||||
struct scm_fp_list *fp = UNIXCB(skb).fp;
|
||||
struct unix_sock *u = unix_sk(sk);
|
||||
|
||||
if (unlikely(fp && fp->count))
|
||||
if (unlikely(fp && fp->count)) {
|
||||
atomic_sub(fp->count, &u->scm_stat.nr_fds);
|
||||
unix_del_edges(fp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -101,6 +101,38 @@ struct unix_sock *unix_get_socket(struct file *filp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static LIST_HEAD(unix_unvisited_vertices);
|
||||
|
||||
static void unix_add_edge(struct scm_fp_list *fpl, struct unix_edge *edge)
|
||||
{
|
||||
struct unix_vertex *vertex = edge->predecessor->vertex;
|
||||
|
||||
if (!vertex) {
|
||||
vertex = list_first_entry(&fpl->vertices, typeof(*vertex), entry);
|
||||
vertex->out_degree = 0;
|
||||
INIT_LIST_HEAD(&vertex->edges);
|
||||
|
||||
list_move_tail(&vertex->entry, &unix_unvisited_vertices);
|
||||
edge->predecessor->vertex = vertex;
|
||||
}
|
||||
|
||||
vertex->out_degree++;
|
||||
list_add_tail(&edge->vertex_entry, &vertex->edges);
|
||||
}
|
||||
|
||||
static void unix_del_edge(struct scm_fp_list *fpl, struct unix_edge *edge)
|
||||
{
|
||||
struct unix_vertex *vertex = edge->predecessor->vertex;
|
||||
|
||||
list_del(&edge->vertex_entry);
|
||||
vertex->out_degree--;
|
||||
|
||||
if (!vertex->out_degree) {
|
||||
edge->predecessor->vertex = NULL;
|
||||
list_move_tail(&vertex->entry, &fpl->vertices);
|
||||
}
|
||||
}
|
||||
|
||||
static void unix_free_vertices(struct scm_fp_list *fpl)
|
||||
{
|
||||
struct unix_vertex *vertex, *next_vertex;
|
||||
@ -111,6 +143,60 @@ static void unix_free_vertices(struct scm_fp_list *fpl)
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_SPINLOCK(unix_gc_lock);
|
||||
|
||||
void unix_add_edges(struct scm_fp_list *fpl, struct unix_sock *receiver)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
|
||||
spin_lock(&unix_gc_lock);
|
||||
|
||||
if (!fpl->count_unix)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
struct unix_sock *inflight = unix_get_socket(fpl->fp[j++]);
|
||||
struct unix_edge *edge;
|
||||
|
||||
if (!inflight)
|
||||
continue;
|
||||
|
||||
edge = fpl->edges + i++;
|
||||
edge->predecessor = inflight;
|
||||
edge->successor = receiver;
|
||||
|
||||
unix_add_edge(fpl, edge);
|
||||
} while (i < fpl->count_unix);
|
||||
|
||||
out:
|
||||
spin_unlock(&unix_gc_lock);
|
||||
|
||||
fpl->inflight = true;
|
||||
|
||||
unix_free_vertices(fpl);
|
||||
}
|
||||
|
||||
void unix_del_edges(struct scm_fp_list *fpl)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
spin_lock(&unix_gc_lock);
|
||||
|
||||
if (!fpl->count_unix)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
struct unix_edge *edge = fpl->edges + i++;
|
||||
|
||||
unix_del_edge(fpl, edge);
|
||||
} while (i < fpl->count_unix);
|
||||
|
||||
out:
|
||||
spin_unlock(&unix_gc_lock);
|
||||
|
||||
fpl->inflight = false;
|
||||
}
|
||||
|
||||
int unix_prepare_fpl(struct scm_fp_list *fpl)
|
||||
{
|
||||
struct unix_vertex *vertex;
|
||||
@ -141,11 +227,13 @@ int unix_prepare_fpl(struct scm_fp_list *fpl)
|
||||
|
||||
void unix_destroy_fpl(struct scm_fp_list *fpl)
|
||||
{
|
||||
if (fpl->inflight)
|
||||
unix_del_edges(fpl);
|
||||
|
||||
kvfree(fpl->edges);
|
||||
unix_free_vertices(fpl);
|
||||
}
|
||||
|
||||
DEFINE_SPINLOCK(unix_gc_lock);
|
||||
unsigned int unix_tot_inflight;
|
||||
static LIST_HEAD(gc_candidates);
|
||||
static LIST_HEAD(gc_inflight_list);
|
||||
|
Loading…
Reference in New Issue
Block a user