Commit 545a6822 authored by David S. Miller's avatar David S. Miller

Merge branch 'unix-Show-number-of-scm-files-in-fdinfo'

Kirill Tkhai says:

====================
unix: Show number of scm files in fdinfo

v2: Pass correct argument to locked in patch [2/2].

Unix sockets like a block box. You never know what is pending there:
there may be a file descriptor holding a mount or a block device,
or there may be whole universes with namespaces, sockets with receive
queues full of sockets etc.

The patchset makes number of pending scm files be visible in fdinfo.
This may be useful to determine, that socket should be investigated
or which task should be killed to put a reference counter on a resourse.

$cat /proc/[pid]/fdinfo/[unix_sk_fd] | grep scm_fds
scm_fds: 1
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6b8350a4 3c32da19
...@@ -171,6 +171,7 @@ struct proto_ops { ...@@ -171,6 +171,7 @@ struct proto_ops {
int (*compat_getsockopt)(struct socket *sock, int level, int (*compat_getsockopt)(struct socket *sock, int level,
int optname, char __user *optval, int __user *optlen); int optname, char __user *optval, int __user *optlen);
#endif #endif
void (*show_fdinfo)(struct seq_file *m, struct socket *sock);
int (*sendmsg) (struct socket *sock, struct msghdr *m, int (*sendmsg) (struct socket *sock, struct msghdr *m,
size_t total_len); size_t total_len);
/* Notes for implementing recvmsg: /* Notes for implementing recvmsg:
......
...@@ -41,6 +41,10 @@ struct unix_skb_parms { ...@@ -41,6 +41,10 @@ struct unix_skb_parms {
u32 consumed; u32 consumed;
} __randomize_layout; } __randomize_layout;
struct scm_stat {
u32 nr_fds;
};
#define UNIXCB(skb) (*(struct unix_skb_parms *)&((skb)->cb)) #define UNIXCB(skb) (*(struct unix_skb_parms *)&((skb)->cb))
#define unix_state_lock(s) spin_lock(&unix_sk(s)->lock) #define unix_state_lock(s) spin_lock(&unix_sk(s)->lock)
...@@ -65,6 +69,7 @@ struct unix_sock { ...@@ -65,6 +69,7 @@ struct unix_sock {
#define UNIX_GC_MAYBE_CYCLE 1 #define UNIX_GC_MAYBE_CYCLE 1
struct socket_wq peer_wq; struct socket_wq peer_wq;
wait_queue_entry_t peer_wake; wait_queue_entry_t peer_wake;
struct scm_stat scm_stat;
}; };
static inline struct unix_sock *unix_sk(const struct sock *sk) static inline struct unix_sock *unix_sk(const struct sock *sk)
......
...@@ -128,6 +128,7 @@ static ssize_t sock_sendpage(struct file *file, struct page *page, ...@@ -128,6 +128,7 @@ static ssize_t sock_sendpage(struct file *file, struct page *page,
static ssize_t sock_splice_read(struct file *file, loff_t *ppos, static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len, struct pipe_inode_info *pipe, size_t len,
unsigned int flags); unsigned int flags);
static void sock_show_fdinfo(struct seq_file *m, struct file *f);
/* /*
* Socket files have a set of 'special' operations as well as the generic file ones. These don't appear * Socket files have a set of 'special' operations as well as the generic file ones. These don't appear
...@@ -150,6 +151,9 @@ static const struct file_operations socket_file_ops = { ...@@ -150,6 +151,9 @@ static const struct file_operations socket_file_ops = {
.sendpage = sock_sendpage, .sendpage = sock_sendpage,
.splice_write = generic_splice_sendpage, .splice_write = generic_splice_sendpage,
.splice_read = sock_splice_read, .splice_read = sock_splice_read,
#ifdef CONFIG_PROC_FS
.show_fdinfo = sock_show_fdinfo,
#endif
}; };
/* /*
...@@ -993,6 +997,14 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from) ...@@ -993,6 +997,14 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from)
return res; return res;
} }
static void sock_show_fdinfo(struct seq_file *m, struct file *f)
{
struct socket *sock = f->private_data;
if (sock->ops->show_fdinfo)
sock->ops->show_fdinfo(m, sock);
}
/* /*
* Atomic setting of ioctl hooks to avoid race * Atomic setting of ioctl hooks to avoid race
* with module unload. * with module unload.
......
...@@ -676,6 +676,16 @@ static int unix_set_peek_off(struct sock *sk, int val) ...@@ -676,6 +676,16 @@ static int unix_set_peek_off(struct sock *sk, int val)
return 0; return 0;
} }
static void unix_show_fdinfo(struct seq_file *m, struct socket *sock)
{
struct sock *sk = sock->sk;
struct unix_sock *u;
if (sk) {
u = unix_sk(sock->sk);
seq_printf(m, "scm_fds: %u\n", READ_ONCE(u->scm_stat.nr_fds));
}
}
static const struct proto_ops unix_stream_ops = { static const struct proto_ops unix_stream_ops = {
.family = PF_UNIX, .family = PF_UNIX,
...@@ -701,6 +711,7 @@ static const struct proto_ops unix_stream_ops = { ...@@ -701,6 +711,7 @@ static const struct proto_ops unix_stream_ops = {
.sendpage = unix_stream_sendpage, .sendpage = unix_stream_sendpage,
.splice_read = unix_stream_splice_read, .splice_read = unix_stream_splice_read,
.set_peek_off = unix_set_peek_off, .set_peek_off = unix_set_peek_off,
.show_fdinfo = unix_show_fdinfo,
}; };
static const struct proto_ops unix_dgram_ops = { static const struct proto_ops unix_dgram_ops = {
...@@ -726,6 +737,7 @@ static const struct proto_ops unix_dgram_ops = { ...@@ -726,6 +737,7 @@ static const struct proto_ops unix_dgram_ops = {
.mmap = sock_no_mmap, .mmap = sock_no_mmap,
.sendpage = sock_no_sendpage, .sendpage = sock_no_sendpage,
.set_peek_off = unix_set_peek_off, .set_peek_off = unix_set_peek_off,
.show_fdinfo = unix_show_fdinfo,
}; };
static const struct proto_ops unix_seqpacket_ops = { static const struct proto_ops unix_seqpacket_ops = {
...@@ -751,6 +763,7 @@ static const struct proto_ops unix_seqpacket_ops = { ...@@ -751,6 +763,7 @@ static const struct proto_ops unix_seqpacket_ops = {
.mmap = sock_no_mmap, .mmap = sock_no_mmap,
.sendpage = sock_no_sendpage, .sendpage = sock_no_sendpage,
.set_peek_off = unix_set_peek_off, .set_peek_off = unix_set_peek_off,
.show_fdinfo = unix_show_fdinfo,
}; };
static struct proto unix_proto = { static struct proto unix_proto = {
...@@ -788,6 +801,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern) ...@@ -788,6 +801,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern)
mutex_init(&u->bindlock); /* single task binding lock */ mutex_init(&u->bindlock); /* single task binding lock */
init_waitqueue_head(&u->peer_wait); init_waitqueue_head(&u->peer_wait);
init_waitqueue_func_entry(&u->peer_wake, unix_dgram_peer_wake_relay); init_waitqueue_func_entry(&u->peer_wake, unix_dgram_peer_wake_relay);
memset(&u->scm_stat, 0, sizeof(struct scm_stat));
unix_insert_socket(unix_sockets_unbound(sk), sk); unix_insert_socket(unix_sockets_unbound(sk), sk);
out: out:
if (sk == NULL) if (sk == NULL)
...@@ -1572,6 +1586,28 @@ static bool unix_skb_scm_eq(struct sk_buff *skb, ...@@ -1572,6 +1586,28 @@ static bool unix_skb_scm_eq(struct sk_buff *skb,
unix_secdata_eq(scm, skb); unix_secdata_eq(scm, skb);
} }
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);
lockdep_assert_held(&sk->sk_receive_queue.lock);
if (unlikely(fp && fp->count))
u->scm_stat.nr_fds += fp->count;
}
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);
lockdep_assert_held(&sk->sk_receive_queue.lock);
if (unlikely(fp && fp->count))
u->scm_stat.nr_fds -= fp->count;
}
/* /*
* Send AF_UNIX data. * Send AF_UNIX data.
*/ */
...@@ -1757,7 +1793,10 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, ...@@ -1757,7 +1793,10 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
if (sock_flag(other, SOCK_RCVTSTAMP)) if (sock_flag(other, SOCK_RCVTSTAMP))
__net_timestamp(skb); __net_timestamp(skb);
maybe_add_creds(skb, sock, other); maybe_add_creds(skb, sock, other);
skb_queue_tail(&other->sk_receive_queue, skb); spin_lock(&other->sk_receive_queue.lock);
scm_stat_add(other, skb);
__skb_queue_tail(&other->sk_receive_queue, skb);
spin_unlock(&other->sk_receive_queue.lock);
unix_state_unlock(other); unix_state_unlock(other);
other->sk_data_ready(other); other->sk_data_ready(other);
sock_put(other); sock_put(other);
...@@ -1859,7 +1898,10 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, ...@@ -1859,7 +1898,10 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
goto pipe_err_free; goto pipe_err_free;
maybe_add_creds(skb, sock, other); maybe_add_creds(skb, sock, other);
skb_queue_tail(&other->sk_receive_queue, skb); spin_lock(&other->sk_receive_queue.lock);
scm_stat_add(other, skb);
__skb_queue_tail(&other->sk_receive_queue, skb);
spin_unlock(&other->sk_receive_queue.lock);
unix_state_unlock(other); unix_state_unlock(other);
other->sk_data_ready(other); other->sk_data_ready(other);
sent += size; sent += size;
...@@ -2058,8 +2100,8 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, ...@@ -2058,8 +2100,8 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
mutex_lock(&u->iolock); mutex_lock(&u->iolock);
skip = sk_peek_offset(sk, flags); skip = sk_peek_offset(sk, flags);
skb = __skb_try_recv_datagram(sk, flags, NULL, &skip, &err, skb = __skb_try_recv_datagram(sk, flags, scm_stat_del,
&last); &skip, &err, &last);
if (skb) if (skb)
break; break;
...@@ -2353,8 +2395,12 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, ...@@ -2353,8 +2395,12 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
sk_peek_offset_bwd(sk, chunk); sk_peek_offset_bwd(sk, chunk);
if (UNIXCB(skb).fp) if (UNIXCB(skb).fp) {
spin_lock(&sk->sk_receive_queue.lock);
scm_stat_del(sk, skb);
spin_unlock(&sk->sk_receive_queue.lock);
unix_detach_fds(&scm, skb); unix_detach_fds(&scm, skb);
}
if (unix_skb_len(skb)) if (unix_skb_len(skb))
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment