> - "if (ret == NETDEV_TX_LOCKED && lockless)" is buggy, and 
> the lockless
>   check should be removed, since driver will return 
> NETDEV_TX_LOCKED only
>   if lockless is true and driver has to do the locking. In 
> the original
>   code as well as the latest code, this code can result in a bug where
>   if LLTX is not set for a driver (lockless == 0) but the 
> driver is written
>   wrongly to do a trylock (despite LLTX being set), the driver returns
>   LOCKED. But since lockless is zero, the packet is requeue'd 
> instead of
>   calling collision code, issuing a warning and freeing up 
> the skb. This
>   skb will be retried with this driver next time, and the 
> same result will
>   ensue. Removing this check will catch these driver bugs 
> instead of hiding
>   the problem. I am keeping this change to readability section since :
>       a. it is confusing to check two things as it is; and
>       b. it is difficult to keep this check in the changed 
> 'switch' code.

I agree that the case shouldn't happen, and will only surface if the
driver is indeed buggy.  I've thought about this conditional being
removed for awhile, since it will protect against a poorly written
driver wrt locking, but then again a driver behaving like that shouldn't
be making it into the kernel.  That being said, out-of-tree drivers are
heavily used (we have an out-of-tree e1000), and something like this
could be missed.  But since that is the corner case of a crappy driver,
I'm ok with this being removed.

> 
> - Changed some names, like try_get_tx_pkt to dequeue_skb (as 
> that is the work
>   being done and easier to understand) and do_dev_requeue to 
> requeue_skb,

These, at a glance, look very similar to the qdisc ->enqueue() and
->dequeue() function pointers.  I know I had to look a few times to
realize they were separate calls through the qdisc and this new function
name.  Perhaps dequeue_skb() can become dev_dequeue_skb() and
requeue_skb() can be dev_requeue_skb()?  Something that helps
differentiate these from the function pointers of the qdisc_ops might
help distinguish what layer the operation is running on.

> +static inline int handle_dev_cpu_collision(struct sk_buff *skb,
> +                                        struct net_device *dev,
> +                                        struct Qdisc *q)
>  {
> -     int ret = handle_dev_cpu_collision(dev);
> +     int ret;
>  
> -     if (ret == SCHED_TX_DROP) {
> +     if (unlikely(dev->xmit_lock_owner == smp_processor_id())) {
> +             /*
> +              * Same CPU holding the lock. It may be a transient
> +              * configuration error, when hard_start_xmit() 
> recurses. We
> +              * detect it by checking xmit owner and drop 
> the packet when
> +              * deadloop is detected. Return OK to try the next skb.
> +              */
>               kfree_skb(skb);
> -             return qdisc_qlen(q);
> +             if (net_ratelimit())
> +                     printk(KERN_DEBUG "Dead loop on netdevice %s, "
> +                            "fix it urgently!\n", dev->name);
> +             ret = qdisc_qlen(q);
> +     } else {
> +             /*
> +              * Another cpu is holding lock, requeue & delay 
> xmits for
> +              * some time.
> +              */
> +             __get_cpu_var(netdev_rx_stat).cpu_collision++;
> +             ret = requeue_skb(skb, dev, q);
>       }
>  
> -     return do_dev_requeue(skb, dev, q);
> +     return ret;
>  }

I like the single return here.

>  static inline int qdisc_restart(struct net_device *dev)  {
>       struct Qdisc *q = dev->qdisc;
> -     unsigned lockless = (dev->features & NETIF_F_LLTX);
>       struct sk_buff *skb;
> +     unsigned lockless;
>       int ret;
>  
> -     skb = try_get_tx_pkt(dev, q);
> -     if (skb == NULL)
> +     /* Dequeue packet */
> +     if (unlikely((skb = dequeue_skb(dev, q)) == NULL))
>               return 0;

I know there's been discussion on the driver side of the world to stop
using unlikely() and likely() clauses.  I personally think it's ok in
situations like this where you have one corner-case conditional without
an else, but it's something to keep in mind when using them in
new/rewritten code like this.

>       if (!netif_queue_stopped(dev))
>               ret = dev_hard_start_xmit(skb, dev);

I thought you were removing this check of netif_queue_stopped(dev)?
Nevermind, I see the followup patch takes care of this.

> +     switch (ret) {
> +     case NETDEV_TX_OK:
> +             /* Driver sent out skb successfully */
> +             ret = qdisc_qlen(q);
> +             break;
> +
> +     case NETDEV_TX_LOCKED:
> +             /* Driver try lock failed */
> +             ret = handle_dev_cpu_collision(skb, dev, q);
> +             break;
> +
> +     default:
> +             /* Driver returned NETDEV_TX_BUSY - requeue skb */
> +             ret = requeue_skb(skb, dev, q);
> +             break;
> +     }

Ooo.  I like this; very clean and simple.

The patch looks fine to me outside of the choice of names for
dequeue_skb() and requeue_skb(), but I can be easily swayed.

Cheers,

-PJ Waskiewicz
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to