Commit 3c32da19 authored by Kirill Tkhai's avatar Kirill Tkhai Committed by David S. Miller
Browse files

unix: Show number of pending scm files of receive queue in fdinfo



Unix sockets like a block box. You never know what is stored 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 patch adds a little debug and accounts number of files (not recursive),
which is in receive queue of a unix socket. Sometimes this is useful
to determine, that socket should be investigated or which task should
be killed to put reference counter on a resourse.

v2: Pass correct argument to lockdep

Signed-off-by: default avatarKirill Tkhai <ktkhai@virtuozzo.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b4653342
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -41,6 +41,10 @@ struct unix_skb_parms {
	u32			consumed;
} __randomize_layout;

struct scm_stat {
	u32 nr_fds;
};

#define UNIXCB(skb)	(*(struct unix_skb_parms *)&((skb)->cb))

#define unix_state_lock(s)	spin_lock(&unix_sk(s)->lock)
@@ -65,6 +69,7 @@ struct unix_sock {
#define UNIX_GC_MAYBE_CYCLE	1
	struct socket_wq	peer_wq;
	wait_queue_entry_t	peer_wake;
	struct scm_stat		scm_stat;
};

static inline struct unix_sock *unix_sk(const struct sock *sk)
+51 −5
Original line number Diff line number Diff line
@@ -676,6 +676,16 @@ static int unix_set_peek_off(struct sock *sk, int val)
	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 = {
	.family =	PF_UNIX,
@@ -701,6 +711,7 @@ static const struct proto_ops unix_stream_ops = {
	.sendpage =	unix_stream_sendpage,
	.splice_read =	unix_stream_splice_read,
	.set_peek_off =	unix_set_peek_off,
	.show_fdinfo =	unix_show_fdinfo,
};

static const struct proto_ops unix_dgram_ops = {
@@ -726,6 +737,7 @@ static const struct proto_ops unix_dgram_ops = {
	.mmap =		sock_no_mmap,
	.sendpage =	sock_no_sendpage,
	.set_peek_off =	unix_set_peek_off,
	.show_fdinfo =	unix_show_fdinfo,
};

static const struct proto_ops unix_seqpacket_ops = {
@@ -751,6 +763,7 @@ static const struct proto_ops unix_seqpacket_ops = {
	.mmap =		sock_no_mmap,
	.sendpage =	sock_no_sendpage,
	.set_peek_off =	unix_set_peek_off,
	.show_fdinfo =	unix_show_fdinfo,
};

static struct proto unix_proto = {
@@ -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 */
	init_waitqueue_head(&u->peer_wait);
	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);
out:
	if (sk == NULL)
@@ -1572,6 +1586,28 @@ static bool unix_skb_scm_eq(struct sk_buff *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.
 */
@@ -1757,7 +1793,10 @@ restart_locked:
	if (sock_flag(other, SOCK_RCVTSTAMP))
		__net_timestamp(skb);
	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);
	other->sk_data_ready(other);
	sock_put(other);
@@ -1859,7 +1898,10 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
			goto pipe_err_free;

		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);
		other->sk_data_ready(other);
		sent += size;
@@ -2058,8 +2100,8 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
		mutex_lock(&u->iolock);

		skip = sk_peek_offset(sk, flags);
		skb = __skb_try_recv_datagram(sk, flags, NULL, &skip, &err,
					      &last);
		skb = __skb_try_recv_datagram(sk, flags, scm_stat_del,
					      &skip, &err, &last);
		if (skb)
			break;

@@ -2353,8 +2395,12 @@ unlock:

			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);
			}

			if (unix_skb_len(skb))
				break;