Add a shadow refcount to count pins from the Rx/Tx ring on an sk_buff so that we can hold multiple refs on it without causing skb_cow_data() to throw an assertion.
This is stored in the private part of the sk_buff as laid out in struct rxrpc_skb_priv. Add two accessor functions for pinning (adding) or unpinning (discarding) a shadow ref. Signed-off-by: David Howells <dhowe...@redhat.com> --- include/trace/events/rxrpc.h | 15 ++++++++--- net/rxrpc/ar-internal.h | 2 + net/rxrpc/skbuff.c | 57 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index e2356c51883b..34237ea8ceb0 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -28,10 +28,12 @@ enum rxrpc_skb_trace { rxrpc_skb_got, rxrpc_skb_lost, rxrpc_skb_new, + rxrpc_skb_pin, rxrpc_skb_purged, rxrpc_skb_received, rxrpc_skb_rotated, rxrpc_skb_seen, + rxrpc_skb_unpin, }; enum rxrpc_local_trace { @@ -228,10 +230,12 @@ enum rxrpc_tx_point { EM(rxrpc_skb_got, "GOT") \ EM(rxrpc_skb_lost, "*L*") \ EM(rxrpc_skb_new, "NEW") \ + EM(rxrpc_skb_pin, "PIN") \ EM(rxrpc_skb_purged, "PUR") \ EM(rxrpc_skb_received, "RCV") \ EM(rxrpc_skb_rotated, "ROT") \ - E_(rxrpc_skb_seen, "SEE") + EM(rxrpc_skb_seen, "SEE") \ + E_(rxrpc_skb_unpin, "UPN") #define rxrpc_local_traces \ EM(rxrpc_local_got, "GOT") \ @@ -633,14 +637,15 @@ TRACE_EVENT(rxrpc_call, TRACE_EVENT(rxrpc_skb, TP_PROTO(struct sk_buff *skb, enum rxrpc_skb_trace op, - int usage, int mod_count, const void *where), + int usage, int mod_count, int pins, const void *where), - TP_ARGS(skb, op, usage, mod_count, where), + TP_ARGS(skb, op, usage, mod_count, pins, where), TP_STRUCT__entry( __field(struct sk_buff *, skb ) __field(enum rxrpc_skb_trace, op ) __field(u8, flags ) + __field(u8, pins ) __field(int, usage ) __field(int, mod_count ) __field(const void *, where ) @@ -651,16 +656,18 @@ TRACE_EVENT(rxrpc_skb, __entry->flags = rxrpc_skb(skb)->rx_flags; __entry->op = op; __entry->usage = usage; + __entry->pins = pins; __entry->mod_count = mod_count; __entry->where = where; ), - TP_printk("s=%p %cx %s u=%d m=%d p=%pSR", + TP_printk("s=%p %cx %s u=%d m=%d r=%u p=%pSR", __entry->skb, __entry->flags & RXRPC_SKB_TX_BUFFER ? 'T' : 'R', __print_symbolic(__entry->op, rxrpc_skb_traces), __entry->usage, __entry->mod_count, + __entry->pins, __entry->where) ); diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 2d5294f3e62f..d784d58e0a0d 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1113,6 +1113,8 @@ void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace); void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace); void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace); void rxrpc_purge_queue(struct sk_buff_head *); +void rxrpc_pin_skb(struct sk_buff *, enum rxrpc_skb_trace); +void rxrpc_unpin_skb(struct sk_buff *, enum rxrpc_skb_trace); /* * sysctl.c diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c index 8e6f45f84b9b..f9986a1510d3 100644 --- a/net/rxrpc/skbuff.c +++ b/net/rxrpc/skbuff.c @@ -22,9 +22,12 @@ */ void rxrpc_new_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); const void *here = __builtin_return_address(0); int n = atomic_inc_return(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); + + atomic_set(&sp->nr_ring_pins, 1); + trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, 1, here); } /* @@ -33,9 +36,12 @@ void rxrpc_new_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) void rxrpc_see_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) { const void *here = __builtin_return_address(0); + if (skb) { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); int n = atomic_read(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); + trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, + atomic_read(&sp->nr_ring_pins), here); } } @@ -44,9 +50,11 @@ void rxrpc_see_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) */ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); const void *here = __builtin_return_address(0); int n = atomic_inc_return(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); + trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, + atomic_read(&sp->nr_ring_pins), here); skb_get(skb); } @@ -56,11 +64,14 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) void rxrpc_free_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) { const void *here = __builtin_return_address(0); + if (skb) { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); int n; CHECK_SLAB_OKAY(&skb->users); n = atomic_dec_return(select_skb_count(skb)); - trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, here); + trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, + atomic_read(&sp->nr_ring_pins), here); kfree_skb(skb); } } @@ -72,10 +83,46 @@ void rxrpc_purge_queue(struct sk_buff_head *list) { const void *here = __builtin_return_address(0); struct sk_buff *skb; + while ((skb = skb_dequeue((list))) != NULL) { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); int n = atomic_dec_return(select_skb_count(skb)); trace_rxrpc_skb(skb, rxrpc_skb_purged, - refcount_read(&skb->users), n, here); + refcount_read(&skb->users), n, + atomic_read(&sp->nr_ring_pins), here); kfree_skb(skb); } } + +/* + * Add a secondary ref on the socket buffer. + */ +void rxrpc_pin_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + const void *here = __builtin_return_address(0); + int n = atomic_read(select_skb_count(skb)); + int np; + + np = atomic_inc_return(&sp->nr_ring_pins); + trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, np, here); +} + +/* + * Remove a secondary ref on the socket buffer. + */ +void rxrpc_unpin_skb(struct sk_buff *skb, enum rxrpc_skb_trace op) +{ + const void *here = __builtin_return_address(0); + + if (skb) { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + int n = atomic_read(select_skb_count(skb)); + int np; + + np = atomic_dec_return(&sp->nr_ring_pins); + trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n, np, here); + if (np == 0) + rxrpc_free_skb(skb, op); + } +}