summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/freescale/gianfar_sysfs.c
diff options
context:
space:
mode:
authorClaudiu Manoil <claudiu.manoil@freescale.com>2013-03-27 11:02:47 (GMT)
committerFleming Andrew-AFLEMING <AFLEMING@freescale.com>2013-04-03 21:14:41 (GMT)
commitcabbbf0bc6013f3ed6249837187dd5c39d0d0ed9 (patch)
treee9b02828d37c0310e3f71049847cf9d2f95ab655 /drivers/net/ethernet/freescale/gianfar_sysfs.c
parent6c4f4b44e87a93c39156ce8639b8efc311a76314 (diff)
downloadlinux-fsl-qoriq-cabbbf0bc6013f3ed6249837187dd5c39d0d0ed9.tar.xz
gianfar: Add basic skb recycling
Introduce per device skb recycle queues, in order to reuse 'hot' skbs on the device's Rx path without allocating new ones. The skb recycling infrastructure is taken from the mainline kernel 3.0 (see skb_is_recycleable() and skb_recycle()). The recycle queue accesses (enqueue/ dequeue skbs) are protected by the skb queue lock, b/w the Rx and Tx paths. There are two work modes when recycling skbs: 1) the device recycles its own skbs: the skbs 'freed' on the device's Tx path are put back into the same device's recycle queue; 2) the transmitting device recycles the skbs to the targeted receiving device's recycle queue, to benefit ipv4 forwarding scenarios. The 'recycle_target' read/write sysfs entry is provided to select b/w the two work modes for a gfar (gianfar) interface. e.g. for the gfar interface 'eth1', by default 'recycle_target' is set to 'eth1', meaning the the device operates in mode 1. When forwarding packets b/w the two gfar devices 'eth2'->'eth1', the 'recycle_target' entry for 'eth1' should be set to 'eth2', to successfully enable operation mode 2. The readonly 'recyle' sysfs entry provides skb recycling statistics for a given gfar device. Taken from the mainline kernel 3.0, where it operated only in mode 1, and adapted to work in packet forwarding scenarios too (mode 2). Signed-off-by: Claudiu Manoil <claudiu.manoil@freescale.com> Change-Id: Id5f705759e1519cc42d5fbf94fd7542eb4661ee4 Reviewed-on: http://git.am.freescale.net:8181/870 Reviewed-by: Fleming Andrew-AFLEMING <AFLEMING@freescale.com> Tested-by: Fleming Andrew-AFLEMING <AFLEMING@freescale.com>
Diffstat (limited to 'drivers/net/ethernet/freescale/gianfar_sysfs.c')
-rw-r--r--drivers/net/ethernet/freescale/gianfar_sysfs.c70
1 files changed, 70 insertions, 0 deletions
diff --git a/drivers/net/ethernet/freescale/gianfar_sysfs.c b/drivers/net/ethernet/freescale/gianfar_sysfs.c
index cd14a4d..2ea7008 100644
--- a/drivers/net/ethernet/freescale/gianfar_sysfs.c
+++ b/drivers/net/ethernet/freescale/gianfar_sysfs.c
@@ -319,6 +319,74 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
static DEVICE_ATTR(fifo_starve_off, 0644, gfar_show_fifo_starve_off,
gfar_set_fifo_starve_off);
+static ssize_t gfar_show_recycle(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar_priv_recycle *rec = &priv->recycle;
+
+ pr_info("recycled skbs: %d\nreused skbs: %d\n",
+ atomic_read(&rec->recycle_cnt),
+ atomic_read(&rec->reuse_cnt));
+
+ return sprintf(buf, "%s\n", priv->ndev->name);
+}
+
+static DEVICE_ATTR(recycle, 0444, gfar_show_recycle, NULL);
+
+static ssize_t gfar_show_recycle_target(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+
+ return sprintf(buf, "%s\n", priv->recycle_ndev->name);
+}
+
+static ssize_t gfar_set_recycle_target(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct gfar_private *priv = netdev_priv(ndev);
+ struct gfar_private *priv_target;
+ int found = 0;
+
+ list_for_each_entry(priv_target, &gfar_recycle_queues, recycle_node) {
+ char *name = priv_target->ndev->name;
+ if ((strlen(name) == count - 1) &&
+ !strncmp(buf, name, count - 1)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ pr_err("Invalid skb recycle target device!\n");
+ return count;
+ }
+
+ if (priv->recycle_target == &priv_target->recycle)
+ /* nothing to do */
+ return count;
+
+ if (ndev->flags & IFF_UP)
+ stop_gfar(ndev);
+
+ priv->recycle_target = &priv_target->recycle;
+ priv->recycle_ndev = priv_target->ndev;
+
+ if (ndev->flags & IFF_UP)
+ startup_gfar(ndev);
+
+ return count;
+}
+
+static DEVICE_ATTR(recycle_target, 0644, gfar_show_recycle_target,
+ gfar_set_recycle_target);
+
+
void gfar_init_sysfs(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
@@ -336,6 +404,8 @@ void gfar_init_sysfs(struct net_device *dev)
rc |= device_create_file(&dev->dev, &dev_attr_fifo_threshold);
rc |= device_create_file(&dev->dev, &dev_attr_fifo_starve);
rc |= device_create_file(&dev->dev, &dev_attr_fifo_starve_off);
+ rc |= device_create_file(&dev->dev, &dev_attr_recycle);
+ rc |= device_create_file(&dev->dev, &dev_attr_recycle_target);
if (rc)
dev_err(&dev->dev, "Error creating gianfar sysfs files.\n");
}