Commit 7c7dd1d6 authored by Liang Chen's avatar Liang Chen Committed by Paolo Abeni

pktgen: Introducing 'SHARED' flag for testing with non-shared skb

Currently, skbs generated by pktgen always have their reference count
incremented before transmission, causing their reference count to be
always greater than 1, leading to two issues:
  1. Only the code paths for shared skbs can be tested.
  2. In certain situations, skbs can only be released by pktgen.
To enhance testing comprehensiveness, we are introducing the "SHARED"
flag to indicate whether an SKB is shared. This flag is enabled by
default, aligning with the current behavior. However, disabling this
flag allows skbs with a reference count of 1 to be transmitted.
So we can test non-shared skbs and code paths where skbs are released
within the stack.
Signed-off-by: default avatarLiang Chen <liangchen.linux@gmail.com>
Reviewed-by: default avatarBenjamin Poirier <bpoirier@nvidia.com>
Link: https://lore.kernel.org/r/20230920125658.46978-2-liangchen.linux@gmail.comSigned-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 057708a9
...@@ -178,6 +178,7 @@ Examples:: ...@@ -178,6 +178,7 @@ Examples::
IPSEC # IPsec encapsulation (needs CONFIG_XFRM) IPSEC # IPsec encapsulation (needs CONFIG_XFRM)
NODE_ALLOC # node specific memory allocation NODE_ALLOC # node specific memory allocation
NO_TIMESTAMP # disable timestamping NO_TIMESTAMP # disable timestamping
SHARED # enable shared SKB
pgset 'flag ![name]' Clear a flag to determine behaviour. pgset 'flag ![name]' Clear a flag to determine behaviour.
Note that you might need to use single quote in Note that you might need to use single quote in
interactive mode, so that your shell wouldn't expand interactive mode, so that your shell wouldn't expand
...@@ -288,6 +289,16 @@ To avoid breaking existing testbed scripts for using AH type and tunnel mode, ...@@ -288,6 +289,16 @@ To avoid breaking existing testbed scripts for using AH type and tunnel mode,
you can use "pgset spi SPI_VALUE" to specify which transformation mode you can use "pgset spi SPI_VALUE" to specify which transformation mode
to employ. to employ.
Disable shared SKB
==================
By default, SKBs sent by pktgen are shared (user count > 1).
To test with non-shared SKBs, remove the "SHARED" flag by simply setting::
pg_set "flag !SHARED"
However, if the "clone_skb" or "burst" parameters are configured, the skb
still needs to be held by pktgen for further access. Hence the skb must be
shared.
Current commands and configuration options Current commands and configuration options
========================================== ==========================================
...@@ -357,6 +368,7 @@ Current commands and configuration options ...@@ -357,6 +368,7 @@ Current commands and configuration options
IPSEC IPSEC
NODE_ALLOC NODE_ALLOC
NO_TIMESTAMP NO_TIMESTAMP
SHARED
spi (ipsec) spi (ipsec)
......
...@@ -200,6 +200,7 @@ ...@@ -200,6 +200,7 @@
pf(VID_RND) /* Random VLAN ID */ \ pf(VID_RND) /* Random VLAN ID */ \
pf(SVID_RND) /* Random SVLAN ID */ \ pf(SVID_RND) /* Random SVLAN ID */ \
pf(NODE) /* Node memory alloc*/ \ pf(NODE) /* Node memory alloc*/ \
pf(SHARED) /* Shared SKB */ \
#define pf(flag) flag##_SHIFT, #define pf(flag) flag##_SHIFT,
enum pkt_flags { enum pkt_flags {
...@@ -1198,7 +1199,8 @@ static ssize_t pktgen_if_write(struct file *file, ...@@ -1198,7 +1199,8 @@ static ssize_t pktgen_if_write(struct file *file,
((pkt_dev->xmit_mode == M_NETIF_RECEIVE) || ((pkt_dev->xmit_mode == M_NETIF_RECEIVE) ||
!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING))) !(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
return -ENOTSUPP; return -ENOTSUPP;
if (value > 0 && pkt_dev->n_imix_entries > 0) if (value > 0 && (pkt_dev->n_imix_entries > 0 ||
!(pkt_dev->flags & F_SHARED)))
return -EINVAL; return -EINVAL;
i += len; i += len;
...@@ -1257,6 +1259,10 @@ static ssize_t pktgen_if_write(struct file *file, ...@@ -1257,6 +1259,10 @@ static ssize_t pktgen_if_write(struct file *file,
((pkt_dev->xmit_mode == M_START_XMIT) && ((pkt_dev->xmit_mode == M_START_XMIT) &&
(!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING))))) (!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))))
return -ENOTSUPP; return -ENOTSUPP;
if (value > 1 && !(pkt_dev->flags & F_SHARED))
return -EINVAL;
pkt_dev->burst = value < 1 ? 1 : value; pkt_dev->burst = value < 1 ? 1 : value;
sprintf(pg_result, "OK: burst=%u", pkt_dev->burst); sprintf(pg_result, "OK: burst=%u", pkt_dev->burst);
return count; return count;
...@@ -1334,10 +1340,19 @@ static ssize_t pktgen_if_write(struct file *file, ...@@ -1334,10 +1340,19 @@ static ssize_t pktgen_if_write(struct file *file,
flag = pktgen_read_flag(f, &disable); flag = pktgen_read_flag(f, &disable);
if (flag) { if (flag) {
if (disable) if (disable) {
/* If "clone_skb", or "burst" parameters are
* configured, it means that the skb still
* needs to be referenced by the pktgen, so
* the skb must be shared.
*/
if (flag == F_SHARED && (pkt_dev->clone_skb ||
pkt_dev->burst > 1))
return -EINVAL;
pkt_dev->flags &= ~flag; pkt_dev->flags &= ~flag;
else } else {
pkt_dev->flags |= flag; pkt_dev->flags |= flag;
}
sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags); sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags);
return count; return count;
...@@ -3446,12 +3461,24 @@ static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev) ...@@ -3446,12 +3461,24 @@ static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)
static void pktgen_xmit(struct pktgen_dev *pkt_dev) static void pktgen_xmit(struct pktgen_dev *pkt_dev)
{ {
unsigned int burst = READ_ONCE(pkt_dev->burst); bool skb_shared = !!(READ_ONCE(pkt_dev->flags) & F_SHARED);
struct net_device *odev = pkt_dev->odev; struct net_device *odev = pkt_dev->odev;
struct netdev_queue *txq; struct netdev_queue *txq;
unsigned int burst = 1;
struct sk_buff *skb; struct sk_buff *skb;
int clone_skb = 0;
int ret; int ret;
/* If 'skb_shared' is false, the read of possible
* new values (if any) for 'burst' and 'clone_skb' will be skipped to
* prevent some concurrent changes from slipping in. And the stabilized
* config will be read in during the next run of pktgen_xmit.
*/
if (skb_shared) {
burst = READ_ONCE(pkt_dev->burst);
clone_skb = READ_ONCE(pkt_dev->clone_skb);
}
/* If device is offline, then don't send */ /* If device is offline, then don't send */
if (unlikely(!netif_running(odev) || !netif_carrier_ok(odev))) { if (unlikely(!netif_running(odev) || !netif_carrier_ok(odev))) {
pktgen_stop_device(pkt_dev); pktgen_stop_device(pkt_dev);
...@@ -3468,7 +3495,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) ...@@ -3468,7 +3495,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
/* If no skb or clone count exhausted then get new one */ /* If no skb or clone count exhausted then get new one */
if (!pkt_dev->skb || (pkt_dev->last_ok && if (!pkt_dev->skb || (pkt_dev->last_ok &&
++pkt_dev->clone_count >= pkt_dev->clone_skb)) { ++pkt_dev->clone_count >= clone_skb)) {
/* build a new pkt */ /* build a new pkt */
kfree_skb(pkt_dev->skb); kfree_skb(pkt_dev->skb);
...@@ -3489,6 +3516,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) ...@@ -3489,6 +3516,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
if (pkt_dev->xmit_mode == M_NETIF_RECEIVE) { if (pkt_dev->xmit_mode == M_NETIF_RECEIVE) {
skb = pkt_dev->skb; skb = pkt_dev->skb;
skb->protocol = eth_type_trans(skb, skb->dev); skb->protocol = eth_type_trans(skb, skb->dev);
if (skb_shared)
refcount_add(burst, &skb->users); refcount_add(burst, &skb->users);
local_bh_disable(); local_bh_disable();
do { do {
...@@ -3497,6 +3525,10 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) ...@@ -3497,6 +3525,10 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
pkt_dev->errors++; pkt_dev->errors++;
pkt_dev->sofar++; pkt_dev->sofar++;
pkt_dev->seq_num++; pkt_dev->seq_num++;
if (unlikely(!skb_shared)) {
pkt_dev->skb = NULL;
break;
}
if (refcount_read(&skb->users) != burst) { if (refcount_read(&skb->users) != burst) {
/* skb was queued by rps/rfs or taps, /* skb was queued by rps/rfs or taps,
* so cannot reuse this skb * so cannot reuse this skb
...@@ -3515,9 +3547,14 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) ...@@ -3515,9 +3547,14 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
goto out; /* Skips xmit_mode M_START_XMIT */ goto out; /* Skips xmit_mode M_START_XMIT */
} else if (pkt_dev->xmit_mode == M_QUEUE_XMIT) { } else if (pkt_dev->xmit_mode == M_QUEUE_XMIT) {
local_bh_disable(); local_bh_disable();
if (skb_shared)
refcount_inc(&pkt_dev->skb->users); refcount_inc(&pkt_dev->skb->users);
ret = dev_queue_xmit(pkt_dev->skb); ret = dev_queue_xmit(pkt_dev->skb);
if (!skb_shared && dev_xmit_complete(ret))
pkt_dev->skb = NULL;
switch (ret) { switch (ret) {
case NET_XMIT_SUCCESS: case NET_XMIT_SUCCESS:
pkt_dev->sofar++; pkt_dev->sofar++;
...@@ -3555,11 +3592,15 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) ...@@ -3555,11 +3592,15 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
pkt_dev->last_ok = 0; pkt_dev->last_ok = 0;
goto unlock; goto unlock;
} }
if (skb_shared)
refcount_add(burst, &pkt_dev->skb->users); refcount_add(burst, &pkt_dev->skb->users);
xmit_more: xmit_more:
ret = netdev_start_xmit(pkt_dev->skb, odev, txq, --burst > 0); ret = netdev_start_xmit(pkt_dev->skb, odev, txq, --burst > 0);
if (!skb_shared && dev_xmit_complete(ret))
pkt_dev->skb = NULL;
switch (ret) { switch (ret) {
case NETDEV_TX_OK: case NETDEV_TX_OK:
pkt_dev->last_ok = 1; pkt_dev->last_ok = 1;
...@@ -3581,7 +3622,8 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) ...@@ -3581,7 +3622,8 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
fallthrough; fallthrough;
case NETDEV_TX_BUSY: case NETDEV_TX_BUSY:
/* Retry it next time */ /* Retry it next time */
refcount_dec(&(pkt_dev->skb->users)); if (skb_shared)
refcount_dec(&pkt_dev->skb->users);
pkt_dev->last_ok = 0; pkt_dev->last_ok = 0;
} }
if (unlikely(burst)) if (unlikely(burst))
...@@ -3594,6 +3636,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) ...@@ -3594,6 +3636,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
/* If pkt_dev->count is zero, then run forever */ /* If pkt_dev->count is zero, then run forever */
if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
if (pkt_dev->skb)
pktgen_wait_for_skb(pkt_dev); pktgen_wait_for_skb(pkt_dev);
/* Done with this */ /* Done with this */
...@@ -3777,6 +3820,7 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname) ...@@ -3777,6 +3820,7 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
pkt_dev->svlan_id = 0xffff; pkt_dev->svlan_id = 0xffff;
pkt_dev->burst = 1; pkt_dev->burst = 1;
pkt_dev->node = NUMA_NO_NODE; pkt_dev->node = NUMA_NO_NODE;
pkt_dev->flags = F_SHARED; /* SKB shared by default */
err = pktgen_setup_dev(t->net, pkt_dev, ifname); err = pktgen_setup_dev(t->net, pkt_dev, ifname);
if (err) if (err)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment