All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] driver core: add device_poll interface
@ 2015-01-26 22:46 Xander Huff
  2015-01-26 22:46 ` [PATCH 2/2] net: add device_poll functionality common to all net devices Xander Huff
  2015-01-29 22:32 ` [PATCH 1/2] driver core: add device_poll interface David Miller
  0 siblings, 2 replies; 3+ messages in thread
From: Xander Huff @ 2015-01-26 22:46 UTC (permalink / raw)
  To: gregkh, davem, jeff.westfahl
  Cc: netdev, linux-kernel, jaeden.amero, ben.shelton, brad.mouring,
	rich.tollerton

From: Jeff Westfahl <jeff.westfahl@ni.com>

Add the device_poll interface to the driver core. This is a generic
interface that any struct device can take advantage of to dynamically
switch between using interrupts and polling. Many drivers can be easily
modified to take advantage of this feature if desired.

This interface is most likely to be used along with the RT patch. It has
only been used thus far on Ethernet interfaces. Even with the standard
RT change to threaded interrupts for all devices, some RT applications
can be sensitive to even the minimal hardware interrupt that still occurs
with threaded interrupt handlers. The device_poll interface can be used
to completely eliminate all hardware interrupts for a device and the
associated jitter.

This is a standalone feature that should be submitted for review and
possible inclusion in mainline, or maybe in the RT patch.

Signed-off-by: Jeff Westfahl <jeff.westfahl@ni.com>
---
 drivers/base/Kconfig        |   3 +
 drivers/base/Makefile       |   1 +
 drivers/base/poll.c         | 327 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/device_poll.h | 105 ++++++++++++++
 4 files changed, 436 insertions(+)
 create mode 100644 drivers/base/poll.c
 create mode 100644 include/linux/device_poll.h

diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 21cf46f..d23df71 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -174,4 +174,7 @@ config SYS_HYPERVISOR
 
 source "drivers/base/regmap/Kconfig"
 
+config DEVICE_POLL
+	bool
+
 endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 99a375a..92ab7f3 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_MODULES)	+= module.o
 endif
 obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
 obj-$(CONFIG_REGMAP)	+= regmap/
+obj-$(CONFIG_DEVICE_POLL) += poll.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
 
diff --git a/drivers/base/poll.c b/drivers/base/poll.c
new file mode 100644
index 0000000..911a296
--- /dev/null
+++ b/drivers/base/poll.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2014 National Instruments Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device_poll.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+
+/* sysfs attributes */
+
+#define to_ext_attr(x) container_of(x, struct dev_ext_attribute, attr)
+
+/* get sysfs attributes */
+
+ssize_t device_poll_get_interval(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct dev_ext_attribute *ea = to_ext_attr(attr);
+	struct device_poll *device_poll = ea->var;
+
+	return sprintf(buf, "%d\n", device_poll->interval);
+}
+
+static ssize_t device_poll_get_policy(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct dev_ext_attribute *ea = to_ext_attr(attr);
+	struct device_poll *device_poll = ea->var;
+
+	switch (device_poll->policy) {
+	case SCHED_NORMAL:
+		return sprintf(buf, "SCHED_NORMAL (SCHED_OTHER)\n");
+	case SCHED_FIFO:
+		return sprintf(buf, "SCHED_FIFO\n");
+	case SCHED_RR:
+		return sprintf(buf, "SCHED_RR\n");
+	case SCHED_BATCH:
+		return sprintf(buf, "SCHED_BATCH\n");
+	case SCHED_IDLE:
+		return sprintf(buf, "SCHED_IDLE\n");
+	default:
+		return sprintf(buf, "unknown\n");
+	}
+}
+
+static ssize_t device_poll_get_priority(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct dev_ext_attribute *ea = to_ext_attr(attr);
+	struct device_poll *device_poll = ea->var;
+
+	return sprintf(buf, "%d\n", device_poll->priority);
+}
+
+/* set sysfs attributes */
+
+static ssize_t device_poll_set_interval(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t size)
+{
+	struct dev_ext_attribute *ea = to_ext_attr(attr);
+	struct device_poll *device_poll = ea->var;
+	int interval;
+	int ret = 0;
+
+	if (device_poll->use_capability && !capable(device_poll->capability))
+		return -EPERM;
+
+	if (0 > kstrtoint(buf, 0, &interval))
+		return -EINVAL;
+
+	device_poll->ops->lock(device_poll);
+	if (device_poll->interval != interval) {
+		device_poll->interval = interval;
+
+		ret = device_poll->ops->reinit(device_poll);
+	}
+	device_poll->ops->unlock(device_poll);
+
+	if (ret)
+		return ret;
+
+	return size;
+}
+
+static ssize_t device_poll_set_policy(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	struct dev_ext_attribute *ea = to_ext_attr(attr);
+	struct device_poll *device_poll = ea->var;
+	char policy_str[16] = { 0 };
+	int policy;
+	struct sched_param param;
+
+	if (device_poll->use_capability && !capable(device_poll->capability))
+		return -EPERM;
+
+	if (1 != sscanf(buf, "%15s", policy_str))
+		return -EINVAL;
+
+	if ((0 == strcmp(policy_str, "SCHED_NORMAL") ||
+	     (0 == strcmp(policy_str, "SCHED_OTHER"))))
+		policy = SCHED_NORMAL;
+	else if (0 == strcmp(policy_str, "SCHED_FIFO"))
+		policy = SCHED_FIFO;
+	else if (0 == strcmp(policy_str, "SCHED_RR"))
+		policy = SCHED_RR;
+	else if (0 == strcmp(policy_str, "SCHED_BATCH"))
+		policy = SCHED_BATCH;
+	else if (0 == strcmp(policy_str, "SCHED_IDLE"))
+		policy = SCHED_IDLE;
+	else
+		return -EINVAL;
+
+	device_poll->ops->lock(device_poll);
+	if (device_poll->policy != policy) {
+		device_poll->policy = policy;
+
+		if (device_poll->task) {
+			param.sched_priority = device_poll->priority;
+			sched_setscheduler(device_poll->task,
+					   device_poll->policy,
+					   &param);
+		}
+	}
+	device_poll->ops->unlock(device_poll);
+
+	return size;
+}
+
+static ssize_t device_poll_set_priority(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t size)
+{
+	struct dev_ext_attribute *ea = to_ext_attr(attr);
+	struct device_poll *device_poll = ea->var;
+	int priority;
+	struct sched_param param;
+
+	if (device_poll->use_capability && !capable(device_poll->capability))
+		return -EPERM;
+
+	if (0 > kstrtoint(buf, 0, &priority))
+		return -EINVAL;
+
+	device_poll->ops->lock(device_poll);
+	if (device_poll->priority != priority) {
+		device_poll->priority = priority;
+
+		if (device_poll->task) {
+			param.sched_priority = device_poll->priority;
+			sched_setscheduler(device_poll->task,
+					   device_poll->policy,
+					   &param);
+		}
+	}
+	device_poll->ops->unlock(device_poll);
+
+	return size;
+}
+
+/* sysfs attributes */
+
+static const DEVICE_ATTR(interval, S_IWUSR | S_IRUGO,
+			 device_poll_get_interval, device_poll_set_interval);
+
+static const DEVICE_ATTR(policy, S_IWUSR | S_IRUGO,
+			 device_poll_get_policy, device_poll_set_policy);
+
+static const DEVICE_ATTR(priority, S_IWUSR | S_IRUGO,
+			 device_poll_get_priority, device_poll_set_priority);
+
+/* device_poll internal functions */
+
+static int device_poll_thread(void *info)
+{
+	struct device_poll *device_poll = info;
+	int polling_interval;
+	int polling_interval_us;
+	struct sched_param param;
+
+	polling_interval = device_poll->interval;
+
+	/* If we got changed to interrupt mode before the polling thread
+	   started. */
+	if (unlikely(0 >= polling_interval)) {
+		while (!kthread_should_stop())
+			usleep(1);
+		return -EINTR;
+	}
+
+	polling_interval_us = polling_interval * 1000;
+
+	param.sched_priority = device_poll->priority;
+	sched_setscheduler(current, device_poll->policy, &param);
+
+	while (!kthread_should_stop()) {
+		/* Ensure changes to device_poll->enabled made on other CPUs
+		   are seen here. */
+		smp_rmb();
+		if (device_poll->enabled)
+			device_poll->ops->interrupt(device_poll);
+
+		if (20 > polling_interval)
+			usleep_range(polling_interval_us, polling_interval_us);
+		else
+			msleep(polling_interval);
+	}
+
+	return 0;
+}
+
+/* device_poll external functions */
+
+int device_poll_init(struct device_poll *device_poll)
+{
+	int ret;
+
+	if (!device_poll || !device_poll->device || !device_poll->ops)
+		return -EINVAL;
+
+	if (!device_poll->ops->reinit || !device_poll->ops->lock ||
+	    !device_poll->ops->unlock || !device_poll->ops->interrupt)
+		return -EINVAL;
+
+	if (device_poll->use_capability && !cap_valid(device_poll->capability))
+		return -EINVAL;
+
+	device_poll->task = NULL;
+	device_poll->enabled = 0;
+
+	device_poll->interval_attr.attr = dev_attr_interval;
+	device_poll->policy_attr.attr = dev_attr_policy;
+	device_poll->priority_attr.attr = dev_attr_priority;
+
+	device_poll->interval_attr.var = device_poll;
+	device_poll->policy_attr.var = device_poll;
+	device_poll->priority_attr.var = device_poll;
+
+	if (device_poll->use_capability) {
+		device_poll->interval_attr.attr.attr.mode |= S_IWUGO;
+		device_poll->policy_attr.attr.attr.mode |= S_IWUGO;
+		device_poll->priority_attr.attr.attr.mode |= S_IWUGO;
+	}
+
+	sysfs_attr_init(&device_poll->interval_attr.attr.attr);
+	sysfs_attr_init(&device_poll->policy_attr.attr.attr);
+	sysfs_attr_init(&device_poll->priority_attr.attr.attr);
+
+	device_poll->attrs[0] = &device_poll->interval_attr.attr.attr;
+	device_poll->attrs[1] = &device_poll->policy_attr.attr.attr;
+	device_poll->attrs[2] = &device_poll->priority_attr.attr.attr;
+	device_poll->attrs[3] = NULL;
+
+	device_poll->attr_group.name = "device_poll";
+	device_poll->attr_group.attrs = device_poll->attrs;
+
+	ret = sysfs_create_group(&device_poll->device->kobj,
+				 &device_poll->attr_group);
+	if (ret)
+		device_poll_exit(device_poll);
+
+	return ret;
+}
+EXPORT_SYMBOL(device_poll_init);
+
+void device_poll_exit(struct device_poll *device_poll)
+{
+	if (!device_poll || !device_poll->device)
+		return;
+
+	sysfs_remove_group(&device_poll->device->kobj,
+			   &device_poll->attr_group);
+}
+EXPORT_SYMBOL(device_poll_exit);
+
+int device_poll_request_irq(struct device_poll *device_poll)
+{
+	int err;
+
+	if (!device_poll)
+		return -EINVAL;
+
+	/* If interrupts are enabled. */
+	if (device_poll->interval <= 0)
+		return -ERANGE;
+
+	/* Start up the polling thread. */
+	device_poll->task = kthread_run(device_poll_thread,
+					device_poll, "poll/%s",
+					dev_name(device_poll->device));
+	if (IS_ERR(device_poll->task)) {
+		err = PTR_ERR(device_poll->task);
+		device_poll->task = NULL;
+		dev_err(device_poll->device,
+			"Unable to create polling thread: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(device_poll_request_irq);
+
+void device_poll_free_irq(struct device_poll *device_poll)
+{
+	if (device_poll_is_active(device_poll)) {
+		kthread_stop(device_poll->task);
+		device_poll->task = NULL;
+	}
+}
+EXPORT_SYMBOL(device_poll_free_irq);
diff --git a/include/linux/device_poll.h b/include/linux/device_poll.h
new file mode 100644
index 0000000..846a3b4
--- /dev/null
+++ b/include/linux/device_poll.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 National Instruments Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_DEVICE_POLL_H_
+#define _LINUX_DEVICE_POLL_H_
+
+#ifdef CONFIG_DEVICE_POLL
+
+#include <linux/device.h>
+
+struct device_poll;
+
+struct device_poll_ops {
+	/* Reinitialize, if the mode changes. */
+	int (*reinit)(struct device_poll *device_poll);
+
+	/* Lock and unlock, for consistency when changing settings. */
+	void (*lock)(struct device_poll *device_poll);
+	void (*unlock)(struct device_poll *device_poll);
+
+	/* Polled interrupt handler. */
+	void (*interrupt)(struct device_poll *device_poll);
+};
+
+struct device_poll {
+	/* The following must be initialized by the driver before calling
+	   device_poll_init. */
+
+	/* The device for which we're polling. */
+	struct device *device;
+
+	/* Device operations. */
+	struct device_poll_ops *ops;
+
+	/* A capability can be specified to allow non-root users to modify
+	   the sysfs attributes. */
+	bool use_capability;
+	int capability;
+
+	/* Polling interval in milliseconds. A value of 0 or less means
+	   use interrupts. */
+	int interval;
+
+	/* Polling task policy and priority, such as SCHED_FIFO 10. */
+	int policy;
+	int priority;
+
+	/* The following are internal struct members and should not be touched
+	   by drivers. */
+
+	struct task_struct *task;
+	int enabled;
+
+	struct dev_ext_attribute interval_attr;
+	struct dev_ext_attribute policy_attr;
+	struct dev_ext_attribute priority_attr;
+	struct attribute *attrs[4];
+	struct attribute_group attr_group;
+};
+
+int device_poll_init(struct device_poll *device_poll);
+void device_poll_exit(struct device_poll *device_poll);
+
+int device_poll_request_irq(struct device_poll *device_poll);
+void device_poll_free_irq(struct device_poll *device_poll);
+
+static inline int device_poll_is_active(struct device_poll *device_poll)
+{
+	return likely(device_poll) && (device_poll->task != NULL);
+}
+
+static inline void device_poll_enable_irq(struct device_poll *device_poll)
+{
+	if (device_poll_is_active(device_poll)) {
+		device_poll->enabled = 1;
+		/* Ensure changes to device_poll->enabled are seen by the
+		   polling thread. */
+		smp_wmb();
+	}
+}
+
+static inline void device_poll_disable_irq(struct device_poll *device_poll)
+{
+	if (device_poll_is_active(device_poll)) {
+		device_poll->enabled = 0;
+		/* Ensure changes to device_poll->enabled are seen by the
+		   polling thread. */
+		smp_wmb();
+	}
+}
+
+#endif
+
+#endif /* _LINUX_DEVICE_POLL_H_ */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] net: add device_poll functionality common to all net devices
  2015-01-26 22:46 [PATCH 1/2] driver core: add device_poll interface Xander Huff
@ 2015-01-26 22:46 ` Xander Huff
  2015-01-29 22:32 ` [PATCH 1/2] driver core: add device_poll interface David Miller
  1 sibling, 0 replies; 3+ messages in thread
From: Xander Huff @ 2015-01-26 22:46 UTC (permalink / raw)
  To: gregkh, davem, jeff.westfahl
  Cc: netdev, linux-kernel, jaeden.amero, ben.shelton, brad.mouring,
	rich.tollerton

From: Jeff Westfahl <jeff.westfahl@ni.com>

The device_poll feature is generic to any device. Most net devices should
be able to use a set of common functionality to implement device_poll, such
as CAP_NET_ADMIN and rtnl_lock. Add this common functionality for all net
devices to use.

This is a standalone feature that should be submitted for review and
possible inclusion in mainline, or maybe in the RT patch.

Signed-off-by: Jeff Westfahl <jeff.westfahl@ni.com>
---
 include/linux/netdev_poll.h | 26 ++++++++++++++++++
 net/Kconfig                 |  3 +++
 net/core/Makefile           |  1 +
 net/core/dev_poll.c         | 64 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 94 insertions(+)
 create mode 100644 include/linux/netdev_poll.h
 create mode 100644 net/core/dev_poll.c

diff --git a/include/linux/netdev_poll.h b/include/linux/netdev_poll.h
new file mode 100644
index 0000000..3785454
--- /dev/null
+++ b/include/linux/netdev_poll.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 National Instruments Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_NETDEV_POLL_H_
+#define _LINUX_NETDEV_POLL_H_
+
+#ifdef CONFIG_NETDEV_POLL
+
+#include <linux/device_poll.h>
+
+int netdev_poll_init(struct device_poll *device_poll);
+
+#endif
+
+#endif /* _LINUX_NETDEV_POLL_H_ */
diff --git a/net/Kconfig b/net/Kconfig
index a073148..6e6ec69 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -324,5 +324,8 @@ source "net/caif/Kconfig"
 source "net/ceph/Kconfig"
 source "net/nfc/Kconfig"
 
+config NETDEV_POLL
+	bool
+	select DEVICE_POLL
 
 endif   # if NET
diff --git a/net/core/Makefile b/net/core/Makefile
index 0d357b1..4255163 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_FIB_RULES) += fib_rules.o
 obj-$(CONFIG_TRACEPOINTS) += net-traces.o
 obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o
 obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o
+obj-$(CONFIG_NETDEV_POLL) += dev_poll.o
diff --git a/net/core/dev_poll.c b/net/core/dev_poll.c
new file mode 100644
index 0000000..1f2d455
--- /dev/null
+++ b/net/core/dev_poll.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 National Instruments Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/netdev_poll.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+
+/* netdev_poll internal functions */
+
+static int netdev_poll_reinit(struct device_poll *device_poll)
+{
+	int ret = 0;
+	struct net_device *netdev = to_net_dev(device_poll->device);
+
+	if (netif_running(netdev)) {
+		netdev->netdev_ops->ndo_stop(netdev);
+		ret = netdev->netdev_ops->ndo_open(netdev);
+	}
+
+	return ret;
+}
+
+static void netdev_poll_lock(struct device_poll *device_poll)
+{
+	rtnl_lock();
+}
+
+static void netdev_poll_unlock(struct device_poll *device_poll)
+{
+	rtnl_unlock();
+}
+
+/* netdev_poll external functions */
+
+int netdev_poll_init(struct device_poll *device_poll)
+{
+	if (!device_poll || !device_poll->device || !device_poll->ops)
+		return -EINVAL;
+
+	if (!device_poll->ops->reinit)
+		device_poll->ops->reinit = netdev_poll_reinit;
+	if (!device_poll->ops->lock)
+		device_poll->ops->lock = netdev_poll_lock;
+	if (!device_poll->ops->unlock)
+		device_poll->ops->unlock = netdev_poll_unlock;
+
+	/* Allow changes from any process with CAP_NET_ADMIN. */
+	device_poll->use_capability = 1;
+	device_poll->capability = CAP_NET_ADMIN;
+
+	return device_poll_init(device_poll);
+}
+EXPORT_SYMBOL(netdev_poll_init);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH 1/2] driver core: add device_poll interface
  2015-01-26 22:46 [PATCH 1/2] driver core: add device_poll interface Xander Huff
  2015-01-26 22:46 ` [PATCH 2/2] net: add device_poll functionality common to all net devices Xander Huff
@ 2015-01-29 22:32 ` David Miller
  1 sibling, 0 replies; 3+ messages in thread
From: David Miller @ 2015-01-29 22:32 UTC (permalink / raw)
  To: xander.huff
  Cc: gregkh, jeff.westfahl, netdev, linux-kernel, jaeden.amero,
	ben.shelton, brad.mouring, rich.tollerton

From: Xander Huff <xander.huff@ni.com>
Date: Mon, 26 Jan 2015 16:46:32 -0600

> From: Jeff Westfahl <jeff.westfahl@ni.com>
> 
> Add the device_poll interface to the driver core. This is a generic
> interface that any struct device can take advantage of to dynamically
> switch between using interrupts and polling. Many drivers can be easily
> modified to take advantage of this feature if desired.
> 
> This interface is most likely to be used along with the RT patch. It has
> only been used thus far on Ethernet interfaces. Even with the standard
> RT change to threaded interrupts for all devices, some RT applications
> can be sensitive to even the minimal hardware interrupt that still occurs
> with threaded interrupt handlers. The device_poll interface can be used
> to completely eliminate all hardware interrupts for a device and the
> associated jitter.
> 
> This is a standalone feature that should be submitted for review and
> possible inclusion in mainline, or maybe in the RT patch.
> 
> Signed-off-by: Jeff Westfahl <jeff.westfahl@ni.com>

There is no reason any device driver needs to explicitly add support
for a feature like this.

Implement this at the IRQ handler level, and run the IRQ handler
itself from the poll thread.

No driver changes at all.

Sorry, I'm not applying this series, drivers already have to implement
too many things explicitly these days.

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2015-01-29 22:32 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-26 22:46 [PATCH 1/2] driver core: add device_poll interface Xander Huff
2015-01-26 22:46 ` [PATCH 2/2] net: add device_poll functionality common to all net devices Xander Huff
2015-01-29 22:32 ` [PATCH 1/2] driver core: add device_poll interface David Miller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.