Commit da38c564 authored by David L Stevens's avatar David L Stevens Committed by David S. Miller

sunvnet: add scatter/gather support

This patch adds scatter/gather support to the sunvnet driver.
Signed-off-by: default avatarDavid L Stevens <david.stevens@oracle.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6d0ba919
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/highmem.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
...@@ -978,11 +979,54 @@ static void vnet_clean_timer_expire(unsigned long port0) ...@@ -978,11 +979,54 @@ static void vnet_clean_timer_expire(unsigned long port0)
del_timer(&port->clean_timer); del_timer(&port->clean_timer);
} }
static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, void **pstart, static inline int vnet_skb_map(struct ldc_channel *lp, struct sk_buff *skb,
int *plen) 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; struct sk_buff *nskb;
int len, pad; int i, len, pad, docopy;
len = skb->len; len = skb->len;
pad = 0; pad = 0;
...@@ -992,14 +1036,25 @@ static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, void **pstart, ...@@ -992,14 +1036,25 @@ static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, void **pstart,
} }
len += VNET_PACKET_SKIP; len += VNET_PACKET_SKIP;
pad += 8 - (len & 7); 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 || if (((unsigned long)skb->data & 7) != VNET_PACKET_SKIP ||
skb_tailroom(skb) < pad || skb_tailroom(skb) < pad ||
skb_headroom(skb) < VNET_PACKET_SKIP) { skb_headroom(skb) < VNET_PACKET_SKIP || docopy) {
int offset; 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); skb_reserve(nskb, VNET_PACKET_SKIP);
nskb->protocol = skb->protocol; nskb->protocol = skb->protocol;
...@@ -1022,9 +1077,6 @@ static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, void **pstart, ...@@ -1022,9 +1077,6 @@ static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, void **pstart,
dev_kfree_skb(skb); dev_kfree_skb(skb);
skb = nskb; skb = nskb;
} }
*pstart = skb->data - VNET_PACKET_SKIP;
*plen = len;
return skb; return skb;
} }
...@@ -1049,15 +1101,9 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -1049,15 +1101,9 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
unsigned int len; unsigned int len;
struct sk_buff *freeskbs = NULL; struct sk_buff *freeskbs = NULL;
int i, err, txi; int i, err, txi;
void *start = NULL;
int nlen = 0;
unsigned pending = 0; unsigned pending = 0;
struct netdev_queue *txq; struct netdev_queue *txq;
skb = vnet_skb_shape(skb, &start, &nlen);
if (unlikely(!skb))
goto out_dropped;
rcu_read_lock(); rcu_read_lock();
port = __tx_port_find(vp, skb); port = __tx_port_find(vp, skb);
if (unlikely(!port)) { if (unlikely(!port)) {
...@@ -1097,6 +1143,13 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -1097,6 +1143,13 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
goto out_dropped; 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]; dr = &port->vio.drings[VIO_DRIVER_TX_RING];
i = skb_get_queue_mapping(skb); i = skb_get_queue_mapping(skb);
txq = netdev_get_tx_queue(dev, i); txq = netdev_get_tx_queue(dev, i);
...@@ -1124,16 +1177,15 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -1124,16 +1177,15 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (len < ETH_ZLEN) if (len < ETH_ZLEN)
len = ETH_ZLEN; len = ETH_ZLEN;
port->tx_bufs[txi].skb = skb; err = vnet_skb_map(port->vio.lp, skb, port->tx_bufs[txi].cookies, 2,
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)); (LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_RW));
if (err < 0) { if (err < 0) {
netdev_info(dev, "tx buffer map error %d\n", err); netdev_info(dev, "tx buffer map error %d\n", err);
goto out_dropped; goto out_dropped;
} }
port->tx_bufs[txi].skb = skb;
skb = NULL;
port->tx_bufs[txi].ncookies = err; port->tx_bufs[txi].ncookies = err;
/* We don't rely on the ACKs to free the skb in vnet_start_xmit(), /* 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) ...@@ -1559,6 +1611,9 @@ static struct vnet *vnet_new(const u64 *local_mac)
dev->ethtool_ops = &vnet_ethtool_ops; dev->ethtool_ops = &vnet_ethtool_ops;
dev->watchdog_timeo = VNET_TX_TIMEOUT; dev->watchdog_timeo = VNET_TX_TIMEOUT;
dev->hw_features = NETIF_F_SG;
dev->features = dev->hw_features;
err = register_netdev(dev); err = register_netdev(dev);
if (err) { if (err) {
pr_err("Cannot register net device, aborting\n"); pr_err("Cannot register net device, aborting\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment