Stop corrupting socket's task_frag
Subsystems (20)
cover Stop corrupting socket's task_frag
The networking code uses flags in sk_allocation to determine if it can use current->task_frag, however in-kernel users of sockets may stop setting sk_allocation when they convert to the preferred memalloc_nofs_save/restore, as SUNRPC has done in commit a1231fda7e94 ("SUNRPC: Set memalloc_nofs_save() on all rpciod/xprtiod jobs").
This will cause corruption in current->task_frag when recursing into the network layer for those subsystems during page fault or reclaim. The corruption is difficult to diagnose because stack traces may not contain the offending subsystem at all. The corruption is unlikely to show up in testing because it requires memory pressure, and so subsystems that convert to memalloc_nofs_save/restore are likely to continue to run into this issue.
Previous reports and proposed fixes:
https://lore.kernel.org/netdev/96a18bd00cbc6cb554603cc0d6ef1c551965b078.1663762494.git.gnault@redhat.com/
https://lore.kernel.org/netdev/b4d8cb09c913d3e34f853736f3f5628abfd7f4b6.1656699567.git.gnault@redhat.com/
https://lore.kernel.org/linux-nfs/de6d99321d1dcaa2ad456b92b3680aa77c07a747.1665401788.git.gnault@redhat.com/
Guilluame Nault has done all of the hard work tracking this problem down and finding the best fix for this issue. I'm just taking a turn posting another fix.
Benjamin Coddington (2):
Treewide: Stop corrupting socket's task_frag
net: simplify sk_page_frag
Guillaume Nault (1):
net: Introduce sk_use_task_frag in struct sock.
drivers/block/drbd/drbd_receiver.c | 3 +++ drivers/block/nbd.c | 1 + drivers/nvme/host/tcp.c | 1 + drivers/scsi/iscsi_tcp.c | 1 + drivers/usb/usbip/usbip_common.c | 1 + fs/afs/rxrpc.c | 1 + fs/cifs/connect.c | 1 + fs/dlm/lowcomms.c | 2 ++ fs/ocfs2/cluster/tcp.c | 1 + include/net/sock.h | 10 ++++++---- net/9p/trans_fd.c | 1 + net/ceph/messenger.c | 1 + net/core/sock.c | 1 + net/sunrpc/xprtsock.c | 3 +++ 14 files changed, 24 insertions(+), 4 deletions(-)
Signature · 1 line
2.31.1
PATCH 1/3 net: Introduce sk_use_task_frag in struct sock.
From: Guillaume Nault <[email protected]>
Sockets that can be used while recursing into memory reclaim, like those used by network block devices and file systems, mustn't use current->task_frag: if the current process is already using it, then the inner memory reclaim call would corrupt the task_frag structure.
To avoid this, sk_page_frag() uses ->sk_allocation to detect sockets that mustn't use current->task_frag, assuming that those used during memory reclaim had their allocation constraints reflected in ->sk_allocation.
This unfortunately doesn't cover all cases: in an attempt to remove all usage of GFP_NOFS and GFP_NOIO, sunrpc stopped setting these flags in ->sk_allocation, and used memalloc_nofs critical sections instead. This breaks the sk_page_frag() heuristic since the allocation constraints are now stored in current->flags, which sk_page_frag() can't read without risking triggering a cache miss and slowing down TCP's fast path.
This patch creates a new field in struct sock, named sk_use_task_frag, which sockets with memory reclaim constraints can set to false if they can't safely use current->task_frag. In such cases, sk_page_frag() now always returns the socket's page_frag (->sk_frag). The first user is sunrpc, which needs to avoid using current->task_frag but can keep ->sk_allocation set to GFP_KERNEL otherwise.
Eventually, it might be possible to simplify sk_page_frag() by only testing ->sk_use_task_frag and avoid relying on the ->sk_allocation heuristic entirely (assuming other sockets will set ->sk_use_task_frag according to their constraints in the future).
The new ->sk_use_task_frag field is placed in a hole in struct sock and belongs to a cache line shared with ->sk_shutdown. Therefore it should be hot and shouldn't have negative performance impacts on TCP's fast path (sk_shutdown is tested just before the while() loop in tcp_sendmsg_locked()).
--- include/net/sock.h | 11 +++++++++-- net/core/sock.c | 1 + 2 files changed, 10 insertions(+), 2 deletions(-)
include/net/sock.h +9 -2
@@ -318,6 +318,9 @@ struct sk_filter;* @sk_stamp: time stamp of last packet received * @sk_stamp_seq: lock for accessing sk_stamp on 32 bit architectures only * @sk_tsflags: SO_TIMESTAMPING flags +* @sk_use_task_frag: allow sk_page_frag() to use current->task_frag. +Sockets that can be used under memory reclaim should +set this to false. * @sk_bind_phc: SO_TIMESTAMPING bind PHC index of PTP virtual clock * for timestamping * @sk_tskey: counter to disambiguate concurrent tstamp requests
@@ -504,6 +507,7 @@ struct sock {#endif u16 sk_tsflags;u8 sk_shutdown; +bool sk_use_task_frag;atomic_t sk_tskey;atomic_t sk_zckey;
@@ -2536,14 +2540,17 @@ static inline void sk_stream_moderate_sndbuf(struct sock *sk)* socket operations and end up recursing into sk_page_frag() * while it's already in use: explicitly avoid task page_frag * usage if the caller is potentially doing any of them. -* This assumes that page fault handlers use the GFP_NOFS flags. +* This assumes that page fault handlers use the GFP_NOFS flags or +* explicitly disable sk_use_task_frag. * * Return: a per task page_frag if context allows that, * otherwise a per socket one. */ static inline struct page_frag *sk_page_frag (struct sock *sk) { -if ((sk->sk_allocation & (__GFP_DIRECT_RECLAIM | __GFP_MEMALLOC | __GFP_FS)) == +if (sk->sk_use_task_frag && + (sk->sk_allocation & (__GFP_DIRECT_RECLAIM | __GFP_MEMALLOC | + __GFP_FS)) == (__GFP_DIRECT_RECLAIM | __GFP_FS))return ¤t->task_frag;
net/core/sock.c +1 -0
@@ -3314,6 +3314,7 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_rcvbuf =READ_ONCE (sysctl_rmem_default); sk->sk_sndbuf =READ_ONCE (sysctl_wmem_default); sk->sk_state =TCP_CLOSE ; + sk->sk_use_task_frag = true;sk_set_socket (sk, sock);sock_set_flag (sk,SOCK_ZAPPED );
Signature · 1 line
2.31.1
Paolo Abeni
maintainerRe: [PATCH v1 1/3] net: Introduce sk_use_task_frag in struct sock.
Quoted 60 lines · Benjamin Coddington
From: Guillaume Nault <[email protected]>
Sockets that can be used while recursing into memory reclaim, like those used by network block devices and file systems, mustn't use
current->task_frag: if the current process is already using it, then the inner memory reclaim call would corrupt thetask_fragstructure.To avoid this,
sk_page_frag()uses ->sk_allocation to detect sockets that mustn't usecurrent->task_frag, assuming that those used during memory reclaim had their allocation constraints reflected in ->sk_allocation.This unfortunately doesn't cover all cases: in an attempt to remove all usage of
GFP_NOFSandGFP_NOIO, sunrpc stopped setting these flags in ->sk_allocation, and usedmemalloc_nofscritical sections instead. This breaks thesk_page_frag()heuristic since the allocation constraints are now stored incurrent->flags, whichsk_page_frag()can't read without risking triggering a cache miss and slowing down TCP's fast path.This patch creates a new field in struct sock, named
sk_use_task_frag, which sockets with memory reclaim constraints can set to false if they can't safely usecurrent->task_frag. In such cases,sk_page_frag()now always returns the socket'spage_frag(->sk_frag). The first user is sunrpc, which needs to avoid usingcurrent->task_fragbut can keep ->sk_allocation set toGFP_KERNELotherwise.Eventually, it might be possible to simplify
sk_page_frag()by only testing ->sk_use_task_frag and avoid relying on the ->sk_allocation heuristic entirely (assuming other sockets will set ->sk_use_task_frag according to their constraints in the future).The new ->sk_use_task_frag field is placed in a hole in struct sock and belongs to a cache line shared with ->sk_shutdown. Therefore it should be hot and shouldn't have negative performance impacts on TCP's fast path (
sk_shutdownis tested just before thewhile()loop intcp_sendmsg_locked()).Link https://lore.kernel.org/netdev/b4d8cb09c913d3e34f853736f3f5628abfd7f4b6.1656699567.git.gnault@redhat.com/ open hereSigned-off-by Guillaume Nault <[email protected]>--- include/net/sock.h | 11 +++++++++-- net/core/sock.c | 1 + 2 files changed, 10 insertions(+), 2 deletions(-)include/net/sock.h +3
-0@@ -318,6 +318,9 @@ struct sk_filter;* @sk_stamp: time stamp of last packet received * @sk_stamp_seq: lock for accessing sk_stamp on 32 bit architectures only * @sk_tsflags: SO_TIMESTAMPING flags +* @sk_use_task_frag: allow sk_page_frag() to use current->task_frag. +Sockets that can be used under memory reclaim should +set this to false. * @sk_bind_phc: SO_TIMESTAMPING bind PHC index of PTP virtual clock * for timestamping * @sk_tskey: counter to disambiguate concurrent tstamp requests
include/net/sock.h struct sock { · line 512
Quoted 6 lines
include/net/sock.h +1
-0@@ -504,6 +507,7 @@ struct sock {#endif u16 sk_tsflags;u8 sk_shutdown; +bool sk_use_task_frag;atomic_t sk_tskey;atomic_t sk_zckey;
I think the above should be fine from a data locality PoV, as the used cacheline should be hot at sk_page_frag_refill() usage time, as sk_tsflags has been accessed just before.
@Eric, does the above fit with the planned sock fields reordering?
Jakub noted we could use a bitfield here to be future proof for additional flags addition. I think in this specific case a bool is preferable, because we actually wont to discourage people to add more of such flags, and the search for holes (or the bool -> bitflag conversion) should give to such eventual future changes some additional thoughts.
Thanks!
Paolo
Re: [PATCH v1 1/3] net: Introduce sk_use_task_frag in struct sock.
Quoted 73 lines · Paolo Abeni
Quoted 60 lines · Benjamin Coddington
From: Guillaume Nault <[email protected]>
Sockets that can be used while recursing into memory reclaim, like those used by network block devices and file systems, mustn't use
current->task_frag: if the current process is already using it, then the inner memory reclaim call would corrupt thetask_fragstructure.To avoid this,
sk_page_frag()uses ->sk_allocation to detect sockets that mustn't usecurrent->task_frag, assuming that those used during memory reclaim had their allocation constraints reflected in ->sk_allocation.This unfortunately doesn't cover all cases: in an attempt to remove all usage of
GFP_NOFSandGFP_NOIO, sunrpc stopped setting these flags in ->sk_allocation, and usedmemalloc_nofscritical sections instead. This breaks thesk_page_frag()heuristic since the allocation constraints are now stored incurrent->flags, whichsk_page_frag()can't read without risking triggering a cache miss and slowing down TCP's fast path.This patch creates a new field in struct sock, named
sk_use_task_frag, which sockets with memory reclaim constraints can set to false if they can't safely usecurrent->task_frag. In such cases,sk_page_frag()now always returns the socket'spage_frag(->sk_frag). The first user is sunrpc, which needs to avoid usingcurrent->task_fragbut can keep ->sk_allocation set toGFP_KERNELotherwise.Eventually, it might be possible to simplify
sk_page_frag()by only testing ->sk_use_task_frag and avoid relying on the ->sk_allocation heuristic entirely (assuming other sockets will set ->sk_use_task_frag according to their constraints in the future).The new ->sk_use_task_frag field is placed in a hole in struct sock and belongs to a cache line shared with ->sk_shutdown. Therefore it should be hot and shouldn't have negative performance impacts on TCP's fast path (
sk_shutdownis tested just before thewhile()loop intcp_sendmsg_locked()).Link https://lore.kernel.org/netdev/b4d8cb09c913d3e34f853736f3f5628abfd7f4b6.1656699567.git.gnault@redhat.com/ open hereSigned-off-by Guillaume Nault <[email protected]>--- include/net/sock.h | 11 +++++++++-- net/core/sock.c | 1 + 2 files changed, 10 insertions(+), 2 deletions(-)include/net/sock.h +3
-0@@ -318,6 +318,9 @@ struct sk_filter;* @sk_stamp: time stamp of last packet received * @sk_stamp_seq: lock for accessing sk_stamp on 32 bit architectures only * @sk_tsflags: SO_TIMESTAMPING flags +* @sk_use_task_frag: allow sk_page_frag() to use current->task_frag. +Sockets that can be used under memory reclaim should +set this to false. * @sk_bind_phc: SO_TIMESTAMPING bind PHC index of PTP virtual clock * for timestamping * @sk_tskey: counter to disambiguate concurrent tstamp requests include/net/sock.h struct sock { · line 512
Quoted 6 lines
include/net/sock.h +1
-0@@ -504,6 +507,7 @@ struct sock {#endif u16 sk_tsflags;u8 sk_shutdown; +bool sk_use_task_frag;atomic_t sk_tskey;atomic_t sk_zckey;I think the above should be fine from a data locality PoV, as the used cacheline should be hot at
sk_page_frag_refill()usage time, assk_tsflagshas been accessed just before.
Quoted 1 line
@Eric, does the above fit with the planned sock fields reordering?
Do not worry about that, this can be handled later if needed.
Quoted 12 lines
Jakub noted we could use a bitfield here to be future proof for additional flags addition. I think in this specific case a bool is preferable, because we actually wont to discourage people to add more of such flags, and the search for holes (or the bool -> bitflag conversion) should give to such eventual future changes some additional thoughts.
Thanks!
Paolo
PATCH 2/3 Treewide: Stop corrupting socket's task_frag
In reply to Benjamin CoddingtonSince moving to memalloc_nofs_save/restore, SUNRPC has stopped setting the GFP_NOIO flag on sk_allocation which the networking system uses to decide when it is safe to use current->task_frag. The results of this are unexpected corruption in task_frag when SUNRPC is involved in memory reclaim.
The corruption can be seen in crashes, but the root cause is often difficult to ascertain as a crashing machine's stack trace will have no evidence of being near NFS or SUNRPC code. I believe this problem to be much more pervasive than reports to the community may indicate.
Fix this by having kernel users of sockets that may corrupt task_frag due to reclaim set sk_use_task_frag = false. Preemptively correcting this situation for users that still set sk_allocation allows them to convert to memalloc_nofs_save/restore without the same unexpected corruptions that are sure to follow, unlikely to show up in testing, and difficult to bisect.
--- drivers/block/drbd/drbd_receiver.c | 3 +++ drivers/block/nbd.c | 1 + drivers/nvme/host/tcp.c | 1 + drivers/scsi/iscsi_tcp.c | 1 + drivers/usb/usbip/usbip_common.c | 1 + fs/afs/rxrpc.c | 1 + fs/cifs/connect.c | 1 + fs/dlm/lowcomms.c | 2 ++ fs/ocfs2/cluster/tcp.c | 1 + net/9p/trans_fd.c | 1 + net/ceph/messenger.c | 1 + net/sunrpc/xprtsock.c | 3 +++ 12 files changed, 17 insertions(+)
drivers/block/drbd/drbd_receiver.c +3 -0
@@ -1030,6 +1030,9 @@ static int conn_connect(struct drbd_connection *connection) sock.socket->sk->sk_allocation =GFP_NOIO ; msock.socket->sk->sk_allocation =GFP_NOIO ; + sock.socket->sk->sk_use_task_frag = false; + msock.socket->sk->sk_use_task_frag = false; + sock.socket->sk->sk_priority =TC_PRIO_INTERACTIVE_BULK ; msock.socket->sk->sk_priority =TC_PRIO_INTERACTIVE ;
drivers/block/nbd.c +1 -0
@@ -514,6 +514,7 @@ static int sock_xmit(struct nbd_device *nbd, int index, int send, noreclaim_flag =memalloc_noreclaim_save (); do { sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC; + sock->sk->sk_use_task_frag = false; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = NULL;
drivers/nvme/host/tcp.c +1 -0
@@ -1531,6 +1531,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, queue->sock->sk->sk_rcvtimeo =10 *HZ ; queue->sock->sk->sk_allocation =GFP_ATOMIC ; + queue->sock->sk->sk_use_task_frag = false;nvme_tcp_set_queue_io_cpu (queue); queue->request =NULL ; queue->data_remaining =0 ;
drivers/scsi/iscsi_tcp.c +1 -0
@@ -733,6 +733,7 @@ iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session, sk->sk_reuse =SK_CAN_REUSE ; sk->sk_sndtimeo =15 *HZ ;/* FIXME: make it configurable */ sk->sk_allocation =GFP_ATOMIC ; + sk->sk_use_task_frag = false;sk_set_memalloc (sk);sock_no_linger (sk);
drivers/usb/usbip/usbip_common.c +1 -0
@@ -315,6 +315,7 @@ int usbip_recv(struct socket *sock, void *buf, int size) do { sock->sk->sk_allocation = GFP_NOIO; + sock->sk->sk_use_task_frag = false; result = sock_recvmsg(sock, &msg, MSG_WAITALL); if (result <= 0)
fs/afs/rxrpc.c +1 -0
@@ -46,6 +46,7 @@ int afs_open_socket(struct afs_net *net) goto error_1; socket->sk->sk_allocation =GFP_NOFS ; + socket->sk->sk_use_task_frag = false;/* bind the callback manager's address to make this a server socket */ memset (&srx,0 ,sizeof (srx));
fs/cifs/connect.c +1 -0
@@ -2935,6 +2935,7 @@ generic_ip_connect(struct TCP_Server_Info *server)cifs_dbg (FYI ,"Socket created\n" ); server->ssocket = socket; socket->sk->sk_allocation =GFP_NOFS ; + socket->sk->sk_use_task_frag = false;if (sfamily ==AF_INET6 )cifs_reclassify_socket6 (socket);else
fs/dlm/lowcomms.c +2 -0
@@ -699,6 +699,7 @@ static void add_listen_sock(struct socket *sock, struct listen_connection *con) sk->sk_user_data = con; sk->sk_allocation =GFP_NOFS ; + sk->sk_use_task_frag = false;/* Install a data_ready callback */ sk->sk_data_ready = lowcomms_listen_data_ready;release_sock (sk);
@@ -718,6 +719,7 @@ static void add_sock(struct socket *sock, struct connection *con) sk->sk_write_space = lowcomms_write_space; sk->sk_state_change = lowcomms_state_change; sk->sk_allocation =GFP_NOFS ; + sk->sk_use_task_frag = false; sk->sk_error_report = lowcomms_error_report;release_sock (sk); }
fs/ocfs2/cluster/tcp.c +1 -0
@@ -1604,6 +1604,7 @@ static void o2net_start_connect(struct work_struct *work) sc->sc_sock = sock;/* freed by sc_kref_release */ sock->sk->sk_allocation =GFP_ATOMIC ; + sock->sk->sk_use_task_frag = false; myaddr.sin_family =AF_INET ; myaddr.sin_addr.s_addr = mynode->nd_ipv4_address;
net/9p/trans_fd.c +1 -0
@@ -851,6 +851,7 @@ static int p9_socket_open(struct p9_client *client, struct socket *csocket)return -ENOMEM ; csocket->sk->sk_allocation =GFP_NOIO ; + csocket->sk->sk_use_task_frag = false; file =sock_alloc_file (csocket,0 ,NULL );if (IS_ERR (file)) { pr_err("%s (%d): failed to map fd\n",
net/ceph/messenger.c +1 -0
@@ -446,6 +446,7 @@ int ceph_tcp_connect(struct ceph_connection *con)if (ret)return ret; sock->sk->sk_allocation =GFP_NOFS ; + sock->sk->sk_use_task_frag = false;#ifdef CONFIG_LOCKDEP lockdep_set_class (&sock->sk->sk_lock, &socket_class);
net/sunrpc/xprtsock.c +3 -0
@@ -1882,6 +1882,7 @@ static int xs_local_finish_connecting(struct rpc_xprt *xprt, sk->sk_write_space = xs_udp_write_space; sk->sk_state_change = xs_local_state_change; sk->sk_error_report = xs_error_report; + sk->sk_use_task_frag = false;xprt_clear_connected (xprt);
@@ -2083,6 +2084,7 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) sk->sk_user_data = xprt; sk->sk_data_ready = xs_data_ready; sk->sk_write_space = xs_udp_write_space; + sk->sk_use_task_frag = false;xprt_set_connected (xprt);
@@ -2250,6 +2252,7 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) sk->sk_state_change = xs_tcp_state_change; sk->sk_write_space = xs_tcp_write_space; sk->sk_error_report = xs_error_report; + sk->sk_use_task_frag = false;/* socket options */ sock_reset_flag (sk,SOCK_LINGER );
Signature · 1 line
2.31.1
David Howells
maintainerRe: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
Quoted 3 lines · Benjamin Coddington
Since moving to memalloc_nofs_save/restore, SUNRPC has stopped setting the
GFP_NOIOflag onsk_allocationwhich the networking system uses to decide when it is safe to usecurrent->task_frag.
Um, what's task_frag?
David
Re: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
Quoted 7 lines · David Howells
Quoted 3 lines · Benjamin Coddington
Since moving to memalloc_nofs_save/restore, SUNRPC has stopped setting the
GFP_NOIOflag onsk_allocationwhich the networking system uses to decide when it is safe to usecurrent->task_frag.Um, what's
task_frag?
Its a per-task page_frag used to coalesce small writes for networking -- see:
5640f7685831 net: use a per task frag allocator
Ben
Shuah Khan
maintainerRe: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
Quoted 8 lines · Benjamin Coddington
Quoted 6 lines · David Howells
Quoted 3 lines · Benjamin Coddington
Since moving to memalloc_nofs_save/restore, SUNRPC has stopped setting the
GFP_NOIOflag onsk_allocationwhich the networking system uses to decide when it is safe to usecurrent->task_frag.
Quoted 9 lines
Quoted 1 line
Um, what's
task_frag?Its a per-task
page_fragused to coalesce small writes for networking -- see:
5640f7685831net: use a per task frag allocatorBen
I am not seeing this in the mainline. Where can find this commit?
thanks,
-- Shuah
Shuah Khan
maintainerRe: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
Quoted 13 lines · Shuah Khan
Quoted 12 lines · Benjamin Coddington
Quoted 7 lines · David Howells
Quoted 3 lines · Benjamin Coddington
Since moving to memalloc_nofs_save/restore, SUNRPC has stopped setting the
GFP_NOIOflag onsk_allocationwhich the networking system uses to decide when it is safe to usecurrent->task_frag.Um, what's
task_frag?Its a per-task
page_fragused to coalesce small writes for networking -- see:
Quoted 8 lines
Quoted 5 lines
5640f7685831net: use a per task frag allocatorBen
I am not seeing this in the mainline. Where can find this commit?
Okay. I see this commit in the mainline. However, I don't see the sk_use_task_frag in mainline.
thanks,
-- Shuah
Re: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
Quoted 20 lines · Shuah Khan
Quoted 19 lines · Shuah Khan
Quoted 17 lines · Benjamin Coddington
Quoted 7 lines · David Howells
Quoted 3 lines · Benjamin Coddington
Since moving to memalloc_nofs_save/restore, SUNRPC has stopped setting the
GFP_NOIOflag onsk_allocationwhich the networking system uses to decide when it is safe to usecurrent->task_frag.Um, what's
task_frag?Its a per-task
page_fragused to coalesce small writes for networking -- see:
5640f7685831net: use a per task frag allocatorBen
Quoted 5 lines
Quoted 2 lines
I am not seeing this in the mainline. Where can find this commit?
Okay. I see this commit in the mainline. However, I don't see the
sk_use_task_fragin mainline.
sk_use_task_frag is in patch 1/3 in this posting.
https://lore.kernel.org/netdev/26d98c8f-372b-b9c8-c29f-096cddaff149 at linuxfoundation.org/T/#m3271959c4cf8dcff1c0c6ba023b2b3821d9e7e99
Ben
Shuah Khan
maintainerRe: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
Quoted 25 lines · Benjamin Coddington
Quoted 23 lines · Shuah Khan
Quoted 21 lines · Shuah Khan
Quoted 8 lines · Benjamin Coddington
Quoted 6 lines · David Howells
Quoted 3 lines · Benjamin Coddington
Since moving to memalloc_nofs_save/restore, SUNRPC has stopped setting the
GFP_NOIOflag onsk_allocationwhich the networking system uses to decide when it is safe to usecurrent->task_frag.Quoted 9 lines
Quoted 1 line
Um, what's
task_frag?Its a per-task
page_fragused to coalesce small writes for networking -- see:
5640f7685831net: use a per task frag allocatorBen
I am not seeing this in the mainline. Where can find this commit?
Quoted 7 lines
Quoted 2 lines
Okay. I see this commit in the mainline. However, I don't see the
sk_use_task_fragin mainline.
sk_use_task_fragis in patch 1/3 in this posting.https://lore.kernel.org/netdev/26d98c8f-372b-b9c8-c29f-096cddaff149 at linuxfoundation.org/T/#m3271959c4cf8dcff1c0c6ba023b2b3821d9e7e99
Aha. I don't have 1/3 in my Inbox - I think it would make sense to cc people on the first patch so we can understand the premise for the change.
thanks,
-- Shuah
Re: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
Quoted 29 lines · Shuah Khan
Quoted 28 lines · Benjamin Coddington
Quoted 25 lines · Shuah Khan
Quoted 13 lines · Shuah Khan
Quoted 12 lines · Benjamin Coddington
Quoted 7 lines · David Howells
Quoted 3 lines · Benjamin Coddington
Since moving to memalloc_nofs_save/restore, SUNRPC has stopped setting the
GFP_NOIOflag onsk_allocationwhich the networking system uses to decide when it is safe to usecurrent->task_frag.Um, what's
task_frag?Its a per-task
page_fragused to coalesce small writes for networking -- see:Quoted 8 lines
Quoted 5 lines
5640f7685831net: use a per task frag allocatorBen
I am not seeing this in the mainline. Where can find this commit?
Okay. I see this commit in the mainline. However, I don't see the
sk_use_task_fragin mainline.
Quoted 8 lines
Quoted 4 lines
sk_use_task_fragis in patch 1/3 in this posting.https://lore.kernel.org/netdev/26d98c8f-372b-b9c8-c29f-096cddaff149 at linuxfoundation.org/T/#m3271959c4cf8dcff1c0c6ba023b2b3821d9e7e99
Aha. I don't have 1/3 in my Inbox - I think it would make sense to cc people on the first patch so we can understand the premise for the change.
Yeah, I can do that if it goes to another version, I was just trying to be considerate of all the noise this sort of posting generates.
Ben
Christoph Hellwig
maintainerRe: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
In reply to Benjamin CoddingtonHmm. Having to set a flag to not accidentally corrupt per-task state seems a bit fragile. Wouldn't it make sense to find a way to opt into the feature only for sockets created from the syscall layer?
Re: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
Quoted 3 lines · Christoph Hellwig
Hmm. Having to set a flag to not accidentally corrupt per-task state seems a bit fragile. Wouldn't it make sense to find a way to opt into the feature only for sockets created from the syscall layer?
It's totally fragile, and that's why it's currently broken in production. The fragile ship sailed when networking decided to depend on users setting the socket's GFP_ flags correctly to avoid corruption.
Meantime, this problem needs fixing in a way that makes everyone happy. This fix doesn't make it less fragile, but it may (hopefully) address the previous criticisms enough that something gets done to fix it.
Ben
Re: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
Quoted 6 lines · Benjamin Coddington
Quoted 3 lines · Christoph Hellwig
Hmm. Having to set a flag to not accidentally corrupt per-task state seems a bit fragile. Wouldn't it make sense to find a way to opt into the feature only for sockets created from the syscall layer?
Quoted 7 lines
It's totally fragile, and that's why it's currently broken in production. The fragile ship sailed when networking decided to depend on users setting the socket's
GFP_flags correctly to avoid corruption.Meantime, this problem needs fixing in a way that makes everyone happy. This fix doesn't make it less fragile, but it may (hopefully) address the previous criticisms enough that something gets done to fix it.
Also, let's remember that while we're discussing how the kernel sould work in an ideal world, the reality is that production NFS systems crash randomly upon memory reclaim since commit a1231fda7e94 ("SUNRPC:
Set memalloc_nofs_save() on all rpciod/xprtiod jobs"). Fixing that is just a matter of re-introducing GFP_NOFS on SUNRPC sockets (which has been proposed several times already). Then we'll have plenty of time to argue about how networking should use the per-task page_frag and how to remove GFP_NOFS in the long term.
Jeff Layton
maintainerRe: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
In reply to Christoph HellwigQuoted 3 lines · Christoph Hellwig
Hmm. Having to set a flag to not accidentally corrupt per-task state seems a bit fragile. Wouldn't it make sense to find a way to opt into the feature only for sockets created from the syscall layer?
I agree that that would be cleaner. task_frag should have been an opt-in thing all along. That change regressed all of the in-kernel users of sockets.
Where would be the right place to set that flag for only userland sockets? A lot of the in-kernel socket users hook into the socket API at a fairly high-level. 9P and CIFS, for instance, call __sock_create.
We could set it in the syscall handlers (and maybe in iouring) I suppose, but that seems like the wrong thing to do too.
In the absence of a clean place to do this, I think we're going to be stuck doing it the way Ben has proposed...
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
In reply to Christoph HellwigQuoted 3 lines · Christoph Hellwig
Hmm. Having to set a flag to not accidentally corrupt per-task state seems a bit fragile. Wouldn't it make sense to find a way to opt into the feature only for sockets created from the syscall layer?
That's something I originally considered. But, as far as I can see, nbd needs this flag _and_ uses sockets created in user space. So it'd still need to opt out manually.
Paolo Abeni
maintainerRe: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
In reply to Benjamin CoddingtonQuoted 72 lines · Benjamin Coddington
Since moving to memalloc_nofs_save/restore, SUNRPC has stopped setting the
GFP_NOIOflag onsk_allocationwhich the networking system uses to decide when it is safe to usecurrent->task_frag. The results of this are unexpected corruption intask_fragwhen SUNRPC is involved in memory reclaim.The corruption can be seen in crashes, but the root cause is often difficult to ascertain as a crashing machine's stack trace will have no evidence of being near NFS or SUNRPC code. I believe this problem to be much more pervasive than reports to the community may indicate.
Fix this by having kernel users of sockets that may corrupt
task_fragdue to reclaim setsk_use_task_frag= false. Preemptively correcting this situation for users that still setsk_allocationallows them to convert to memalloc_nofs_save/restore without the same unexpected corruptions that are sure to follow, unlikely to show up in testing, and difficult to bisect.CC Philipp Reisner <[email protected]>CC Lars Ellenberg <[email protected]>CC "Christoph B?hmwalder" <[email protected]>CC Jens Axboe <[email protected]>CC Josef Bacik <[email protected]>CC Keith Busch <[email protected]>CC Christoph Hellwig <[email protected]>CC Sagi Grimberg <[email protected]>CC Lee Duncan <[email protected]>CC Chris Leech <[email protected]>CC Mike Christie <[email protected]>CC "James E.J. Bottomley" <[email protected]>CC "Martin K. Petersen" <[email protected]>CC Valentina Manea <[email protected]>CC Shuah Khan <[email protected]>CC Greg Kroah-Hartman <[email protected]>CC David Howells <[email protected]>CC Marc Dionne <[email protected]>CC Steve French <[email protected]>CC Christine Caulfield <[email protected]>CC David Teigland <[email protected]>CC Mark Fasheh <[email protected]>CC Joel Becker <[email protected]>CC Joseph Qi <[email protected]>CC Eric Van Hensbergen <[email protected]>CC Latchesar Ionkov <[email protected]>CC Dominique Martinet <[email protected]>CC "David S. Miller" <[email protected]>CC Eric Dumazet <[email protected]>CC Jakub Kicinski <[email protected]>CC Paolo Abeni <[email protected]>CC Ilya Dryomov <[email protected]>CC Xiubo Li <[email protected]>CC Chuck Lever <[email protected]>CC Jeff Layton <[email protected]>CC Trond Myklebust <[email protected]>CC Anna Schumaker <[email protected]>CC drbd-dev at lists.linbit.comCC linux-block at vger.kernel.orgCC linux-kernel at vger.kernel.orgCC nbd at other.debian.orgCC linux-nvme at lists.infradead.orgCC open-iscsi at googlegroups.comCC linux-scsi at vger.kernel.orgCC linux-usb at vger.kernel.orgCC linux-afs at lists.infradead.orgCC linux-cifs at vger.kernel.orgCC samba-technical at lists.samba.orgCC cluster-devel at redhat.comCC ocfs2-devel at oss.oracle.comCC v9fs-developer at lists.sourceforge.netCC netdev at vger.kernel.orgCC ceph-devel at vger.kernel.orgCC linux-nfs at vger.kernel.org
Quoted 2 lines
Suggested-by Guillaume Nault <[email protected]>Signed-off-by Benjamin Coddington <[email protected]>
I think this is the most feasible way out of the existing issue, and I think this patchset should go via the networking tree, targeting the Linux 6.2.
If someone has disagreement with the above, please speak!
Thanks,
Paolo
Jakub Kicinski
maintainerRe: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
Quoted 3 lines · On Fri, 09 Dec 2022 13:37:08 +0100 Paolo Abeni wrote:
I think this is the most feasible way out of the existing issue, and I think this patchset should go via the networking tree, targeting the Linux 6.2.
FWIW some fields had been moved so this will not longer apply cleanly, see b534dc46c8ae016. But I think we can apply it to net since the merge window is upon us? Just a heads up.
Paolo Abeni
maintainerRe: [PATCH v1 2/3] Treewide: Stop corrupting socket's task_frag
Quoted 8 lines · Jakub Kicinski
Quoted 3 lines · On Fri, 09 Dec 2022 13:37:08 +0100 Paolo Abeni wrote:
I think this is the most feasible way out of the existing issue, and I think this patchset should go via the networking tree, targeting the Linux 6.2.
FWIW some fields had been moved so this will not longer apply cleanly, see
b534dc46c8ae016. But I think we can apply it to net since the merge window is upon us? Just a heads up.
We will need an additional revision, see my reply to patch 3/3. I think the -net tree should be the appropriate target.
Thanks,
Paolo
PATCH 3/3 net: simplify sk_page_frag
In reply to Benjamin CoddingtonNow that in-kernel socket users that may recurse during reclaim have benn converted to sk_use_task_frag = false, we can have sk_page_frag() simply check that value.
--- include/net/sock.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-)
include/net/sock.h +2 -7
@@ -2539,19 +2539,14 @@ static inline void sk_stream_moderate_sndbuf(struct sock *sk)* Both direct reclaim and page faults can nest inside other * socket operations and end up recursing into sk_page_frag() * while it's already in use: explicitly avoid task page_frag -* usage if the caller is potentially doing any of them. -* This assumes that page fault handlers use the GFP_NOFS flags or -* explicitly disable sk_use_task_frag. +* when users disable sk_use_task_frag. * * Return: a per task page_frag if context allows that, * otherwise a per socket one. */ static inline struct page_frag *sk_page_frag (struct sock *sk) { -if (sk->sk_use_task_frag && - (sk->sk_allocation & (__GFP_DIRECT_RECLAIM | __GFP_MEMALLOC | - __GFP_FS)) == - (__GFP_DIRECT_RECLAIM | __GFP_FS)) +if (sk->sk_use_task_frag)return ¤t->task_frag;return &sk->sk_frag;
Signature · 1 line
2.31.1
Paolo Abeni
maintainerRe: [PATCH v1 3/3] net: simplify sk_page_frag
Quoted 25 lines · Benjamin Coddington
Now that in-kernel socket users that may recurse during reclaim have benn converted to
sk_use_task_frag= false, we can havesk_page_frag()simply check that value.Signed-off-by Benjamin Coddington <[email protected]>--- include/net/sock.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-)include/net/sock.h +1
-3@@ -2539,19 +2539,14 @@ static inline void sk_stream_moderate_sndbuf(struct sock *sk)* Both direct reclaim and page faults can nest inside other * socket operations and end up recursing into sk_page_frag() * while it's already in use: explicitly avoid task page_frag -* usage if the caller is potentially doing any of them. -* This assumes that page fault handlers use the GFP_NOFS flags or -* explicitly disable sk_use_task_frag. +* when users disable sk_use_task_frag. * * Return: a per task page_frag if context allows that, * otherwise a per socket one. */
include/net/sock.h static inline void sk_stream_moderate_sndbuf(struct sock *sk) · line 2552
Quoted 10 lines
include/net/sock.h +1
-4static inline struct page_frag *sk_page_frag (struct sock *sk) { -if (sk->sk_use_task_frag && - (sk->sk_allocation & (__GFP_DIRECT_RECLAIM | __GFP_MEMALLOC | - __GFP_FS)) == - (__GFP_DIRECT_RECLAIM | __GFP_FS)) +if (sk->sk_use_task_frag)return ¤t->task_frag;return &sk->sk_frag;
To make the above as safe as possible I think we should double-check the in-kernel users explicitly setting sk_allocation to GFP_ATOMIC, as that has the side effect of disabling the task_frag usage, too.
Patch 2/3 already catches some of such users, and we can safely leave alone few others, (specifically l2tp, fou and inet_ctl_sock_create()).
Even wireguard and tls looks safe IMHO.
So the only left-over should be espintcp, I suggest updating patch 2/3 clearing sk_use_task_frag even in espintcp_init_sk().
Other than that LGTM.
Cheers,
Paolo
Paolo Abeni
maintainerRe: [PATCH v1 3/3] net: simplify sk_page_frag
In reply to Benjamin CoddingtonQuoted 25 lines · Benjamin Coddington
Now that in-kernel socket users that may recurse during reclaim have benn converted to
sk_use_task_frag= false, we can havesk_page_frag()simply check that value.Signed-off-by Benjamin Coddington <[email protected]>--- include/net/sock.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-)include/net/sock.h +1
-3@@ -2539,19 +2539,14 @@ static inline void sk_stream_moderate_sndbuf(struct sock *sk)* Both direct reclaim and page faults can nest inside other * socket operations and end up recursing into sk_page_frag() * while it's already in use: explicitly avoid task page_frag -* usage if the caller is potentially doing any of them. -* This assumes that page fault handlers use the GFP_NOFS flags or -* explicitly disable sk_use_task_frag. +* when users disable sk_use_task_frag. * * Return: a per task page_frag if context allows that, * otherwise a per socket one. */
include/net/sock.h static inline void sk_stream_moderate_sndbuf(struct sock *sk) · line 2552
Quoted 10 lines
include/net/sock.h +1
-4static inline struct page_frag *sk_page_frag (struct sock *sk) { -if (sk->sk_use_task_frag && - (sk->sk_allocation & (__GFP_DIRECT_RECLAIM | __GFP_MEMALLOC | - __GFP_FS)) == - (__GFP_DIRECT_RECLAIM | __GFP_FS)) +if (sk->sk_use_task_frag)return ¤t->task_frag;return &sk->sk_frag;
To make the above as safe as possible I think we should double-check the in-kernel users explicitly setting sk_allocation to GFP_ATOMIC, as that has the side effect of disabling the task_frag usage, too.
Patch 2/3 already catches some of such users, and we can safely leave alone few others, (specifically l2tp, fou and inet_ctl_sock_create()).
Even wireguard and tls looks safe IMHO.
So the only left-over should be espintcp, I suggest updating patch 2/3 clearing sk_use_task_frag even in espintcp_init_sk().
Other than that LGTM.
Cheers,
Paolo
Paolo Abeni
maintainerRe: [PATCH v1 3/3] net: simplify sk_page_frag
In reply to Benjamin CoddingtonQuoted 25 lines · Benjamin Coddington
Now that in-kernel socket users that may recurse during reclaim have benn converted to
sk_use_task_frag= false, we can havesk_page_frag()simply check that value.Signed-off-by Benjamin Coddington <[email protected]>--- include/net/sock.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-)include/net/sock.h +1
-3@@ -2539,19 +2539,14 @@ static inline void sk_stream_moderate_sndbuf(struct sock *sk)* Both direct reclaim and page faults can nest inside other * socket operations and end up recursing into sk_page_frag() * while it's already in use: explicitly avoid task page_frag -* usage if the caller is potentially doing any of them. -* This assumes that page fault handlers use the GFP_NOFS flags or -* explicitly disable sk_use_task_frag. +* when users disable sk_use_task_frag. * * Return: a per task page_frag if context allows that, * otherwise a per socket one. */
include/net/sock.h static inline void sk_stream_moderate_sndbuf(struct sock *sk) · line 2552
Quoted 10 lines
include/net/sock.h +1
-4static inline struct page_frag *sk_page_frag (struct sock *sk) { -if (sk->sk_use_task_frag && - (sk->sk_allocation & (__GFP_DIRECT_RECLAIM | __GFP_MEMALLOC | - __GFP_FS)) == - (__GFP_DIRECT_RECLAIM | __GFP_FS)) +if (sk->sk_use_task_frag)return ¤t->task_frag;return &sk->sk_frag;
To make the above as safe as possible I think we should double-check the in-kernel users explicitly setting sk_allocation to GFP_ATOMIC, as that has the side effect of disabling the task_frag usage, too.
Patch 2/3 already catches some of such users, and we can safely leave alone few others, (specifically l2tp, fou and inet_ctl_sock_create()).
Even wireguard and tls looks safe IMHO.
So the only left-over should be espintcp, I suggest updating patch 2/3 clearing sk_use_task_frag even in espintcp_init_sk().
Other than that LGTM.
Cheers,
Paolo
Hi Dave, Eric, Jakub, Paolo,
I think it makes sense for all three of these to go together through netdev. If you agree, would you like me to chase down individual ACKs for each treewide touch?
What can I do from netdev's perspective to move this forward?
Ben
Quoted 48 lines · Benjamin Coddington
The networking code uses flags in
sk_allocationto determine if it can usecurrent->task_frag, however in-kernel users of sockets may stop settingsk_allocationwhen they convert to the preferred memalloc_nofs_save/restore, as SUNRPC has done in commita1231fda7e94("SUNRPC: Setmemalloc_nofs_save()on all rpciod/xprtiod jobs").This will cause corruption in
current->task_fragwhen recursing into the network layer for those subsystems during page fault or reclaim. The corruption is difficult to diagnose because stack traces may not contain the offending subsystem at all. The corruption is unlikely to show up in testing because it requires memory pressure, and so subsystems that convert to memalloc_nofs_save/restore are likely to continue to run into this issue.Previous reports and proposed fixes:
https://lore.kernel.org/netdev/96a18bd00cbc6cb554603cc0d6ef1c551965b078.1663762494.git.gnault@redhat.com/
https://lore.kernel.org/netdev/b4d8cb09c913d3e34f853736f3f5628abfd7f4b6.1656699567.git.gnault@redhat.com/
https://lore.kernel.org/linux-nfs/de6d99321d1dcaa2ad456b92b3680aa77c07a747.1665401788.git.gnault@redhat.com/Guilluame Nault has done all of the hard work tracking this problem down and finding the best fix for this issue. I'm just taking a turn posting another fix.
Benjamin Coddington (2):
Treewide: Stop corrupting socket'stask_frag
net: simplifysk_page_fragGuillaume Nault (1):
net: Introducesk_use_task_fragin struct sock.drivers/block/drbd/drbd_receiver.c | 3 +++ drivers/block/nbd.c | 1 + drivers/nvme/host/tcp.c | 1 + drivers/scsi/iscsi_tcp.c | 1 + drivers/usb/usbip/usbip_common.c | 1 + fs/afs/rxrpc.c | 1 + fs/cifs/connect.c | 1 + fs/dlm/lowcomms.c | 2 ++ fs/ocfs2/cluster/tcp.c | 1 + include/net/sock.h | 10 ++++++---- net/9p/trans_fd.c | 1 + net/ceph/messenger.c | 1 + net/core/sock.c | 1 + net/sunrpc/xprtsock.c | 3 +++ 14 files changed, 24 insertions(+), 4 deletions(-)Signature · 1 line
2.31.1