diff --git a/net/core/pktgen.c b/net/core/pktgen.c index de8d5cc..30774f1 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -212,7 +212,8 @@ /* Xmit modes */ #define M_START_XMIT 0 /* Default normal TX */ -#define M_NETIF_RECEIVE 1 /* Inject packets into stack */ +#define M_NETIF_RECEIVE 1 /* Inject packets into stack */ +#define M_NETIF_QUEUE_XMIT 2 /* Inject packets into qdisc */ /* If lock -- protects updating of if_list */ #define if_lock(t) spin_lock(&(t->if_lock)); @@ -616,7 +617,8 @@ static int pktgen_if_show(struct seq_file *seq, void *v) seq_printf(seq, " tos: 0x%02x\n", pkt_dev->tos); if (pkt_dev->traffic_class) - seq_printf(seq, " traffic_class: 0x%02x\n", pkt_dev->traffic_class); + seq_printf(seq, " traffic_class: 0x%02x\n", + pkt_dev->traffic_class); if (pkt_dev->burst > 1) seq_printf(seq, " burst: %d\n", pkt_dev->burst); @@ -624,8 +626,12 @@ static int pktgen_if_show(struct seq_file *seq, void *v) if (pkt_dev->node >= 0) seq_printf(seq, " node: %d\n", pkt_dev->node); + if (pkt_dev->xmit_mode == M_START_XMIT) + seq_puts(seq, " xmit_mode: qdisc_bypass\n"); if (pkt_dev->xmit_mode == M_NETIF_RECEIVE) seq_puts(seq, " xmit_mode: netif_receive\n"); + if (pkt_dev->xmit_mode == M_NETIF_QUEUE_XMIT) + seq_puts(seq, " xmit_mode: qdisc_xmit\n"); seq_puts(seq, " Flags: "); @@ -1198,6 +1204,22 @@ static ssize_t pktgen_if_write(struct file *file, * at module loading time */ pkt_dev->clone_skb = 0; + } else if (strcmp(f, "qdisc_xmit") == 0) { + /* clone_skb set earlier, not supported in this mode */ + if (pkt_dev->clone_skb > 0) + return -ENOTSUPP; + + pkt_dev->xmit_mode = M_NETIF_QUEUE_XMIT; + + /* make sure new packet is allocated every time + * pktgen_xmit() is called + */ + pkt_dev->last_ok = 1; + + /* override clone_skb if user passed default value + * at module loading time + */ + pkt_dev->clone_skb = 0; } else { sprintf(pg_result, "xmit_mode -:%s:- unknown\nAvailable modes: %s", @@ -3361,6 +3383,23 @@ static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev) pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start)); } +static inline netdev_tx_t netdevq_start_xmit(struct sk_buff *skb, + struct net_device *dev, + struct netdev_queue *txq, bool more) +{ + const struct net_device_ops *ops = dev->netdev_ops; + int rc; + + skb->xmit_more = more ? 1 : 0; + skb->dev = dev; + rc = dev_queue_xmit(skb); + if (likely(rc == NET_XMIT_SUCCESS || rc == NET_XMIT_CN)) { + txq_trans_update(txq); + } + + return rc; +} + static void pktgen_xmit(struct pktgen_dev *pkt_dev) { unsigned int burst = ACCESS_ONCE(pkt_dev->burst); @@ -3432,8 +3471,54 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) #endif } while (--burst > 0); goto out; /* Skips xmit_mode M_START_XMIT */ + } else if (pkt_dev->xmit_mode == M_NETIF_QUEUE_XMIT) { + + txq = skb_get_tx_queue(odev, pkt_dev->skb); + local_bh_disable(); + + if (unlikely(netif_xmit_frozen_or_drv_stopped(txq))) { + ret = NETDEV_TX_BUSY; + pkt_dev->last_ok = 0; + goto out; + } + atomic_add(burst, &pkt_dev->skb->users); + +qxmit_more: + ret = netdevq_start_xmit(pkt_dev->skb, odev, txq, --burst > 0); + + switch (ret) { + case NETDEV_TX_OK: + pkt_dev->last_ok = 1; + pkt_dev->sofar++; + pkt_dev->seq_num++; + pkt_dev->tx_bytes += pkt_dev->last_pkt_size; + if (burst > 0 && !netif_xmit_frozen_or_drv_stopped(txq)) + goto qxmit_more; + break; + case NET_XMIT_DROP: + case NET_XMIT_CN: + case NET_XMIT_POLICED: + /* skb has been consumed */ + pkt_dev->errors++; + break; + default: /* Drivers are not supposed to return other values! */ + net_info_ratelimited("%s xmit error: %d\n", + pkt_dev->odevname, ret); + pkt_dev->errors++; + /* fallthru */ + case NETDEV_TX_LOCKED: + case NETDEV_TX_BUSY: + /* Retry it next time */ + atomic_dec(&(pkt_dev->skb->users)); + pkt_dev->last_ok = 0; + } + if (unlikely(burst)) + atomic_sub(burst, &pkt_dev->skb->users); + + goto out; /* Skips xmit_mode M_START_XMIT */ } + /*M_START_XMIT*/ txq = skb_get_tx_queue(odev, pkt_dev->skb); local_bh_disable(); @@ -3478,6 +3563,7 @@ xmit_more: } if (unlikely(burst)) atomic_sub(burst, &pkt_dev->skb->users); + unlock: HARD_TX_UNLOCK(odev, txq);