forked from mirrors/linux
		
	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: Kirill Tkhai <ktkhai@virtuozzo.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									b4653342b1
								
							
						
					
					
						commit
						3c32da19a8
					
				
					 2 changed files with 56 additions and 5 deletions
				
			
		|  | @ -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 { | ||||||
| #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) | ||||||
|  |  | ||||||
|  | @ -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 = { | ||||||
| 	.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 = { | ||||||
| 	.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 = { | ||||||
| 	.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) | ||||||
| 	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, | ||||||
| 	       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, | ||||||
| 	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, | ||||||
| 			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, | ||||||
| 		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, | ||||||
| 
 | 
 | ||||||
| 			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; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Kirill Tkhai
						Kirill Tkhai