sunvnet: add scatter/gather support
Subsystems (2)
PATCH 3/6 sunvnet: add scatter/gather support
This patch adds scatter/gather support to the sunvnet driver.
Signed-off-by David L Stevens <[email protected]>
--- drivers/net/ethernet/sun/sunvnet.c | 97 ++++++++++++++++++++++++++++-------- 1 files changed, 76 insertions(+), 21 deletions(-)
drivers/net/ethernet/sun/sunvnet.c +76 -21
@@ -15,6 +15,7 @@#include <linux/ethtool.h> #include <linux/etherdevice.h> #include <linux/mutex.h> +#include <linux/highmem.h> #include <linux/if_vlan.h> #if IS_ENABLED (CONFIG_IPV6 )
@@ -978,11 +979,54 @@ static void vnet_clean_timer_expire(unsigned long port0)del_timer (&port->clean_timer); } -static inline struct sk_buff *vnet_skb_shape (struct sk_buff *skb,void **pstart, -int *plen) +static inline int vnet_skb_map (struct ldc_channel *lp,struct sk_buff *skb, +struct ldc_trans_cookie *cookies,int ncookies, +unsigned int map_perm) +{ +int i, nc, err, blen; + +/* header */ + blen =skb_headlen (skb); +if (blen <ETH_ZLEN ) + blen =ETH_ZLEN ; + blen +=VNET_PACKET_SKIP ; + blen +=8 - (blen &7 ); + + err =ldc_map_single (lp, skb->data-VNET_PACKET_SKIP , blen, cookies, + ncookies, map_perm); +if (err <0 ) +return err; + nc = err; + +for (i =0 ; i <skb_shinfo (skb)->nr_frags; i++) { +skb_frag_t *f = &skb_shinfo (skb)->frags[i]; +u8 *vaddr; + +if (nc < ncookies) { + vaddr =kmap_atomic (skb_frag_page (f)); + blen =skb_frag_size (f); + blen +=8 - (blen &7 ); + err =ldc_map_single (lp, vaddr + f->page_offset, + blen, cookies + nc, ncookies - nc, + map_perm); +kunmap_atomic (vaddr); + }else { + err = -EMSGSIZE ; + } + +if (err <0 ) { +ldc_unmap (lp, cookies, nc); +return err; + } + nc += err; + } +return nc; +} + +static inline struct sk_buff *vnet_skb_shape (struct sk_buff *skb,int ncookies) {struct sk_buff *nskb; -int len, pad; +int i, len, pad, docopy; len = skb->len; pad =0 ;
@@ -992,14 +1036,25 @@ static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, void **pstart, } len +=VNET_PACKET_SKIP ; pad +=8 - (len &7 ); - len +=8 - (len &7 ); +/* make sure we have enough cookies and alignment in every frag */ + docopy =skb_shinfo (skb)->nr_frags >= ncookies; +for (i =0 ; i <skb_shinfo (skb)->nr_frags; i++) { +skb_frag_t *f = &skb_shinfo (skb)->frags[i]; + + docopy |= f->page_offset &7 ; + }if (((unsigned long )skb->data &7 ) !=VNET_PACKET_SKIP ||skb_tailroom (skb) < pad || -skb_headroom (skb) <VNET_PACKET_SKIP ) { +skb_headroom (skb) <VNET_PACKET_SKIP || docopy) {int offset; - nskb =alloc_and_align_skb (skb->dev, skb->len); + len = skb->len >ETH_ZLEN ? skb->len :ETH_ZLEN ; + nskb =alloc_and_align_skb (skb->dev, len); +if (nskb ==NULL ) { +dev_kfree_skb (skb); +return NULL ; + }skb_reserve (nskb,VNET_PACKET_SKIP ); nskb->protocol = skb->protocol;
@@ -1022,9 +1077,6 @@ static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, void **pstart,dev_kfree_skb (skb); skb = nskb; } - - *pstart = skb->data -VNET_PACKET_SKIP ; - *plen = len;return skb; }
@@ -1049,15 +1101,9 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)unsigned int len;struct sk_buff *freeskbs =NULL ;int i, err, txi; -void *start =NULL ; -int nlen =0 ;unsigned pending =0 ;struct netdev_queue *txq; - skb =vnet_skb_shape (skb, &start, &nlen); -if (unlikely (!skb)) - goto out_dropped; -rcu_read_lock (); port =__tx_port_find (vp, skb);if (unlikely (!port)) {
@@ -1097,6 +1143,13 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev) goto out_dropped; } + skb =vnet_skb_shape (skb,2 ); + +if (unlikely (!skb)) { +rcu_read_unlock (); + goto out_dropped; + } + dr = &port->vio.drings[VIO_DRIVER_TX_RING ]; i =skb_get_queue_mapping (skb); txq =netdev_get_tx_queue (dev, i);
@@ -1124,16 +1177,15 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)if (len <ETH_ZLEN ) len =ETH_ZLEN ; - port->tx_bufs[txi].skb = skb; - skb =NULL ; - - err =ldc_map_single (port->vio.lp, start, nlen, - port->tx_bufs[txi].cookies,VNET_MAXCOOKIES , - (LDC_MAP_SHADOW |LDC_MAP_DIRECT |LDC_MAP_RW )); + err =vnet_skb_map (port->vio.lp, skb, port->tx_bufs[txi].cookies,2 , + (LDC_MAP_SHADOW |LDC_MAP_DIRECT |LDC_MAP_RW ));if (err <0 ) {netdev_info (dev,"tx buffer map error %d\n" , err); goto out_dropped; } + + port->tx_bufs[txi].skb = skb; + skb =NULL ; port->tx_bufs[txi].ncookies = err;/* We don't rely on the ACKs to free the skb in vnet_start_xmit(),
@@ -1559,6 +1611,9 @@ static struct vnet *vnet_new(const u64 *local_mac) dev->ethtool_ops = &vnet_ethtool_ops; dev->watchdog_timeo =VNET_TX_TIMEOUT ; + dev->hw_features =NETIF_F_SG ; + dev->features = dev->hw_features; + err =register_netdev (dev);if (err) {pr_err ("Cannot register net device, aborting\n" );
Signature · 1 line
1.7.1