sunvnet: add scatter/gather support
This patch adds scatter/gather support to the sunvnet driver. Signed-off-by: David L Stevens <david.stevens@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
6d0ba91991
commit
da38c56417
|
@ -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)
|
||||||
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,
|
||||||
}
|
}
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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;
|
(LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_RW));
|
||||||
|
|
||||||
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));
|
|
||||||
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)
|
||||||
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");
|
||||||
|
|
Loading…
Reference in New Issue