Skip to content

Commit

Permalink
Merge branch 'rx_copybreak'
Browse files Browse the repository at this point in the history
Govindarajulu Varadarajan says:

====================
enic: Add support for rx_copybreak

The following series implements rx_copybreak.

dma_map_single()/dma_unmap_single() is more expensive than alloc_skb & memcpy
for smaller packets. By doing this we can reuse the dma buff which is already
mapped. This is very useful when iommu is on. The default skb copybreak value
is 256.

When iommu is on, we can go much higher than 256. All the drivers that supports
rx_copybreak provides module parameter to change this value. Since module
parameter is the least preferred way for changing driver values, this series
adds ethtool support for setting rx_copybreak.

v4:
Validate tunable length in ethtool_get_tunable, not in driver implemented
function.

Loose tunable_ops array for each tunable type. Define one function and let the
driver use switch case for each type.

Use double underscore for data type in UAPI headers.
Use const qualifier where possible.

v3:
Add tunable namespace to ethtool. Use new ethtool cmd ETHTOOL_S/GTUNABLE to
set/get rx_copybreak from userspace.

v2:
Add new ethtool_cmd for DMA buffer parameters, instead of adding new members to
existing ethtool_ringparam.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 5, 2014
2 parents e020836 + d4ad30b commit b52b727
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 3 deletions.
1 change: 1 addition & 0 deletions drivers/net/ethernet/cisco/enic/enic.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ struct enic {
____cacheline_aligned struct vnic_cq cq[ENIC_CQ_MAX];
unsigned int cq_count;
struct enic_rfs_flw_tbl rfs_h;
u32 rx_copybreak;
};

static inline struct device *enic_get_dev(struct enic *enic)
Expand Down
39 changes: 39 additions & 0 deletions drivers/net/ethernet/cisco/enic/enic_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,43 @@ static int enic_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
return ret;
}

static int enic_get_tunable(struct net_device *dev,
const struct ethtool_tunable *tuna, void *data)
{
struct enic *enic = netdev_priv(dev);
int ret = 0;

switch (tuna->id) {
case ETHTOOL_RX_COPYBREAK:
*(u32 *)data = enic->rx_copybreak;
break;
default:
ret = -EINVAL;
break;
}

return ret;
}

static int enic_set_tunable(struct net_device *dev,
const struct ethtool_tunable *tuna,
const void *data)
{
struct enic *enic = netdev_priv(dev);
int ret = 0;

switch (tuna->id) {
case ETHTOOL_RX_COPYBREAK:
enic->rx_copybreak = *(u32 *)data;
break;
default:
ret = -EINVAL;
break;
}

return ret;
}

static const struct ethtool_ops enic_ethtool_ops = {
.get_settings = enic_get_settings,
.get_drvinfo = enic_get_drvinfo,
Expand All @@ -391,6 +428,8 @@ static const struct ethtool_ops enic_ethtool_ops = {
.get_coalesce = enic_get_coalesce,
.set_coalesce = enic_set_coalesce,
.get_rxnfc = enic_get_rxnfc,
.get_tunable = enic_get_tunable,
.set_tunable = enic_set_tunable,
};

void enic_set_ethtool_ops(struct net_device *netdev)
Expand Down
50 changes: 47 additions & 3 deletions drivers/net/ethernet/cisco/enic/enic_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
#define PCI_DEVICE_ID_CISCO_VIC_ENET_DYN 0x0044 /* enet dynamic vnic */
#define PCI_DEVICE_ID_CISCO_VIC_ENET_VF 0x0071 /* enet SRIOV VF */

#define RX_COPYBREAK_DEFAULT 256

/* Supported devices */
static const struct pci_device_id enic_id_table[] = {
{ PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET) },
Expand Down Expand Up @@ -924,6 +926,7 @@ static void enic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
pci_unmap_single(enic->pdev, buf->dma_addr,
buf->len, PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(buf->os_buf);
buf->os_buf = NULL;
}

static int enic_rq_alloc_buf(struct vnic_rq *rq)
Expand All @@ -934,7 +937,24 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq)
unsigned int len = netdev->mtu + VLAN_ETH_HLEN;
unsigned int os_buf_index = 0;
dma_addr_t dma_addr;
struct vnic_rq_buf *buf = rq->to_use;

if (buf->os_buf) {
buf = buf->next;
rq->to_use = buf;
rq->ring.desc_avail--;
if ((buf->index & VNIC_RQ_RETURN_RATE) == 0) {
/* Adding write memory barrier prevents compiler and/or
* CPU reordering, thus avoiding descriptor posting
* before descriptor is initialized. Otherwise, hardware
* can read stale descriptor fields.
*/
wmb();
iowrite32(buf->index, &rq->ctrl->posted_index);
}

return 0;
}
skb = netdev_alloc_skb_ip_align(netdev, len);
if (!skb)
return -ENOMEM;
Expand All @@ -957,6 +977,25 @@ static void enic_intr_update_pkt_size(struct vnic_rx_bytes_counter *pkt_size,
pkt_size->small_pkt_bytes_cnt += pkt_len;
}

static bool enic_rxcopybreak(struct net_device *netdev, struct sk_buff **skb,
struct vnic_rq_buf *buf, u16 len)
{
struct enic *enic = netdev_priv(netdev);
struct sk_buff *new_skb;

if (len > enic->rx_copybreak)
return false;
new_skb = netdev_alloc_skb_ip_align(netdev, len);
if (!new_skb)
return false;
pci_dma_sync_single_for_cpu(enic->pdev, buf->dma_addr, len,
DMA_FROM_DEVICE);
memcpy(new_skb->data, (*skb)->data, len);
*skb = new_skb;

return true;
}

static void enic_rq_indicate_buf(struct vnic_rq *rq,
struct cq_desc *cq_desc, struct vnic_rq_buf *buf,
int skipped, void *opaque)
Expand All @@ -978,9 +1017,6 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
return;

skb = buf->os_buf;
prefetch(skb->data - NET_IP_ALIGN);
pci_unmap_single(enic->pdev, buf->dma_addr,
buf->len, PCI_DMA_FROMDEVICE);

cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc,
&type, &color, &q_number, &completed_index,
Expand Down Expand Up @@ -1011,6 +1047,13 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
/* Good receive
*/

if (!enic_rxcopybreak(netdev, &skb, buf, bytes_written)) {
buf->os_buf = NULL;
pci_unmap_single(enic->pdev, buf->dma_addr, buf->len,
PCI_DMA_FROMDEVICE);
}
prefetch(skb->data - NET_IP_ALIGN);

skb_put(skb, bytes_written);
skb->protocol = eth_type_trans(skb, netdev);
skb_record_rx_queue(skb, q_number);
Expand Down Expand Up @@ -2531,6 +2574,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_err(dev, "Cannot register net device, aborting\n");
goto err_out_dev_deinit;
}
enic->rx_copybreak = RX_COPYBREAK_DEFAULT;

return 0;

Expand Down
4 changes: 4 additions & 0 deletions include/linux/ethtool.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ struct ethtool_ops {
struct ethtool_eeprom *, u8 *);
int (*get_eee)(struct net_device *, struct ethtool_eee *);
int (*set_eee)(struct net_device *, struct ethtool_eee *);
int (*get_tunable)(struct net_device *,
const struct ethtool_tunable *, void *);
int (*set_tunable)(struct net_device *,
const struct ethtool_tunable *, const void *);


};
Expand Down
28 changes: 28 additions & 0 deletions include/uapi/linux/ethtool.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,32 @@ struct ethtool_value {
__u32 data;
};

enum tunable_id {
ETHTOOL_ID_UNSPEC,
ETHTOOL_RX_COPYBREAK,
};

enum tunable_type_id {
ETHTOOL_TUNABLE_UNSPEC,
ETHTOOL_TUNABLE_U8,
ETHTOOL_TUNABLE_U16,
ETHTOOL_TUNABLE_U32,
ETHTOOL_TUNABLE_U64,
ETHTOOL_TUNABLE_STRING,
ETHTOOL_TUNABLE_S8,
ETHTOOL_TUNABLE_S16,
ETHTOOL_TUNABLE_S32,
ETHTOOL_TUNABLE_S64,
};

struct ethtool_tunable {
__u32 cmd;
__u32 id;
__u32 type_id;
__u32 len;
void *data[0];
};

/**
* struct ethtool_regs - hardware register dump
* @cmd: Command number = %ETHTOOL_GREGS
Expand Down Expand Up @@ -1152,6 +1178,8 @@ enum ethtool_sfeatures_retval_bits {

#define ETHTOOL_GRSSH 0x00000046 /* Get RX flow hash configuration */
#define ETHTOOL_SRSSH 0x00000047 /* Set RX flow hash configuration */
#define ETHTOOL_GTUNABLE 0x00000048 /* Get tunable configuration */
#define ETHTOOL_STUNABLE 0x00000049 /* Set tunable configuration */

/* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET
Expand Down
81 changes: 81 additions & 0 deletions net/core/ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,80 @@ static int ethtool_get_module_eeprom(struct net_device *dev,
modinfo.eeprom_len);
}

static int ethtool_tunable_valid(const struct ethtool_tunable *tuna)
{
switch (tuna->id) {
case ETHTOOL_RX_COPYBREAK:
if (tuna->len != sizeof(u32) ||
tuna->type_id != ETHTOOL_TUNABLE_U32)
return -EINVAL;
break;
default:
return -EINVAL;
}

return 0;
}

static int ethtool_get_tunable(struct net_device *dev, void __user *useraddr)
{
int ret;
struct ethtool_tunable tuna;
const struct ethtool_ops *ops = dev->ethtool_ops;
void *data;

if (!ops->get_tunable)
return -EOPNOTSUPP;
if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
return -EFAULT;
ret = ethtool_tunable_valid(&tuna);
if (ret)
return ret;
data = kmalloc(tuna.len, GFP_USER);
if (!data)
return -ENOMEM;
ret = ops->get_tunable(dev, &tuna, data);
if (ret)
goto out;
useraddr += sizeof(tuna);
ret = -EFAULT;
if (copy_to_user(useraddr, data, tuna.len))
goto out;
ret = 0;

out:
kfree(data);
return ret;
}

static int ethtool_set_tunable(struct net_device *dev, void __user *useraddr)
{
int ret;
struct ethtool_tunable tuna;
const struct ethtool_ops *ops = dev->ethtool_ops;
void *data;

if (!ops->set_tunable)
return -EOPNOTSUPP;
if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
return -EFAULT;
ret = ethtool_tunable_valid(&tuna);
if (ret)
return ret;
data = kmalloc(tuna.len, GFP_USER);
if (!data)
return -ENOMEM;
useraddr += sizeof(tuna);
ret = -EFAULT;
if (copy_from_user(data, useraddr, tuna.len))
goto out;
ret = ops->set_tunable(dev, &tuna, data);

out:
kfree(data);
return ret;
}

/* The main entry point in this file. Called from net/core/dev_ioctl.c */

int dev_ethtool(struct net *net, struct ifreq *ifr)
Expand Down Expand Up @@ -1670,6 +1744,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_GCHANNELS:
case ETHTOOL_GET_TS_INFO:
case ETHTOOL_GEEE:
case ETHTOOL_GTUNABLE:
break;
default:
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
Expand Down Expand Up @@ -1857,6 +1932,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_GMODULEEEPROM:
rc = ethtool_get_module_eeprom(dev, useraddr);
break;
case ETHTOOL_GTUNABLE:
rc = ethtool_get_tunable(dev, useraddr);
break;
case ETHTOOL_STUNABLE:
rc = ethtool_set_tunable(dev, useraddr);
break;
default:
rc = -EOPNOTSUPP;
}
Expand Down

0 comments on commit b52b727

Please sign in to comment.