Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

af_packet: TPACKET_V3: replace busy-wait loop

A busy-wait loop is used to implement waiting for bits to be copied
from the skb to the kernel buffer before retiring a block. This is
a problem on PREEMPT_RT because the copying task could be preempted
by the busy-waiting task and thus live lock in the busy-wait loop.

Replace the busy-wait logic with an rwlock_t. This provides lockdep
coverage and makes the code RT ready.

Signed-off-by: John Ogness <john.ogness@linutronix.de>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

John Ogness and committed by
Jakub Kicinski
632ca50f 999cf8ae

+11 -11
+10 -10
net/packet/af_packet.c
··· 593 593 req_u->req3.tp_block_size); 594 594 p1->tov_in_jiffies = msecs_to_jiffies(p1->retire_blk_tov); 595 595 p1->blk_sizeof_priv = req_u->req3.tp_sizeof_priv; 596 + rwlock_init(&p1->blk_fill_in_prog_lock); 596 597 597 598 p1->max_frame_len = p1->kblk_size - BLK_PLUS_PRIV(p1->blk_sizeof_priv); 598 599 prb_init_ft_ops(p1, req_u); ··· 660 659 * 661 660 */ 662 661 if (BLOCK_NUM_PKTS(pbd)) { 663 - while (atomic_read(&pkc->blk_fill_in_prog)) { 664 - /* Waiting for skb_copy_bits to finish... */ 665 - cpu_relax(); 666 - } 662 + /* Waiting for skb_copy_bits to finish... */ 663 + write_lock(&pkc->blk_fill_in_prog_lock); 664 + write_unlock(&pkc->blk_fill_in_prog_lock); 667 665 } 668 666 669 667 if (pkc->last_kactive_blk_num == pkc->kactive_blk_num) { ··· 921 921 * the timer-handler already handled this case. 922 922 */ 923 923 if (!(status & TP_STATUS_BLK_TMO)) { 924 - while (atomic_read(&pkc->blk_fill_in_prog)) { 925 - /* Waiting for skb_copy_bits to finish... */ 926 - cpu_relax(); 927 - } 924 + /* Waiting for skb_copy_bits to finish... */ 925 + write_lock(&pkc->blk_fill_in_prog_lock); 926 + write_unlock(&pkc->blk_fill_in_prog_lock); 928 927 } 929 928 prb_close_block(pkc, pbd, po, status); 930 929 return; ··· 943 944 static void prb_clear_blk_fill_status(struct packet_ring_buffer *rb) 944 945 { 945 946 struct tpacket_kbdq_core *pkc = GET_PBDQC_FROM_RB(rb); 946 - atomic_dec(&pkc->blk_fill_in_prog); 947 + 948 + read_unlock(&pkc->blk_fill_in_prog_lock); 947 949 } 948 950 949 951 static void prb_fill_rxhash(struct tpacket_kbdq_core *pkc, ··· 998 998 pkc->nxt_offset += TOTAL_PKT_LEN_INCL_ALIGN(len); 999 999 BLOCK_LEN(pbd) += TOTAL_PKT_LEN_INCL_ALIGN(len); 1000 1000 BLOCK_NUM_PKTS(pbd) += 1; 1001 - atomic_inc(&pkc->blk_fill_in_prog); 1001 + read_lock(&pkc->blk_fill_in_prog_lock); 1002 1002 prb_run_all_ft_ops(pkc, ppd); 1003 1003 } 1004 1004
+1 -1
net/packet/internal.h
··· 39 39 char *nxt_offset; 40 40 struct sk_buff *skb; 41 41 42 - atomic_t blk_fill_in_prog; 42 + rwlock_t blk_fill_in_prog_lock; 43 43 44 44 /* Default is set to 8ms */ 45 45 #define DEFAULT_PRB_RETIRE_TOV (8)