On Mon, Jan 09, 2006 at 04:43:08AM +0000, Paul Janzen wrote:
> Paul Janzen <[EMAIL PROTECTED]> writes:
> > In mv643xx_eth_start_xmit:
> > [...]
> >     spin_lock_irqsave(&mp->lock, flags);
> > [...]
> >             /* Since hardware can't handle unaligned fragments smaller
> >              * than 9 bytes, if we find any, we linearize the skb
> >              * and start again. */
> > [...]
> >                             skb_linearize(skb, GFP_ATOMIC);
> > [...]
> >
> > which ends up calling kunmap_skb_frag(vaddr), which, when
> > CONFIG_HIGHMEM=y, calls local_bh_enable with interrupts off.
> 
> A patch for this problem is enclosed.  I believe it also solves a
> potential deadlock if skb_linearize() returns -ENOMEM. 
> 
> Signed-off-by: Paul Janzen <[EMAIL PROTECTED]>

Thank you Paul.  Good find and fix.  I have one question below.

> --- a/drivers/net/mv643xx_eth.c       2005-12-24 15:47:48.000000000 -0800
> +++ b/drivers/net/mv643xx_eth.c       2006-01-08 20:30:25.000000000 -0800
> @@ -1093,6 +1093,27 @@ static int mv643xx_poll(struct net_devic
>  }
>  #endif
>  
> +/* Hardware can't handle unaligned fragments smaller than 9 bytes.
> + * This helper function detects that case.  When I've seen it, it's
> + * always been the first frag (probably near the end of the page), but
> + * we check all frags to be safe.
> + */

By the way, the above comment is mine, but since then, I have seen small,
unaligned fragments beyond the first frag, so I'll remove the last
sentence.

> +static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb)
> +{
> +        unsigned int frag;
> +        skb_frag_t *fragp;
> +
> +        for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
> +                fragp = &skb_shinfo(skb)->frags[frag];
> +                if (fragp->size <= 8 && fragp->page_offset & 0x7) 
> +                        return 1;
> +                
> +        }
> +        return 0;
> +}
> +
> +
>  /*
>   * mv643xx_eth_start_xmit
>   *
> @@ -1136,12 +1157,22 @@ static int mv643xx_eth_start_xmit(struct
>               return 1;
>       }
>  
> +        if (has_tiny_unaligned_frags(skb)) {
> +                if ((skb_linearize(skb, GFP_ATOMIC) != 0) 
> +                    || has_tiny_unaligned_frags(skb)) { 

There is no way that skb_linearize can return without removing all frags
from the skb.  So the extra call to has_tiny_unaligned_frags() is
unnecessary, right?

-Dale
-
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