static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
{
    struct net_device *odev = pkt_dev->odev;
    int (*xmit)(struct sk_buff *, struct net_device *)
        = odev->netdev_ops->ndo_start_xmit;
    struct netdev_queue *txq;
    __u64 idle_start = 0;
    u16 queue_map;
    int ret;

    if (pkt_dev->delay_us || pkt_dev->delay_ns) {
        u64 now;

        now = getCurUs();
        if (now < pkt_dev->next_tx_us)
            spin(pkt_dev, pkt_dev->next_tx_us);

        /* This is max DELAY, this has special meaning of
         * "never transmit"
         */
        if (pkt_dev->delay_us == 0x7FFFFFFF) {
            pkt_dev->next_tx_us = getCurUs() + pkt_dev->delay_us;
            pkt_dev->next_tx_ns = pkt_dev->delay_ns;
            goto out;
        }
    }

    if (!pkt_dev->skb) {
        set_cur_queue_map(pkt_dev);
        queue_map = pkt_dev->cur_queue_map;
    } else {
        queue_map = skb_get_queue_mapping(pkt_dev->skb);
    }

    txq = netdev_get_tx_queue(odev, queue_map);
    if (netif_tx_queue_stopped(txq) ||
        netif_tx_queue_frozen(txq) ||
        need_resched()) {
        idle_start = getCurUs();

        if (!netif_running(odev)) {
            pktgen_stop_device(pkt_dev);
            kfree_skb(pkt_dev->skb);
            pkt_dev->skb = NULL;
            goto out;
        }
        if (need_resched())
            schedule();

        pkt_dev->idle_acc += getCurUs() - idle_start;

        if (netif_tx_queue_stopped(txq) ||
            netif_tx_queue_frozen(txq)) {
            pkt_dev->next_tx_us = getCurUs();    /* TODO */
            pkt_dev->next_tx_ns = 0;
            goto out;    /* Try the next interface */
        }
    }

    if (pkt_dev->last_ok || !pkt_dev->skb) {
        if ((++pkt_dev->clone_count >= pkt_dev->clone_skb)
            || (!pkt_dev->skb)) {
            /* build a new pkt */
            kfree_skb(pkt_dev->skb);

            pkt_dev->skb = fill_packet(odev, pkt_dev);
            if (pkt_dev->skb == NULL) {
                printk(KERN_ERR "pktgen: ERROR: couldn't "
                       "allocate skb in fill_packet.\n");
                schedule();
                pkt_dev->clone_count--;    /* back out increment, OOM */
                goto out;
            }
            pkt_dev->allocated_skbs++;
            pkt_dev->clone_count = 0;    /* reset counter */
        }
    }

    /* fill_packet() might have changed the queue */
    queue_map = skb_get_queue_mapping(pkt_dev->skb);
    txq = netdev_get_tx_queue(odev, queue_map);

    __netif_tx_lock_bh(txq);
    if (!netif_tx_queue_stopped(txq) &&
        !netif_tx_queue_frozen(txq)) {

        atomic_inc(&(pkt_dev->skb->users));
          retry_now:
        ret = (*xmit)(pkt_dev->skb, odev);
        if (likely(ret == NETDEV_TX_OK)) {
            pkt_dev->last_ok = 1;
            pkt_dev->sofar++;
            pkt_dev->seq_num++;
            pkt_dev->tx_bytes += pkt_dev->cur_pkt_size;

        } else if (ret == NETDEV_TX_LOCKED
               && (odev->features & NETIF_F_LLTX)) {
            cpu_relax();
            goto retry_now;
        } else {    /* Retry it next time */

            atomic_dec(&(pkt_dev->skb->users));

            if (debug && net_ratelimit())
                printk(KERN_INFO "pktgen: Hard xmit error\n");

            pkt_dev->errors++;
            pkt_dev->last_ok = 0;
        }

        pkt_dev->next_tx_us = getCurUs();
        pkt_dev->next_tx_ns = 0;

        pkt_dev->next_tx_us += pkt_dev->delay_us;
        pkt_dev->next_tx_ns += pkt_dev->delay_ns;

        if (pkt_dev->next_tx_ns > 1000) {
            pkt_dev->next_tx_us++;
            pkt_dev->next_tx_ns -= 1000;
        }
    }

    else {            /* Retry it next time */
        pkt_dev->last_ok = 0;
        pkt_dev->next_tx_us = getCurUs();    /* TODO */
        pkt_dev->next_tx_ns = 0;
    }

    __netif_tx_unlock_bh(txq);

    /* If pkt_dev->count is zero, then run forever */
    if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
        if (atomic_read(&(pkt_dev->skb->users)) != 1) {
            idle_start = getCurUs();
            while (atomic_read(&(pkt_dev->skb->users)) != 1) {
                if (signal_pending(current)) {
                    break;
                }
                schedule();
            }
            pkt_dev->idle_acc += getCurUs() - idle_start;
        }

        /* Done with this */
        pktgen_stop_device(pkt_dev);
        kfree_skb(pkt_dev->skb);
        pkt_dev->skb = NULL;
    }
out:;
}

/*
 * Main loop of the thread goes here
 */

static int pktgen_thread_worker(void *arg)
{
    DEFINE_WAIT(wait);
    struct pktgen_thread *t = arg;
    struct pktgen_dev *pkt_dev = NULL;
    int cpu = t->cpu;

    BUG_ON(smp_processor_id() != cpu);

    init_waitqueue_head(&t->queue);
    complete(&t->start_done);

    pr_debug("pktgen: starting pktgen/%d:  pid=%d\n", cpu, task_pid_nr(current));

    set_current_state(TASK_INTERRUPTIBLE);

    set_freezable();

    while (!kthread_should_stop()) {
        pkt_dev = next_to_run(t);

        if (!pkt_dev &&
            (t->control & (T_STOP | T_RUN | T_REMDEVALL | T_REMDEV))
            == 0) {
            prepare_to_wait(&(t->queue), &wait,
                    TASK_INTERRUPTIBLE);
            schedule_timeout(HZ / 10);
            finish_wait(&(t->queue), &wait);
        }

        __set_current_state(TASK_RUNNING);

        if (pkt_dev)
            pktgen_xmit(pkt_dev);

        if (t->control & T_STOP) {
            pktgen_stop(t);
            t->control &= ~(T_STOP);
        }

        if (t->control & T_RUN) {
            pktgen_run(t);
            t->control &= ~(T_RUN);
        }

        if (t->control & T_REMDEVALL) {
            pktgen_rem_all_ifs(t);
            t->control &= ~(T_REMDEVALL);
        }

        if (t->control & T_REMDEV) {
            pktgen_rem_one_if(t);
            t->control &= ~(T_REMDEV);
        }

        try_to_freeze();

        set_current_state(TASK_INTERRUPTIBLE);
    }

    pr_debug("pktgen: %s stopping all device\n", t->tsk->comm);
    pktgen_stop(t);

    pr_debug("pktgen: %s removing all device\n", t->tsk->comm);
    pktgen_rem_all_ifs(t);

    pr_debug("pktgen: %s removing thread.\n", t->tsk->comm);
    pktgen_rem_thread(t);

    return 0;
}

Reply via email to