f_phonet: use page-sized rather than MTU-sized RX buffers
authorRémi Denis-Courmont <remi.denis-courmont@nokia.com>
Thu, 6 Aug 2009 21:56:44 +0000 (21:56 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 13 Aug 2009 03:44:49 +0000 (20:44 -0700)
Instead of a large (physically) linear buffer, we generate a set of
paged sk_buff, so no extra memory copy is involved. This removes
high-order allocations and saves quite a bit of memory. Phonet MTU is
65541 bytes, so the two buffers were padded to 128 kilo-bytes each.
Now, we create 17 page buffers, almost a 75% memory use reduction.

Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/usb/gadget/f_phonet.c

index f4eff7c..d2de10b 100644 (file)
 #include "u_phonet.h"
 
 #define PN_MEDIA_USB   0x1B
+#define MAXPACKET      512
+#if (PAGE_SIZE % MAXPACKET)
+#error MAXPACKET must divide PAGE_SIZE!
+#endif
 
 /*-------------------------------------------------------------------------*/
 
@@ -45,6 +49,10 @@ struct phonet_port {
 
 struct f_phonet {
        struct usb_function             function;
+       struct {
+               struct sk_buff          *skb;
+               spinlock_t              lock;
+       } rx;
        struct net_device               *dev;
        struct usb_ep                   *in_ep, *out_ep;
 
@@ -52,7 +60,7 @@ struct f_phonet {
        struct usb_request              *out_reqv[0];
 };
 
-static int phonet_rxq_size = 2;
+static int phonet_rxq_size = 17;
 
 static inline struct f_phonet *func_to_pn(struct usb_function *f)
 {
@@ -138,7 +146,7 @@ pn_hs_sink_desc = {
 
        .bEndpointAddress =     USB_DIR_OUT,
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       cpu_to_le16(512),
+       .wMaxPacketSize =       cpu_to_le16(MAXPACKET),
 };
 
 static struct usb_endpoint_descriptor
@@ -298,21 +306,21 @@ static void pn_net_setup(struct net_device *dev)
 static int
 pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
 {
-       struct sk_buff *skb;
-       const size_t size = fp->dev->mtu;
+       struct net_device *dev = fp->dev;
+       struct page *page;
        int err;
 
-       skb = alloc_skb(size, gfp_flags);
-       if (!skb)
+       page = __netdev_alloc_page(dev, gfp_flags);
+       if (!page)
                return -ENOMEM;
 
-       req->buf = skb->data;
-       req->length = size;
-       req->context = skb;
+       req->buf = page_address(page);
+       req->length = PAGE_SIZE;
+       req->context = page;
 
        err = usb_ep_queue(fp->out_ep, req, gfp_flags);
        if (unlikely(err))
-               dev_kfree_skb_any(skb);
+               netdev_free_page(dev, page);
        return err;
 }
 
@@ -320,25 +328,37 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
 {
        struct f_phonet *fp = ep->driver_data;
        struct net_device *dev = fp->dev;
-       struct sk_buff *skb = req->context;
+       struct page *page = req->context;
+       struct sk_buff *skb;
+       unsigned long flags;
        int status = req->status;
 
        switch (status) {
        case 0:
-               if (unlikely(!netif_running(dev)))
-                       break;
-               if (unlikely(req->actual < 1))
+               spin_lock_irqsave(&fp->rx.lock, flags);
+               skb = fp->rx.skb;
+               if (!skb)
+                       skb = fp->rx.skb = netdev_alloc_skb(dev, 12);
+               if (req->actual < req->length) /* Last fragment */
+                       fp->rx.skb = NULL;
+               spin_unlock_irqrestore(&fp->rx.lock, flags);
+
+               if (unlikely(!skb))
                        break;
-               skb_put(skb, req->actual);
-               skb->protocol = htons(ETH_P_PHONET);
-               skb_reset_mac_header(skb);
-               __skb_pull(skb, 1);
-               skb->dev = dev;
-               dev->stats.rx_packets++;
-               dev->stats.rx_bytes += skb->len;
-
-               netif_rx(skb);
-               skb = NULL;
+               skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, 0,
+                               req->actual);
+               page = NULL;
+
+               if (req->actual < req->length) { /* Last fragment */
+                       skb->protocol = htons(ETH_P_PHONET);
+                       skb_reset_mac_header(skb);
+                       pskb_pull(skb, 1);
+                       skb->dev = dev;
+                       dev->stats.rx_packets++;
+                       dev->stats.rx_bytes += skb->len;
+
+                       netif_rx(skb);
+               }
                break;
 
        /* Do not resubmit in these cases: */
@@ -356,8 +376,8 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
                break;
        }
 
-       if (skb)
-               dev_kfree_skb_any(skb);
+       if (page)
+               netdev_free_page(dev, page);
        if (req)
                pn_rx_submit(fp, req, GFP_ATOMIC);
 }
@@ -375,6 +395,10 @@ static void __pn_reset(struct usb_function *f)
 
        usb_ep_disable(fp->out_ep);
        usb_ep_disable(fp->in_ep);
+       if (fp->rx.skb) {
+               dev_kfree_skb_irq(fp->rx.skb);
+               fp->rx.skb = NULL;
+       }
 }
 
 static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
@@ -573,6 +597,7 @@ int __init phonet_bind_config(struct usb_configuration *c)
        fp->function.set_alt = pn_set_alt;
        fp->function.get_alt = pn_get_alt;
        fp->function.disable = pn_disconnect;
+       spin_lock_init(&fp->rx.lock);
 
        err = usb_add_function(c, &fp->function);
        if (err)