IPSEC # IPsec encapsulation (needs CONFIG_XFRM)
                              NODE_ALLOC # node specific memory allocation
                              NO_TIMESTAMP # disable timestamping
+                             SHARED # enable shared SKB
  pgset 'flag ![name]'    Clear a flag to determine behaviour.
                         Note that you might need to use single quote in
                         interactive mode, so that your shell wouldn't expand
 you can use "pgset spi SPI_VALUE" to specify which transformation mode
 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
 ==========================================
     IPSEC
     NODE_ALLOC
     NO_TIMESTAMP
+    SHARED
 
     spi (ipsec)
 
 
        pf(VID_RND)             /* Random VLAN ID */                    \
        pf(SVID_RND)            /* Random SVLAN ID */                   \
        pf(NODE)                /* Node memory alloc*/                  \
+       pf(SHARED)              /* Shared SKB */                        \
 
 #define pf(flag)               flag##_SHIFT,
 enum pkt_flags {
                    ((pkt_dev->xmit_mode == M_NETIF_RECEIVE) ||
                     !(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
                        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;
 
                i += len;
                     ((pkt_dev->xmit_mode == M_START_XMIT) &&
                     (!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))))
                        return -ENOTSUPP;
+
+               if (value > 1 && !(pkt_dev->flags & F_SHARED))
+                       return -EINVAL;
+
                pkt_dev->burst = value < 1 ? 1 : value;
                sprintf(pg_result, "OK: burst=%u", pkt_dev->burst);
                return count;
 
                flag = pktgen_read_flag(f, &disable);
                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;
-                       else
+                       } else {
                                pkt_dev->flags |= flag;
+                       }
 
                        sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags);
                        return count;
 
 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 netdev_queue *txq;
+       unsigned int burst = 1;
        struct sk_buff *skb;
+       int clone_skb = 0;
        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 (unlikely(!netif_running(odev) || !netif_carrier_ok(odev))) {
                pktgen_stop_device(pkt_dev);
 
        /* If no skb or clone count exhausted then get new one */
        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 */
                kfree_skb(pkt_dev->skb);
 
        if (pkt_dev->xmit_mode == M_NETIF_RECEIVE) {
                skb = pkt_dev->skb;
                skb->protocol = eth_type_trans(skb, skb->dev);
-               refcount_add(burst, &skb->users);
+               if (skb_shared)
+                       refcount_add(burst, &skb->users);
                local_bh_disable();
                do {
                        ret = netif_receive_skb(skb);
                                pkt_dev->errors++;
                        pkt_dev->sofar++;
                        pkt_dev->seq_num++;
+                       if (unlikely(!skb_shared)) {
+                               pkt_dev->skb = NULL;
+                               break;
+                       }
                        if (refcount_read(&skb->users) != burst) {
                                /* skb was queued by rps/rfs or taps,
                                 * so cannot reuse this skb
                goto out; /* Skips xmit_mode M_START_XMIT */
        } else if (pkt_dev->xmit_mode == M_QUEUE_XMIT) {
                local_bh_disable();
-               refcount_inc(&pkt_dev->skb->users);
+               if (skb_shared)
+                       refcount_inc(&pkt_dev->skb->users);
 
                ret = dev_queue_xmit(pkt_dev->skb);
+
+               if (!skb_shared && dev_xmit_complete(ret))
+                       pkt_dev->skb = NULL;
+
                switch (ret) {
                case NET_XMIT_SUCCESS:
                        pkt_dev->sofar++;
                pkt_dev->last_ok = 0;
                goto unlock;
        }
-       refcount_add(burst, &pkt_dev->skb->users);
+       if (skb_shared)
+               refcount_add(burst, &pkt_dev->skb->users);
 
 xmit_more:
        ret = netdev_start_xmit(pkt_dev->skb, odev, txq, --burst > 0);
 
+       if (!skb_shared && dev_xmit_complete(ret))
+               pkt_dev->skb = NULL;
+
        switch (ret) {
        case NETDEV_TX_OK:
                pkt_dev->last_ok = 1;
                fallthrough;
        case NETDEV_TX_BUSY:
                /* Retry it next time */
-               refcount_dec(&(pkt_dev->skb->users));
+               if (skb_shared)
+                       refcount_dec(&pkt_dev->skb->users);
                pkt_dev->last_ok = 0;
        }
        if (unlikely(burst))
 
        /* If pkt_dev->count is zero, then run forever */
        if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
-               pktgen_wait_for_skb(pkt_dev);
+               if (pkt_dev->skb)
+                       pktgen_wait_for_skb(pkt_dev);
 
                /* Done with this */
                pktgen_stop_device(pkt_dev);
        pkt_dev->svlan_id = 0xffff;
        pkt_dev->burst = 1;
        pkt_dev->node = NUMA_NO_NODE;
+       pkt_dev->flags = F_SHARED;      /* SKB shared by default */
 
        err = pktgen_setup_dev(t->net, pkt_dev, ifname);
        if (err)