FAILED: patch "[PATCH] net/packet: defer vmalloc TX_RING free until skbs finish" failed to apply to 5.15-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x 992cc9f94ca924089a506ba9b327caa9af797529
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026083141-unequal-agreement-bb76@gregkh' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 992cc9f94ca924089a506ba9b327caa9af797529 Mon Sep 17 00:00:00 2001
From: Kyle Zeng <[email protected]>
Date: Sun, 16 Aug 2026 16:56:46 -0700
Subject: [PATCH] net/packet: defer vmalloc TX_RING free until skbs finish
AF_PACKET TX_RING skbs keep a raw pointer to their ring frame. The skb page references preserve page-backed ring blocks after pg_vec is freed, but they do not preserve a vmalloc mapping.
tpacket_destruct_skb() currently drops the pending reference before writing the timestamp and TP_STATUS_AVAILABLE to the frame. Move the decrement after those stores. The smp_wmb() in __packet_set_status() orders the frame stores before the decrement.
Also recheck pending TX frames under pg_vec_lock before non-closing ring replacement, so a racing send cannot add a pending skb between the initial check and the ring swap.
Ring allocation can produce a mixture of page-backed and vmalloc-backed blocks. Allocate deferred-work storage during TX ring setup when the first vmalloc-backed block is encountered, and keep its pointer in the pg_vec allocation header. If allocation fails, return -ENOMEM from ring setup. On socket close, a non-NULL pointer identifies a vmalloc-backed vector without a scan. If TX skbs remain, defer the whole vector to system_long_wq.
After pg_vec is detached, a late destructor can skip the pending decrement. Use socket write-memory accounting as the deferred lifetime gate instead: an skb remains charged through its final sock_wfree(), after all ring-frame accesses. The delayed work retains a socket reference and reschedules itself until no TX skbs remain.
Move pending_refcnt release to packet_sock_destruct() so late skb destructors and deferred cleanup can safely use it after packet_release(). Page-backed teardown remains synchronous, and no lock is added to the TX completion hot path.
net/packet/af_packet.c +87 -9
@@ -88,6 +88,7 @@#include <linux/errqueue.h> #include <linux/net_tstamp.h> #include <linux/percpu.h> +#include <linux/workqueue.h> #ifdef CONFIG_INET #include <net/inet_common.h> #endif
@@ -1341,6 +1342,8 @@ static void packet_sock_destruct(struct sock *sk)WARN_ON (atomic_read (&sk->sk_rmem_alloc));WARN_ON (refcount_read (&sk->sk_wmem_alloc)); +packet_free_pending (pkt_sk (sk)); +if (!sock_flag (sk,SOCK_DEAD )) {pr_err ("Attempt to release alive packet socket: %p\n" , sk);return ;
@@ -2534,11 +2537,11 @@ static void tpacket_destruct_skb(struct sk_buff *skb)__u32 ts; ph =skb_zcopy_get_nouarg (skb); -packet_dec_pending (&po->tx_ring); ts =__packet_set_timestamp (po, ph, skb);__packet_set_status (po, ph,TP_STATUS_AVAILABLE | ts); +packet_dec_pending (&po->tx_ring);complete (&po->skb_completion); }
@@ -3207,7 +3210,6 @@ static int packet_release(struct socket *sock)/* Purge queues */ skb_queue_purge (&sk->sk_receive_queue); -packet_free_pending (po);sock_put (sk);return 0 ;
@@ -4370,11 +4372,26 @@ static const struct vm_operations_struct packet_mmap_ops = { .close = packet_mm_close, }; +struct packet_pg_vec { +struct packet_pg_vec_free *deferred; +unsigned int order; +unsigned int len; + struct pgv pg_vec[] __counted_by(len); +}; + +struct packet_pg_vec_free { +struct delayed_work work; +struct sock *sk; +struct packet_pg_vec *vec; +}; +static void free_pg_vec (struct pgv *pg_vec,unsigned int order,unsigned int len) { +struct packet_pg_vec *vec;int i; + vec = container_of_const(pg_vec, struct packet_pg_vec, pg_vec[0]);for (i =0 ; i < len; i++) {if (likely (pg_vec[i].buffer)) {if (is_vmalloc_addr (pg_vec[i].buffer))
@@ -4385,7 +4402,46 @@ static void free_pg_vec(struct pgv *pg_vec, unsigned int order, pg_vec[i].buffer =NULL ; } } -kfree (pg_vec); +kfree (vec->deferred); +kfree (vec); +} + +static void packet_free_pg_vec_work (struct work_struct *work) +{ +struct packet_pg_vec_free *deferred; +struct packet_pg_vec *vec; +struct sock *sk; + + deferred =container_of_const (to_delayed_work (work), + struct packet_pg_vec_free, work); + vec = deferred->vec; + sk = deferred->sk; +if (sk_wmem_alloc_get (sk)) { +queue_delayed_work (system_long_wq, &deferred->work,1 ); +return ; + } + +free_pg_vec (vec->pg_vec, vec->order, vec->len); +sock_put (sk); +} + +static void packet_free_tx_ring (struct sock *sk,struct pgv *pg_vec, +unsigned int order,unsigned int len) +{ +struct packet_pg_vec_free *deferred; +struct packet_pg_vec *vec; + + vec = container_of_const(pg_vec, struct packet_pg_vec, pg_vec[0]); + deferred = vec->deferred; +if (!deferred || !sk_wmem_alloc_get (sk)) { +free_pg_vec (pg_vec, order, len); +return ; + } + +/* A detached ring's pending count can miss late skb destructors. */ + deferred->sk = sk; +sock_hold (sk); +queue_delayed_work (system_long_wq, &deferred->work,0 ); }static char *alloc_one_pg_vec_page (unsigned long order)
@@ -4413,20 +4469,35 @@ static char *alloc_one_pg_vec_page(unsigned long order)return NULL ; } -static struct pgv *alloc_pg_vec (struct tpacket_req *req,int order) +static struct pgv *alloc_pg_vec (struct tpacket_req *req,int order,bool tx_ring) {unsigned int block_nr = req->tp_block_nr; +struct packet_pg_vec *vec;struct pgv *pg_vec;int i; - pg_vec = kzalloc_objs(struct pgv, block_nr, GFP_KERNEL | __GFP_NOWARN); -if (unlikely (!pg_vec)) - goto out; + vec =kzalloc_flex (*vec, pg_vec, block_nr,GFP_KERNEL | __GFP_NOWARN); +if (unlikely (!vec)) +return NULL ; + vec->order = order; + vec->len = block_nr; + pg_vec = vec->pg_vec;for (i =0 ; i < block_nr; i++) { pg_vec[i].buffer =alloc_one_pg_vec_page (order);if (unlikely (!pg_vec[i].buffer)) goto out_free_pgvec; + +if (tx_ring && !vec->deferred && +is_vmalloc_addr (pg_vec[i].buffer)) { + vec->deferred =kzalloc_obj (*vec->deferred, +GFP_KERNEL | __GFP_NOWARN); +if (!vec->deferred) + goto out_free_pgvec; + vec->deferred->vec = vec; +INIT_DELAYED_WORK (&vec->deferred->work, + packet_free_pg_vec_work); + } } out:
@@ -4509,7 +4580,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, err = -ENOMEM ; order =get_order (req->tp_block_size); - pg_vec =alloc_pg_vec (req, order); + pg_vec =alloc_pg_vec (req, order, tx_ring);if (unlikely (!pg_vec)) goto out;switch (po->tp_version) {
@@ -4561,6 +4632,9 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, err = -EBUSY ;mutex_lock (&po->pg_vec_lock);if (closing ||atomic_long_read (&po->mapped) ==0 ) { +if (tx_ring && !closing &&packet_read_pending (rb)) + goto out_unlock; + err =0 ;spin_lock_bh (&rb_queue->lock);swap (rb->pg_vec, pg_vec);
@@ -4582,6 +4656,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,pr_err ("packet_mmap: vma is busy: %ld\n" ,atomic_long_read (&po->mapped)); } +out_unlock:mutex_unlock (&po->pg_vec_lock);spin_lock (&po->bind_lock);
@@ -4603,7 +4678,10 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, out_free_pg_vec:if (pg_vec) {bitmap_free (rx_owner_map); -free_pg_vec (pg_vec, order, req->tp_block_nr); +if (tx_ring && closing) +packet_free_tx_ring (sk, pg_vec, order, req->tp_block_nr); +else +free_pg_vec (pg_vec, order, req->tp_block_nr); } out:return err;
Sasha Levin
maintainerPATCH 1/1 net/packet: defer vmalloc TX_RING free until skbs finish
From: Kyle Zeng <[email protected]>
[ Upstream commit 992cc9f94ca924089a506ba9b327caa9af797529 ]
AF_PACKET TX_RING skbs keep a raw pointer to their ring frame. The skb page references preserve page-backed ring blocks after pg_vec is freed, but they do not preserve a vmalloc mapping.
tpacket_destruct_skb() currently drops the pending reference before writing the timestamp and TP_STATUS_AVAILABLE to the frame. Move the decrement after those stores. The smp_wmb() in __packet_set_status() orders the frame stores before the decrement.
Also recheck pending TX frames under pg_vec_lock before non-closing ring replacement, so a racing send cannot add a pending skb between the initial check and the ring swap.
Ring allocation can produce a mixture of page-backed and vmalloc-backed blocks. Allocate deferred-work storage during TX ring setup when the first vmalloc-backed block is encountered, and keep its pointer in the pg_vec allocation header. If allocation fails, return -ENOMEM from ring setup. On socket close, a non-NULL pointer identifies a vmalloc-backed vector without a scan. If TX skbs remain, defer the whole vector to system_long_wq.
After pg_vec is detached, a late destructor can skip the pending decrement. Use socket write-memory accounting as the deferred lifetime gate instead: an skb remains charged through its final sock_wfree(), after all ring-frame accesses. The delayed work retains a socket reference and reschedules itself until no TX skbs remain.
Move pending_refcnt release to packet_sock_destruct() so late skb destructors and deferred cleanup can safely use it after packet_release(). Page-backed teardown remains synchronous, and no lock is added to the TX completion hot path.
--- net/packet/af_packet.c | 97 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 9 deletions(-)
net/packet/af_packet.c +88 -9
@@ -86,6 +86,7 @@#include <linux/errqueue.h> #include <linux/net_tstamp.h> #include <linux/percpu.h> +#include <linux/workqueue.h> #ifdef CONFIG_INET #include <net/inet_common.h> #endif
@@ -1359,6 +1360,8 @@ static void packet_sock_destruct(struct sock *sk)WARN_ON (atomic_read (&sk->sk_rmem_alloc));WARN_ON (refcount_read (&sk->sk_wmem_alloc)); +packet_free_pending (pkt_sk (sk)); +if (!sock_flag (sk,SOCK_DEAD )) {pr_err ("Attempt to release alive packet socket: %p\n" , sk);return ;
@@ -2555,11 +2558,11 @@ static void tpacket_destruct_skb(struct sk_buff *skb)__u32 ts; ph =skb_zcopy_get_nouarg (skb); -packet_dec_pending (&po->tx_ring); ts =__packet_set_timestamp (po, ph, skb);__packet_set_status (po, ph,TP_STATUS_AVAILABLE | ts); +packet_dec_pending (&po->tx_ring);complete (&po->skb_completion); }
@@ -3218,7 +3221,6 @@ static int packet_release(struct socket *sock)/* Purge queues */ skb_queue_purge (&sk->sk_receive_queue); -packet_free_pending (po);sk_refcnt_debug_release (sk);sock_put (sk);
@@ -4357,11 +4359,26 @@ static const struct vm_operations_struct packet_mmap_ops = { .close = packet_mm_close, }; +struct packet_pg_vec { +struct packet_pg_vec_free *deferred; +unsigned int order; +unsigned int len; +struct pgv pg_vec[]; +}; + +struct packet_pg_vec_free { +struct delayed_work work; +struct sock *sk; +struct packet_pg_vec *vec; +}; +static void free_pg_vec (struct pgv *pg_vec,unsigned int order,unsigned int len) { +struct packet_pg_vec *vec;int i; + vec = container_of(pg_vec, struct packet_pg_vec, pg_vec[0]);for (i =0 ; i < len; i++) {if (likely (pg_vec[i].buffer)) {if (is_vmalloc_addr (pg_vec[i].buffer))
@@ -4372,7 +4389,46 @@ static void free_pg_vec(struct pgv *pg_vec, unsigned int order, pg_vec[i].buffer =NULL ; } } -kfree (pg_vec); +kfree (vec->deferred); +kfree (vec); +} + +static void packet_free_pg_vec_work (struct work_struct *work) +{ +struct packet_pg_vec_free *deferred; +struct packet_pg_vec *vec; +struct sock *sk; + + deferred =container_of (to_delayed_work (work), + struct packet_pg_vec_free, work); + vec = deferred->vec; + sk = deferred->sk; +if (sk_wmem_alloc_get (sk)) { +queue_delayed_work (system_long_wq, &deferred->work,1 ); +return ; + } + +free_pg_vec (vec->pg_vec, vec->order, vec->len); +sock_put (sk); +} + +static void packet_free_tx_ring (struct sock *sk,struct pgv *pg_vec, +unsigned int order,unsigned int len) +{ +struct packet_pg_vec_free *deferred; +struct packet_pg_vec *vec; + + vec = container_of(pg_vec, struct packet_pg_vec, pg_vec[0]); + deferred = vec->deferred; +if (!deferred || !sk_wmem_alloc_get (sk)) { +free_pg_vec (pg_vec, order, len); +return ; + } + +/* A detached ring's pending count can miss late skb destructors. */ + deferred->sk = sk; +sock_hold (sk); +queue_delayed_work (system_long_wq, &deferred->work,0 ); }static char *alloc_one_pg_vec_page (unsigned long order)
@@ -4400,20 +4456,36 @@ static char *alloc_one_pg_vec_page(unsigned long order)return NULL ; } -static struct pgv *alloc_pg_vec (struct tpacket_req *req,int order) +static struct pgv *alloc_pg_vec (struct tpacket_req *req,int order,bool tx_ring) {unsigned int block_nr = req->tp_block_nr; +struct packet_pg_vec *vec;struct pgv *pg_vec;int i; - pg_vec =kcalloc (block_nr,sizeof (struct pgv ),GFP_KERNEL | __GFP_NOWARN); -if (unlikely (!pg_vec)) - goto out; + vec =kzalloc (struct_size (vec, pg_vec, block_nr), +GFP_KERNEL | __GFP_NOWARN); +if (unlikely (!vec)) +return NULL ; + vec->order = order; + vec->len = block_nr; + pg_vec = vec->pg_vec;for (i =0 ; i < block_nr; i++) { pg_vec[i].buffer =alloc_one_pg_vec_page (order);if (unlikely (!pg_vec[i].buffer)) goto out_free_pgvec; + +if (tx_ring && !vec->deferred && +is_vmalloc_addr (pg_vec[i].buffer)) { + vec->deferred =kzalloc (sizeof (*vec->deferred), +GFP_KERNEL | __GFP_NOWARN); +if (!vec->deferred) + goto out_free_pgvec; + vec->deferred->vec = vec; +INIT_DELAYED_WORK (&vec->deferred->work, + packet_free_pg_vec_work); + } } out:
@@ -4496,7 +4568,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, err = -ENOMEM ; order =get_order (req->tp_block_size); - pg_vec =alloc_pg_vec (req, order); + pg_vec =alloc_pg_vec (req, order, tx_ring);if (unlikely (!pg_vec)) goto out;switch (po->tp_version) {
@@ -4548,6 +4620,9 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, err = -EBUSY ;mutex_lock (&po->pg_vec_lock);if (closing ||atomic_long_read (&po->mapped) ==0 ) { +if (tx_ring && !closing &&packet_read_pending (rb)) + goto out_unlock; + err =0 ;spin_lock_bh (&rb_queue->lock);swap (rb->pg_vec, pg_vec);
@@ -4569,6 +4644,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,pr_err ("packet_mmap: vma is busy: %ld\n" ,atomic_long_read (&po->mapped)); } +out_unlock:mutex_unlock (&po->pg_vec_lock);spin_lock (&po->bind_lock);
@@ -4590,7 +4666,10 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, out_free_pg_vec:if (pg_vec) {bitmap_free (rx_owner_map); -free_pg_vec (pg_vec, order, req->tp_block_nr); +if (tx_ring && closing) +packet_free_tx_ring (sk, pg_vec, order, req->tp_block_nr); +else +free_pg_vec (pg_vec, order, req->tp_block_nr); } out:return err;
Signature · 1 line
2.53.0