[RFC net 2/2] af_packet: add KUnit coverage for asynchronous V3 retirement
flat view
DORMANTno replies
From: Jingxuan Xie <hidden>
Date: 2026-10-28 07:06:33
Subsystem:
networking [general], packet sockets, the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Willem de Bruijn, Linus Torvalds
From: jingxuanxie <redacted> Exercise the actual private reservation, completion, retirement, timer, poll and ring lifecycle helpers from af_packet.c. Tests are built only with PACKET_KUNIT_TEST and do not export production test interfaces or require external packet traffic. Cover unfinished A with writable B, ordered publication after B finishes first, completion without subsequent traffic, both seal/copy orderings, multiple copiers, empty timeouts, single/two-block rings, exact fits, frozen USER/SEALED blocks, reuse after userspace returns a block, bounded publication and worker shutdown. Also cover invalid and valid virtio headers, V2 fallback skb cleanup, concurrent polling during ring creation/teardown and 65536/65537-block cursor wraparound. The packet-prb suite passes all 17 cases on QEMU ARM64 with four CPUs, PREEMPT_RT and lockdep. The same guest passes psock_tpacket and psock_fanout. Assisted-by: LLM Signed-off-by: Jingxuan Xie <redacted> --- net/packet/.kunitconfig | 10 + net/packet/Kconfig | 9 + net/packet/af_packet.c | 4 + net/packet/af_packet_test.c | 649 ++++++++++++++++++++++++++++++++++++ 4 files changed, 672 insertions(+) create mode 100644 net/packet/.kunitconfig create mode 100644 net/packet/af_packet_test.c
diff --git a/net/packet/.kunitconfig b/net/packet/.kunitconfig
new file mode 100644
index 000000000000..baed7c14b1d7
--- /dev/null
+++ b/net/packet/.kunitconfig@@ -0,0 +1,10 @@ +CONFIG_KUNIT=y +CONFIG_KUNIT_AUTORUN_ENABLED=y +CONFIG_NET=y +CONFIG_INET=y +CONFIG_PACKET=y +CONFIG_PACKET_KUNIT_TEST=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_LOCK_ALLOC=y +CONFIG_PROVE_LOCKING=y
diff --git a/net/packet/Kconfig b/net/packet/Kconfig
index 2997382d597c..9f7190ff31df 100644
--- a/net/packet/Kconfig
+++ b/net/packet/Kconfig@@ -23,3 +23,12 @@ config PACKET_DIAG help Support for PF_PACKET sockets monitoring interface used by the ss tool. If unsure, say Y. + +config PACKET_KUNIT_TEST + bool "KUnit tests for TPACKET_V3 block retirement" if !KUNIT_ALL_TESTS + depends on KUNIT=y && PACKET=y + default KUNIT_ALL_TESTS + help + Test packet ring reservation, block retirement and ordered publication. + These tests do not register a network device or require external traffic. + If unsure, say N.
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index b48982059a76..b8cb477eb754 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c@@ -5037,6 +5037,10 @@ static int __init packet_init(void) module_init(packet_init); module_exit(packet_exit); + +#if IS_ENABLED(CONFIG_PACKET_KUNIT_TEST) +#include "af_packet_test.c" +#endif MODULE_DESCRIPTION("Packet socket support (AF_PACKET)"); MODULE_LICENSE("GPL"); MODULE_ALIAS_NETPROTO(PF_PACKET);
diff --git a/net/packet/af_packet_test.c b/net/packet/af_packet_test.c
new file mode 100644
index 000000000000..ecb758e11ff6
--- /dev/null
+++ b/net/packet/af_packet_test.c@@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Included by af_packet.c so tests exercise the actual private helpers. */ +#include <kunit/test.h> +#include <linux/etherdevice.h> +#include <linux/kthread.h> + +struct prb_test_context { + struct packet_sock *po; + struct socket socket; + struct net_device *dev; + struct pgv *blocks; + unsigned int notifications; +}; + +static void prb_test_data_ready(struct sock *sk) +{ + struct prb_test_context *ctx = sk->sk_user_data; + + ctx->notifications++; +} + +static int prb_test_init(struct kunit *test) +{ + struct prb_test_context *ctx; + union tpacket_req_u req = {}; + unsigned int i; + int err; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + test->priv = ctx; + ctx->po = kunit_kzalloc(test, sizeof(*ctx->po), GFP_KERNEL); + ctx->blocks = kunit_kcalloc(test, 16, sizeof(*ctx->blocks), GFP_KERNEL); + if (!ctx->po || !ctx->blocks) + return -ENOMEM; + for (i = 0; i < 16; i++) { + ctx->blocks[i].buffer = kunit_kzalloc(test, PAGE_SIZE, GFP_KERNEL); + if (!ctx->blocks[i].buffer) + return -ENOMEM; + } + ctx->dev = alloc_netdev(0, "prb%d", NET_NAME_UNKNOWN, ether_setup); + if (!ctx->dev) + return -ENOMEM; + dev_net_set(ctx->dev, &init_net); + sock_net_set(&ctx->po->sk, &init_net); + ctx->socket.type = SOCK_RAW; + init_waitqueue_head(&ctx->socket.wq.wait); + sock_init_data_uid(&ctx->socket, &ctx->po->sk, GLOBAL_ROOT_UID); + ctx->po->sk.sk_type = SOCK_RAW; + ctx->po->sk.sk_user_data = ctx; + ctx->po->sk.sk_data_ready = prb_test_data_ready; + ctx->po->prot_hook.func = tpacket_rcv; + ctx->po->prot_hook.af_packet_priv = &ctx->po->sk; + ctx->po->tp_version = TPACKET_V3; + ctx->po->tp_hdrlen = TPACKET3_HDRLEN; + ctx->po->rx_ring.pg_vec = ctx->blocks; + req.req3.tp_block_size = PAGE_SIZE; + req.req3.tp_block_nr = 16; + req.req3.tp_retire_blk_tov = 60000; + err = init_prb_bdqc(ctx->po, &ctx->po->rx_ring, ctx->blocks, &req); + if (err) { + free_netdev(ctx->dev); + return err; + } + hrtimer_cancel(&ctx->po->rx_ring.prb_bdqc.retire_blk_timer); + return 0; +} + +static void prb_test_exit(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + + prb_shutdown_retire_blk_timer(ctx->po, &ctx->po->sk.sk_receive_queue); + kvfree(ctx->po->rx_ring.prb_bdqc.blocks); + free_netdev(ctx->dev); +} + +static void prb_test_invalid_vnet_does_not_reserve(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_block_desc *pbd = (void *)ctx->blocks[0].buffer; + struct sk_buff *skb = alloc_skb(128, GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb); + skb_put_zero(skb, 64); + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb->dev = ctx->dev; + skb->protocol = htons(ETH_P_IP); + skb_shinfo(skb)->gso_size = 32; + skb_shinfo(skb)->gso_type = SKB_GSO_DODGY; + ctx->po->vnet_hdr_sz = sizeof(struct virtio_net_hdr); + tpacket_rcv(skb, ctx->dev, &ctx->po->prot_hook, ctx->dev); + KUNIT_EXPECT_EQ(test, BLOCK_NUM_PKTS(pbd), 0U); + KUNIT_EXPECT_EQ(test, BLOCK_LEN(pbd), (u32)BLK_PLUS_PRIV(0)); + KUNIT_EXPECT_EQ(test, atomic_read(&ctx->po->tp_drops), 1); +} + +struct prb_test_copy { + struct prb_block_state *block; + struct tpacket3_hdr *hdr; +}; + +static struct prb_test_copy prb_test_reserve(struct prb_test_context *ctx, + unsigned int length) +{ + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + struct prb_test_copy copy = {}; + struct sk_buff skb = {}; + + spin_lock_bh(&ctx->po->sk.sk_receive_queue.lock); + copy.hdr = __packet_lookup_frame_in_block(ctx->po, &skb, length); + if (copy.hdr) + copy.block = &pkc->blocks[pkc->kactive_blk_num]; + spin_unlock_bh(&ctx->po->sk.sk_receive_queue.lock); + return copy; +} + +static void prb_test_complete(struct prb_test_context *ctx, + struct prb_test_copy copy, unsigned int stamp) +{ + copy.hdr->tp_len = 64; + copy.hdr->tp_snaplen = 64; + copy.hdr->tp_sec = stamp; + copy.hdr->tp_nsec = stamp; + spin_lock_bh(&ctx->po->sk.sk_receive_queue.lock); + prb_complete_copy(ctx->po, copy.block); + spin_unlock_bh(&ctx->po->sk.sk_receive_queue.lock); +} + +static void prb_test_seal(struct prb_test_context *ctx, unsigned int status) +{ + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + + spin_lock_bh(&ctx->po->sk.sk_receive_queue.lock); + prb_retire_current_block(pkc, ctx->po, status); + prb_dispatch_next_block(pkc, ctx->po); + spin_unlock_bh(&ctx->po->sk.sk_receive_queue.lock); +} + +static struct tpacket_block_desc *prb_test_block(struct prb_test_context *ctx, + unsigned int index) +{ + return (void *)ctx->blocks[index].buffer; +} + +static void prb_test_busy_a_allows_b(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + unsigned int length = pkc->max_frame_len - V3_ALIGNMENT; + struct prb_test_copy a = prb_test_reserve(ctx, length); + struct prb_test_copy b; + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, a.hdr); + b = prb_test_reserve(ctx, length); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, b.hdr); + KUNIT_EXPECT_EQ(test, pkc->kactive_blk_num, 1U); + KUNIT_EXPECT_EQ(test, a.block->phase, PRB_BLOCK_SEALED); + KUNIT_EXPECT_EQ(test, b.block->phase, PRB_BLOCK_ACTIVE); + KUNIT_EXPECT_EQ(test, a.block->in_flight, 1U); + KUNIT_EXPECT_EQ(test, BLOCK_STATUS(prb_test_block(ctx, 0)), 0U); + KUNIT_EXPECT_FALSE(test, !!(packet_poll(NULL, &ctx->socket, NULL) & EPOLLIN)); + prb_test_complete(ctx, b, 22); + prb_test_seal(ctx, 0); + KUNIT_EXPECT_EQ(test, ctx->notifications, 0U); + KUNIT_EXPECT_EQ(test, BLOCK_STATUS(prb_test_block(ctx, 1)), 0U); + prb_test_complete(ctx, a, 11); + KUNIT_EXPECT_EQ(test, ctx->notifications, 2U); + KUNIT_EXPECT_EQ(test, pkc->sealed_blocks, 0U); + KUNIT_EXPECT_EQ(test, BLOCK_STATUS(prb_test_block(ctx, 0)), TP_STATUS_USER); + KUNIT_EXPECT_EQ(test, BLOCK_STATUS(prb_test_block(ctx, 1)), TP_STATUS_USER); + KUNIT_EXPECT_EQ(test, prb_test_block(ctx, 0)->hdr.bh1.ts_last_pkt.ts_sec, 11U); + KUNIT_EXPECT_EQ(test, prb_test_block(ctx, 1)->hdr.bh1.ts_last_pkt.ts_sec, 22U); + KUNIT_EXPECT_EQ(test, BLOCK_SNUM(prb_test_block(ctx, 0)), 1ULL); + KUNIT_EXPECT_EQ(test, BLOCK_SNUM(prb_test_block(ctx, 1)), 2ULL); + KUNIT_EXPECT_EQ(test, a.hdr->tp_next_offset, 0U); + KUNIT_EXPECT_EQ(test, b.hdr->tp_next_offset, 0U); + KUNIT_EXPECT_TRUE(test, !!(packet_poll(NULL, &ctx->socket, NULL) & EPOLLIN)); +} + +static void prb_test_complete_before_seal(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct prb_test_copy copy = prb_test_reserve(ctx, 128); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copy.hdr); + prb_test_complete(ctx, copy, 7); + KUNIT_EXPECT_EQ(test, ctx->notifications, 0U); + prb_test_seal(ctx, 0); + KUNIT_EXPECT_EQ(test, ctx->notifications, 1U); + KUNIT_EXPECT_EQ(test, BLOCK_STATUS(prb_test_block(ctx, 0)), TP_STATUS_USER); +} + +static void prb_test_multiple_copiers(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct prb_test_copy first = prb_test_reserve(ctx, 128); + struct prb_test_copy last = prb_test_reserve(ctx, 128); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, first.hdr); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, last.hdr); + KUNIT_EXPECT_PTR_EQ(test, first.block, last.block); + prb_test_seal(ctx, 0); + prb_test_complete(ctx, last, 20); + KUNIT_EXPECT_EQ(test, ctx->notifications, 0U); + prb_test_complete(ctx, first, 10); + KUNIT_EXPECT_EQ(test, ctx->notifications, 1U); + KUNIT_EXPECT_EQ(test, prb_test_block(ctx, 0)->hdr.bh1.ts_last_pkt.ts_sec, 20U); +} + +static void prb_test_timeout_busy_and_empty(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + struct prb_test_copy copy; + + prb_retire_rx_blk_timer_expired(&pkc->retire_blk_timer); + KUNIT_EXPECT_EQ(test, pkc->kactive_blk_num, 0U); + KUNIT_EXPECT_EQ(test, ctx->notifications, 0U); + copy = prb_test_reserve(ctx, 128); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copy.hdr); + prb_retire_rx_blk_timer_expired(&pkc->retire_blk_timer); + KUNIT_EXPECT_EQ(test, pkc->kactive_blk_num, 1U); + KUNIT_EXPECT_EQ(test, ctx->notifications, 0U); + prb_test_complete(ctx, copy, 9); + KUNIT_EXPECT_EQ(test, BLOCK_STATUS(prb_test_block(ctx, 0)), + (u32)(TP_STATUS_USER | TP_STATUS_BLK_TMO)); + KUNIT_EXPECT_TRUE(test, !!(packet_poll(NULL, &ctx->socket, NULL) & EPOLLIN)); +} + +static void prb_test_single_block_wrap(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + struct prb_test_copy copy, next; + + pkc->knum_blocks = 1; + copy = prb_test_reserve(ctx, pkc->max_frame_len - V3_ALIGNMENT); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copy.hdr); + next = prb_test_reserve(ctx, 128); + KUNIT_EXPECT_PTR_EQ(test, next.hdr, NULL); + KUNIT_EXPECT_EQ(test, pkc->sealed_blocks, 1U); + KUNIT_EXPECT_EQ(test, __packet_rcv_has_room(ctx->po, NULL), ROOM_NONE); + prb_test_complete(ctx, copy, 3); + next = prb_test_reserve(ctx, 128); + KUNIT_EXPECT_PTR_EQ(test, next.hdr, NULL); + WRITE_ONCE(BLOCK_STATUS(prb_test_block(ctx, 0)), TP_STATUS_KERNEL); + next = prb_test_reserve(ctx, 128); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, next.hdr); + KUNIT_EXPECT_EQ(test, BLOCK_SNUM(prb_test_block(ctx, 0)), 2ULL); + KUNIT_EXPECT_EQ(test, next.block->in_flight, 1U); + prb_test_complete(ctx, next, 4); + prb_test_seal(ctx, 0); + KUNIT_EXPECT_EQ(test, ctx->notifications, 2U); +} + +static void prb_test_exact_fit(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + struct prb_test_copy copy = prb_test_reserve(ctx, pkc->max_frame_len); + struct prb_test_copy next; + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copy.hdr); + KUNIT_EXPECT_EQ(test, BLOCK_LEN(prb_test_block(ctx, 0)), pkc->kblk_size); + next = prb_test_reserve(ctx, 128); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, next.hdr); + KUNIT_EXPECT_PTR_EQ(test, next.block, &pkc->blocks[1]); + prb_test_complete(ctx, next, 2); + prb_test_seal(ctx, 0); + prb_test_complete(ctx, copy, 1); + KUNIT_EXPECT_EQ(test, ctx->notifications, 2U); +} + +static void prb_test_no_scan_past_user(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + struct prb_test_copy copy = prb_test_reserve(ctx, 128); + struct prb_test_copy next; + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copy.hdr); + pkc->blocks[1].phase = PRB_BLOCK_USER; + BLOCK_STATUS(prb_test_block(ctx, 1)) = TP_STATUS_USER; + prb_test_seal(ctx, 0); + next = prb_test_reserve(ctx, 128); + KUNIT_EXPECT_PTR_EQ(test, next.hdr, NULL); + KUNIT_EXPECT_EQ(test, pkc->blocks[2].phase, PRB_BLOCK_FREE); + prb_test_complete(ctx, copy, 1); + WRITE_ONCE(BLOCK_STATUS(prb_test_block(ctx, 1)), TP_STATUS_KERNEL); + next = prb_test_reserve(ctx, 128); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, next.hdr); + KUNIT_EXPECT_PTR_EQ(test, next.block, &pkc->blocks[1]); + prb_test_complete(ctx, next, 2); +} + +static void prb_test_no_reuse_of_sealed(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + struct prb_test_copy a, b, next; + unsigned int length = pkc->max_frame_len - V3_ALIGNMENT; + + pkc->knum_blocks = 2; + a = prb_test_reserve(ctx, length); + b = prb_test_reserve(ctx, length); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, a.hdr); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, b.hdr); + next = prb_test_reserve(ctx, 128); + KUNIT_EXPECT_PTR_EQ(test, next.hdr, NULL); + KUNIT_EXPECT_EQ(test, pkc->sealed_blocks, 2U); + KUNIT_EXPECT_EQ(test, a.block->in_flight, 1U); + KUNIT_EXPECT_EQ(test, __packet_rcv_has_room(ctx->po, NULL), ROOM_NONE); + prb_test_complete(ctx, b, 2); + KUNIT_EXPECT_EQ(test, ctx->notifications, 0U); + prb_test_complete(ctx, a, 1); + KUNIT_EXPECT_EQ(test, ctx->notifications, 2U); + WRITE_ONCE(BLOCK_STATUS(prb_test_block(ctx, 0)), TP_STATUS_KERNEL); + next = prb_test_reserve(ctx, 128); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, next.hdr); + prb_test_complete(ctx, next, 3); +} + +static void prb_test_large_ring_wrap(struct kunit *test, unsigned int count) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + struct prb_block_state *states; + struct prb_test_copy copies[4]; + struct pgv *pages; + unsigned int start = 65534, i; + + pages = kunit_kcalloc(test, count, sizeof(*pages), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages); + states = kvcalloc(count, sizeof(*states), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, states); + /* Only blocks around the wrap point need backing storage. */ + for (i = 0; i <= ARRAY_SIZE(copies); i++) + pages[(start + i) % count].buffer = ctx->blocks[i].buffer; + kvfree(pkc->blocks); + ctx->blocks = pages; + ctx->po->rx_ring.pg_vec = pages; + pkc->pkbdq = pages; + pkc->blocks = states; + pkc->knum_blocks = count; + pkc->kactive_blk_num = start; + prb_open_block(pkc, prb_test_block(ctx, start)); + for (i = 0; i < ARRAY_SIZE(copies); i++) { + copies[i] = prb_test_reserve(ctx, pkc->max_frame_len); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copies[i].hdr); + KUNIT_EXPECT_EQ(test, pkc->kactive_blk_num, (start + i) % count); + } + prb_test_seal(ctx, 0); + for (i = 1; i < ARRAY_SIZE(copies); i++) + prb_test_complete(ctx, copies[i], i + 1); + KUNIT_EXPECT_EQ(test, ctx->notifications, 0U); + prb_test_complete(ctx, copies[0], 1); + KUNIT_EXPECT_EQ(test, ctx->notifications, 4U); + KUNIT_EXPECT_EQ(test, pkc->sealed_blocks, 0U); + KUNIT_EXPECT_EQ(test, pkc->publish_blk_num, (start + 4) % count); + for (i = 0; i < ARRAY_SIZE(copies); i++) { + struct tpacket_block_desc *pbd = prb_test_block(ctx, (start + i) % count); + + KUNIT_EXPECT_EQ(test, BLOCK_STATUS(pbd), TP_STATUS_USER); + KUNIT_EXPECT_EQ(test, BLOCK_SNUM(pbd), (u64)i + 2); + } +} + +static void prb_test_65536_blocks(struct kunit *test) +{ + prb_test_large_ring_wrap(test, 65536); +} + +static void prb_test_65537_blocks(struct kunit *test) +{ + prb_test_large_ring_wrap(test, 65537); +} + +static void prb_test_bounded_publish_and_worker(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + struct prb_test_copy copies[12]; + unsigned int i, notifications, pending; + + for (i = 0; i < ARRAY_SIZE(copies); i++) { + copies[i] = prb_test_reserve(ctx, pkc->max_frame_len - V3_ALIGNMENT); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copies[i].hdr); + } + prb_test_seal(ctx, 0); + for (i = 1; i < ARRAY_SIZE(copies); i++) + prb_test_complete(ctx, copies[i], i + 1); + copies[0].hdr->tp_sec = 1; + spin_lock_bh(&ctx->po->sk.sk_receive_queue.lock); + prb_complete_copy(ctx->po, copies[0].block); + notifications = ctx->notifications; + pending = pkc->sealed_blocks; + spin_unlock_bh(&ctx->po->sk.sk_receive_queue.lock); + KUNIT_EXPECT_EQ(test, notifications, (unsigned int)PRB_PUBLISH_BUDGET); + KUNIT_EXPECT_EQ(test, pending, 4U); + flush_work(&pkc->retire_work); + KUNIT_EXPECT_EQ(test, ctx->notifications, 12U); + KUNIT_EXPECT_EQ(test, pkc->sealed_blocks, 0U); + for (i = 0; i < ARRAY_SIZE(copies); i++) { + KUNIT_EXPECT_EQ(test, BLOCK_STATUS(prb_test_block(ctx, i)), TP_STATUS_USER); + KUNIT_EXPECT_EQ(test, BLOCK_SNUM(prb_test_block(ctx, i)), (u64)i + 1); + } +} + +static void prb_test_shutdown_stops_publication(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + struct prb_test_copy copy = prb_test_reserve(ctx, 128); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copy.hdr); + prb_test_seal(ctx, 0); + spin_lock_bh(&ctx->po->sk.sk_receive_queue.lock); + queue_work(system_dfl_wq, &pkc->retire_work); + pkc->retire_shutdown = true; + spin_unlock_bh(&ctx->po->sk.sk_receive_queue.lock); + prb_shutdown_retire_blk_timer(ctx->po, &ctx->po->sk.sk_receive_queue); + prb_test_complete(ctx, copy, 1); + KUNIT_EXPECT_EQ(test, ctx->notifications, 0U); + KUNIT_EXPECT_FALSE(test, work_pending(&pkc->retire_work)); + KUNIT_EXPECT_FALSE(test, hrtimer_active(&pkc->retire_blk_timer)); +} + +static void prb_test_payload_and_full_ring_drop(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_kbdq_core *pkc = &ctx->po->rx_ring.prb_bdqc; + struct tpacket_block_desc *pbd = prb_test_block(ctx, 0); + struct tpacket3_hdr *hdr; + struct sk_buff *skb; + unsigned int i; + + pkc->knum_blocks = 1; + for (i = 0; i < 2; i++) { + skb = alloc_skb(128, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb); + memset(skb_put(skb, 64), 0x5a + i, 64); + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb->dev = ctx->dev; + skb->protocol = htons(ETH_P_IP); + local_bh_disable(); + tpacket_rcv(skb, ctx->dev, &ctx->po->prot_hook, ctx->dev); + local_bh_enable(); + if (!i) + prb_test_seal(ctx, 0); + } + KUNIT_EXPECT_EQ(test, atomic_read(&ctx->po->tp_drops), 1); + KUNIT_EXPECT_EQ(test, pkc->blocks[0].in_flight, 0U); + KUNIT_EXPECT_EQ(test, BLOCK_NUM_PKTS(pbd), 1U); + hdr = (void *)((char *)pbd + pbd->hdr.bh1.offset_to_first_pkt); + KUNIT_EXPECT_EQ(test, hdr->tp_len, 64U); + KUNIT_EXPECT_EQ(test, hdr->tp_snaplen, 64U); + KUNIT_EXPECT_EQ(test, hdr->tp_next_offset, 0U); + for (i = 0; i < 64; i++) + KUNIT_EXPECT_EQ(test, *((u8 *)hdr + hdr->tp_mac + i), (u8)0x5a); +} + +static void prb_test_valid_vnet_header(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct tpacket_block_desc *pbd = prb_test_block(ctx, 0); + struct tpacket3_hdr *hdr; + struct virtio_net_hdr *vnet; + struct sk_buff *skb = alloc_skb(128, GFP_KERNEL); + unsigned int i; + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb); + memset(skb_put(skb, 64), 0x5a, 64); + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb->dev = ctx->dev; + skb->protocol = htons(ETH_P_IP); + skb_shinfo(skb)->gso_size = 32; + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4 | SKB_GSO_TCP_ECN; + skb->ip_summed = CHECKSUM_PARTIAL; + skb->csum_start = skb_headroom(skb) + 20; + skb->csum_offset = 16; + ctx->po->vnet_hdr_sz = sizeof(struct virtio_net_hdr); + local_bh_disable(); + tpacket_rcv(skb, ctx->dev, &ctx->po->prot_hook, ctx->dev); + local_bh_enable(); + prb_test_seal(ctx, 0); + KUNIT_EXPECT_EQ(test, atomic_read(&ctx->po->tp_drops), 0); + KUNIT_EXPECT_EQ(test, BLOCK_NUM_PKTS(pbd), 1U); + hdr = (void *)((char *)pbd + pbd->hdr.bh1.offset_to_first_pkt); + vnet = (void *)((char *)hdr + hdr->tp_mac - sizeof(*vnet)); + KUNIT_EXPECT_EQ(test, vnet->gso_type, + (u8)(VIRTIO_NET_HDR_GSO_TCPV4 | VIRTIO_NET_HDR_GSO_ECN)); + KUNIT_EXPECT_EQ(test, vnet->flags, (u8)VIRTIO_NET_HDR_F_NEEDS_CSUM); + KUNIT_EXPECT_EQ(test, __virtio16_to_cpu(vio_le(), vnet->hdr_len), (u16)64); + KUNIT_EXPECT_EQ(test, __virtio16_to_cpu(vio_le(), vnet->gso_size), (u16)32); + KUNIT_EXPECT_EQ(test, __virtio16_to_cpu(vio_le(), vnet->csum_start), (u16)20); + KUNIT_EXPECT_EQ(test, __virtio16_to_cpu(vio_le(), vnet->csum_offset), (u16)16); + KUNIT_EXPECT_EQ(test, hdr->tp_snaplen, 64U); + for (i = 0; i < 64; i++) + KUNIT_EXPECT_EQ(test, *((u8 *)hdr + hdr->tp_mac + i), (u8)0x5a); +} + +static void prb_test_invalid_vnet_releases_copy(struct kunit *test) +{ + struct prb_test_context *ctx = test->priv; + struct sk_buff *skb = alloc_skb(512, GFP_KERNEL); + struct packet_sock *po = ctx->po; + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb); + skb_put_zero(skb, 256); + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb->dev = ctx->dev; + skb->protocol = htons(ETH_P_IP); + skb_shinfo(skb)->gso_size = 32; + skb_shinfo(skb)->gso_type = SKB_GSO_DODGY; + po->tp_version = TPACKET_V2; + po->sk.sk_prot = &packet_proto; + po->tp_hdrlen = TPACKET2_HDRLEN; + po->rx_ring.frame_size = 128; + po->rx_ring.frames_per_block = PAGE_SIZE / 128; + WRITE_ONCE(((struct tpacket2_hdr *)ctx->blocks[0].buffer)->tp_status, + TP_STATUS_KERNEL); + po->vnet_hdr_sz = sizeof(struct virtio_net_hdr); + po->copy_thresh = 1; + local_bh_disable(); + tpacket_rcv(skb, ctx->dev, &po->prot_hook, ctx->dev); + local_bh_enable(); + KUNIT_EXPECT_EQ(test, atomic_read(&po->sk.sk_rmem_alloc), 0); + KUNIT_EXPECT_EQ(test, atomic_read(&po->tp_drops), 1); + KUNIT_EXPECT_TRUE(test, skb_queue_empty(&po->sk.sk_receive_queue)); +} + +struct prb_test_poller { + struct socket socket; + atomic_t polls; + struct completion started; +}; + +static int prb_test_poll_thread(void *data) +{ + struct prb_test_poller *poller = data; + struct packet_sock *po = pkt_sk(poller->socket.sk); + + while (!kthread_should_stop()) { + packet_sock_flag_set(po, PACKET_SOCK_PRESSURE, true); + packet_poll(NULL, &poller->socket, NULL); + if (atomic_inc_return(&poller->polls) == 1) + complete(&poller->started); + cond_resched(); + } + return 0; +} + +static void prb_test_ring_lifecycle(struct kunit *test) +{ + struct packet_sock *po; + struct prb_test_poller *poller; + struct task_struct *task; + union tpacket_req_u req = {}, empty = {}; + unsigned int i; + int result; + + po = kunit_kzalloc(test, sizeof(*po), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, po); + poller = kunit_kzalloc(test, sizeof(*poller), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, poller); + sock_net_set(&po->sk, &init_net); + init_waitqueue_head(&poller->socket.wq.wait); + sock_init_data_uid(&poller->socket, &po->sk, GLOBAL_ROOT_UID); + mutex_init(&po->pg_vec_lock); + spin_lock_init(&po->bind_lock); + po->tp_version = TPACKET_V3; + po->sk.sk_type = SOCK_RAW; + po->prot_hook.func = packet_rcv; + init_completion(&poller->started); + task = kthread_run(prb_test_poll_thread, poller, "prb-poll-test"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, task); + KUNIT_EXPECT_NE(test, wait_for_completion_timeout(&poller->started, HZ), 0UL); + for (i = 0; i < 16; i++) { + memset(&req, 0, sizeof(req)); + memset(&empty, 0, sizeof(empty)); + req.req3.tp_block_size = PAGE_SIZE; + req.req3.tp_block_nr = 2; + req.req3.tp_frame_size = 256; + req.req3.tp_frame_nr = 2 * PAGE_SIZE / 256; + req.req3.tp_retire_blk_tov = 1000; + result = packet_set_ring(&po->sk, &req, 0, 0); + KUNIT_EXPECT_EQ(test, result, 0); + if (result) + break; + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, po->rx_ring.pg_vec); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, po->rx_ring.prb_bdqc.blocks); + queue_work(system_dfl_wq, &po->rx_ring.prb_bdqc.retire_work); + atomic_long_set(&po->mapped, 1); + KUNIT_EXPECT_EQ(test, packet_set_ring(&po->sk, &empty, 0, 0), -EBUSY); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, po->rx_ring.pg_vec); + atomic_long_set(&po->mapped, 0); + result = packet_set_ring(&po->sk, &empty, 1, 0); + KUNIT_EXPECT_EQ(test, result, 0); + if (result) + break; + KUNIT_EXPECT_PTR_EQ(test, po->rx_ring.pg_vec, NULL); + KUNIT_EXPECT_PTR_EQ(test, po->rx_ring.prb_bdqc.blocks, NULL); + KUNIT_EXPECT_FALSE(test, hrtimer_active(&po->rx_ring.prb_bdqc.retire_blk_timer)); + KUNIT_EXPECT_FALSE(test, work_pending(&po->rx_ring.prb_bdqc.retire_work)); + schedule_timeout_uninterruptible(1); + } + kthread_stop(task); + KUNIT_EXPECT_GT(test, atomic_read(&poller->polls), 0); + if (po->rx_ring.pg_vec) { + memset(&empty, 0, sizeof(empty)); + packet_set_ring(&po->sk, &empty, 1, 0); + } +} + +static struct kunit_case prb_test_cases[] = { + KUNIT_CASE(prb_test_invalid_vnet_does_not_reserve), + KUNIT_CASE(prb_test_busy_a_allows_b), + KUNIT_CASE(prb_test_complete_before_seal), + KUNIT_CASE(prb_test_multiple_copiers), + KUNIT_CASE(prb_test_timeout_busy_and_empty), + KUNIT_CASE(prb_test_single_block_wrap), + KUNIT_CASE(prb_test_exact_fit), + KUNIT_CASE(prb_test_no_scan_past_user), + KUNIT_CASE(prb_test_no_reuse_of_sealed), + KUNIT_CASE(prb_test_65536_blocks), + KUNIT_CASE(prb_test_65537_blocks), + KUNIT_CASE(prb_test_bounded_publish_and_worker), + KUNIT_CASE(prb_test_shutdown_stops_publication), + KUNIT_CASE(prb_test_payload_and_full_ring_drop), + KUNIT_CASE(prb_test_valid_vnet_header), + KUNIT_CASE(prb_test_invalid_vnet_releases_copy), + KUNIT_CASE(prb_test_ring_lifecycle), + {} +}; + +static struct kunit_suite prb_test_suite = { + .name = "packet-prb", + .init = prb_test_init, + .exit = prb_test_exit, + .test_cases = prb_test_cases, +}; + +kunit_test_suite(prb_test_suite); --
2.25.1 ________________________________ 本电子邮件及转载的任何文件都属绝密文件,并且仅供标明地址的个人或团体使用。如果您收到的电子邮件存在错误,请通知本公司,删除全部原始信息(包括任何原始信息的备份),并且禁止散布或分发全部或部分原始信息。本公司不担保本电子邮件中信息的准确性、适当性或完整性,并且对此产生的任何错误或疏忽不承担任何责任。在正式协议签订以前,这里提到的任何参考术语、与主题相关的情形以及随后的电子邮件中的谈判,都将严格“遵守合同”。接收方应在接收电子邮件或任何附件时检查有无病毒,本公司对由于转载本电子邮件而引发病毒产生的任何损坏不承担任何责任。 Reproduced in this e-mail and any files are considered top-secret documents, and only indicate the address of individuals or groups. If you receive an e-mail there is an error, please notify the Company, delete all the original information (including any backup of the original information), and the prohibition of the dissemination or distribution of all or part of the original information. The Company does not guarantee the accuracy of the information in this e-mail, adequacy, or completeness, and this produces any error or omission does not bear any responsibility. The formal agreement was signed prior to any reference to the term mentioned here, and the circumstances relevant to the subject and subsequent e-mail in the negotiations will be strictly "comply with the contract." The receiver should be receiving an e-mail or any attachment, check for viruses. The company reproduced this e-mail because of the virus caused any damage resulting from not bear any responsibility.