* Re: [PATCH v4] net: cgroup: fix access the unallocated memory in netprio cgroup
From: Neil Horman @ 2012-07-19 10:28 UTC (permalink / raw)
To: Li Zefan
Cc: John Fastabend, Gao feng, eric.dumazet, linux-kernel, netdev,
davem, Eric Dumazet, Rustad, Mark D
In-Reply-To: <50075C43.7000706@huawei.com>
On Thu, Jul 19, 2012 at 09:00:51AM +0800, Li Zefan wrote:
> >>>>> static struct cgroup_subsys_state *cgrp_create(struct cgroup *cgrp)
>
> >>>>> {
> >>>>> struct cgroup_netprio_state *cs;
> >>>>> - int ret;
> >>>>> + int ret = -EINVAL;
> >>>>>
> >>>>> cs = kzalloc(sizeof(*cs), GFP_KERNEL);
> >>>>> if (!cs)
> >>>>> return ERR_PTR(-ENOMEM);
> >>>>>
> >>>>> - if (cgrp->parent && cgrp_netprio_state(cgrp->parent)->prioidx) {
> >>>>> - kfree(cs);
> >>>>> - return ERR_PTR(-EINVAL);
> >>>>> - }
> >>>>> + if (cgrp->parent && cgrp_netprio_state(cgrp->parent)->prioidx)
> >>>>> + goto out;
> >>>>>
> >>>>> ret = get_prioidx(&cs->prioidx);
> >>>>> - if (ret != 0) {
> >>>>> + if (ret < 0) {
> >>>>> pr_warn("No space in priority index array\n");
> >>>>> - kfree(cs);
> >>>>> - return ERR_PTR(ret);
> >>>>> + goto out;
> >>>>> + }
> >>>>> +
> >>>>> + ret = update_netdev_tables();
> >>>>> + if (ret < 0) {
> >>>>> + put_prioidx(cs->prioidx);
> >>>>> + goto out;
> >>>>> }
> >>>>
> >>>> Gao,
> >>>>
> >>>> This introduces a null ptr dereference when netprio_cgroup is built
> >>>> into the kernel because update_netdev_tables() depends on init_net.
> >>>> However cgrp_create is being called by cgroup_init before
> >>>> do_initcalls() is called and before net_dev_init().
> >>>>
> >>>> .John
> >>>>
> >>> Not sure I follow here John. Shouldn't init_net be initialized prior to any
> >>> network devices getting registered? In other words, shouldn't for_each_netdev
> >>> just result in zero iterations through the loop?
> >>> Neil
> >>>
> >>
> >> init_net _is_ initialized prior to any network devices getting
> >> registered but not before cgrp_create called via cgroup_init.
> >>
> >> #define for_each_netdev(net, d) \
> >> list_for_each_entry(d, &(net)->dev_base_head, dev_list)
> >>
> >> but dev_base_head is zeroed at this time. In netdev_init we have,
> >>
> >> INIT_LIST_HEAD(&net->dev_base_head);
> >>
> >> but we haven't got that far yet because cgroup_init is called
> >> before do_initcalls().
> >>
> > ok, I see that, and it makes sense, but at this point I'm more concerned with
> > cgroups getting initalized twice. The early_init flag is clear in the
> > cgroup_subsystem for netprio, so we really shouldn't be getting initalized from
> > cgroup_init. We should be getting initalized from the module_init() call that
>
> > we register
>
> If the early_init flag is set, a cgroup subsys will be initialized from
> cgroup_early_init(), otherwise cgroup_init().
>
> If netprio is built as a module, the subsys will be initailized from module_init(),
> otherwise cgroup_init() (in this case cgroup_load_subsys() called in module_init()
> is a no-op).
>
> So it won't get initialized twice.
>
>
Yeah, we already figured that out :).
Still its not a sane interface. If you create a module_init function for a bit
of code, you expect that function to be called before the rest of your code ever
gets executed. The way cgroup_init works, ss->cgroup_create gets called before
the module_init routine does when the module is built monolithically. So no, no
double initalization, but definately some behavior that is going to be prone to
mistakes.
Neil
^ permalink raw reply
* [PATCH net-next 2/2] asix: Add support for programming the EEPROM
From: Christian Riesch @ 2012-07-19 10:23 UTC (permalink / raw)
To: netdev; +Cc: Allan Chou, Mark Lord, Grant Grundler, Christian Riesch
In-Reply-To: <1342693387-17945-1-git-send-email-christian.riesch@omicron.at>
This patch adds the asix_set_eeprom() function to provide support for
programming the configuration EEPROM via ethtool.
Signed-off-by: Christian Riesch <christian.riesch@omicron.at>
---
drivers/net/usb/asix.h | 2 +
drivers/net/usb/asix_common.c | 81 ++++++++++++++++++++++++++++++++++++++++
drivers/net/usb/asix_devices.c | 3 +
drivers/net/usb/ax88172a.c | 1 +
4 files changed, 87 insertions(+), 0 deletions(-)
diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h
index fbff177..e889631 100644
--- a/drivers/net/usb/asix.h
+++ b/drivers/net/usb/asix.h
@@ -208,6 +208,8 @@ int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);
int asix_get_eeprom_len(struct net_device *net);
int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
u8 *data);
+int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
+ u8 *data);
void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info);
diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
index 0b5b2d3..774d9ce 100644
--- a/drivers/net/usb/asix_common.c
+++ b/drivers/net/usb/asix_common.c
@@ -516,6 +516,87 @@ int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
return 0;
}
+int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
+ u8 *data)
+{
+ struct usbnet *dev = netdev_priv(net);
+ u16 *eeprom_buff;
+ int first_word, last_word;
+ int i;
+ int ret;
+
+ netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n",
+ eeprom->len, eeprom->offset, eeprom->magic);
+
+ if (eeprom->len == 0)
+ return -EINVAL;
+
+ if (eeprom->magic != AX_EEPROM_MAGIC)
+ return -EINVAL;
+
+ first_word = eeprom->offset >> 1;
+ last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+
+ eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1),
+ GFP_KERNEL);
+ if (!eeprom_buff)
+ return -ENOMEM;
+
+ /* align data to 16 bit boundaries, read the missing data from
+ the EEPROM */
+ if (eeprom->offset & 1) {
+ ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, first_word, 0, 2,
+ &(eeprom_buff[0]));
+ if (ret < 0) {
+ netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word);
+ goto free;
+ }
+ }
+
+ if ((eeprom->offset + eeprom->len) & 1) {
+ ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, last_word, 0, 2,
+ &(eeprom_buff[last_word - first_word]));
+ if (ret < 0) {
+ netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word);
+ goto free;
+ }
+ }
+
+ memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len);
+
+ /* write data to EEPROM */
+ ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL);
+ if (ret < 0) {
+ netdev_err(net, "Failed to enable EEPROM write\n");
+ goto free;
+ }
+ msleep(20);
+
+ for (i = first_word; i <= last_word; i++) {
+ netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n",
+ i, eeprom_buff[i - first_word]);
+ ret = asix_write_cmd(dev, AX_CMD_WRITE_EEPROM, i,
+ eeprom_buff[i - first_word], 0, NULL);
+ if (ret < 0) {
+ netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n",
+ i);
+ goto free;
+ }
+ msleep(20);
+ }
+
+ ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL);
+ if (ret < 0) {
+ netdev_err(net, "Failed to disable EEPROM write\n");
+ goto free;
+ }
+
+ ret = 0;
+free:
+ kfree(eeprom_buff);
+ return ret;
+}
+
void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
{
/* Inherit standard device info */
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index 658c08f..4fd48df 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -119,6 +119,7 @@ static const struct ethtool_ops ax88172_ethtool_ops = {
.set_wol = asix_set_wol,
.get_eeprom_len = asix_get_eeprom_len,
.get_eeprom = asix_get_eeprom,
+ .set_eeprom = asix_set_eeprom,
.get_settings = usbnet_get_settings,
.set_settings = usbnet_set_settings,
.nway_reset = usbnet_nway_reset,
@@ -258,6 +259,7 @@ static const struct ethtool_ops ax88772_ethtool_ops = {
.set_wol = asix_set_wol,
.get_eeprom_len = asix_get_eeprom_len,
.get_eeprom = asix_get_eeprom,
+ .set_eeprom = asix_set_eeprom,
.get_settings = usbnet_get_settings,
.set_settings = usbnet_set_settings,
.nway_reset = usbnet_nway_reset,
@@ -478,6 +480,7 @@ static const struct ethtool_ops ax88178_ethtool_ops = {
.set_wol = asix_set_wol,
.get_eeprom_len = asix_get_eeprom_len,
.get_eeprom = asix_get_eeprom,
+ .set_eeprom = asix_set_eeprom,
.get_settings = usbnet_get_settings,
.set_settings = usbnet_set_settings,
.nway_reset = usbnet_nway_reset,
diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c
index 97dce0f..c8e0aa8 100644
--- a/drivers/net/usb/ax88172a.c
+++ b/drivers/net/usb/ax88172a.c
@@ -194,6 +194,7 @@ static const struct ethtool_ops ax88172a_ethtool_ops = {
.set_wol = asix_set_wol,
.get_eeprom_len = asix_get_eeprom_len,
.get_eeprom = asix_get_eeprom,
+ .set_eeprom = asix_set_eeprom,
.get_settings = ax88172a_get_settings,
.set_settings = ax88172a_set_settings,
.nway_reset = ax88172a_nway_reset,
--
1.7.0.4
^ permalink raw reply related
* [PATCH net-next 1/2] asix: Rework reading from EEPROM
From: Christian Riesch @ 2012-07-19 10:23 UTC (permalink / raw)
To: netdev; +Cc: Allan Chou, Mark Lord, Grant Grundler, Christian Riesch
The current code for reading the EEPROM via ethtool in the asix
driver has a few issues. It cannot handle odd length values
(accesses must be aligned at 16 bit boundaries) and interprets the
offset provided by ethtool as 16 bit word offset instead as byte offset.
The new code for asix_get_eeprom() introduced by this patch is
modeled after the code in
drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c
and provides read access to the entire EEPROM with arbitrary
offsets and lengths.
Signed-off-by: Christian Riesch <christian.riesch@omicron.at>
---
drivers/net/usb/asix.h | 5 ++---
drivers/net/usb/asix_common.c | 39 ++++++++++++++++++++++-----------------
drivers/net/usb/asix_devices.c | 9 ---------
drivers/net/usb/ax88172a.c | 3 ---
4 files changed, 24 insertions(+), 32 deletions(-)
diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h
index 77d9e4c..fbff177 100644
--- a/drivers/net/usb/asix.h
+++ b/drivers/net/usb/asix.h
@@ -156,8 +156,7 @@
#define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */
#define AX_EEPROM_MAGIC 0xdeadbeef
-#define AX88172_EEPROM_LEN 0x40
-#define AX88772_EEPROM_LEN 0xff
+#define AX_EEPROM_LEN 0x200
/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
struct asix_data {
@@ -165,7 +164,7 @@ struct asix_data {
u8 mac_addr[ETH_ALEN];
u8 phymode;
u8 ledmode;
- u8 eeprom_len;
+ u8 res;
};
int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
index 336f755..0b5b2d3 100644
--- a/drivers/net/usb/asix_common.c
+++ b/drivers/net/usb/asix_common.c
@@ -478,46 +478,51 @@ int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
int asix_get_eeprom_len(struct net_device *net)
{
- struct usbnet *dev = netdev_priv(net);
- struct asix_data *data = (struct asix_data *)&dev->data;
-
- return data->eeprom_len;
+ return AX_EEPROM_LEN;
}
int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
u8 *data)
{
struct usbnet *dev = netdev_priv(net);
- __le16 *ebuf = (__le16 *)data;
+ u16 *eeprom_buff;
+ int first_word, last_word;
int i;
- /* Crude hack to ensure that we don't overwrite memory
- * if an odd length is supplied
- */
- if (eeprom->len % 2)
+ if (eeprom->len == 0)
return -EINVAL;
eeprom->magic = AX_EEPROM_MAGIC;
+ first_word = eeprom->offset >> 1;
+ last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+
+ eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1),
+ GFP_KERNEL);
+ if (!eeprom_buff)
+ return -ENOMEM;
+
/* ax8817x returns 2 bytes from eeprom on read */
- for (i=0; i < eeprom->len / 2; i++) {
- if (asix_read_cmd(dev, AX_CMD_READ_EEPROM,
- eeprom->offset + i, 0, 2, &ebuf[i]) < 0)
- return -EINVAL;
+ for (i = first_word; i <= last_word; i++) {
+ if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2,
+ &(eeprom_buff[i - first_word])) < 0) {
+ kfree(eeprom_buff);
+ return -EIO;
+ }
}
+
+ memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+ kfree(eeprom_buff);
return 0;
}
void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
{
- struct usbnet *dev = netdev_priv(net);
- struct asix_data *data = (struct asix_data *)&dev->data;
-
/* Inherit standard device info */
usbnet_get_drvinfo(net, info);
strncpy (info->driver, DRIVER_NAME, sizeof info->driver);
strncpy (info->version, DRIVER_VERSION, sizeof info->version);
- info->eedump_len = data->eeprom_len;
+ info->eedump_len = AX_EEPROM_LEN;
}
int asix_set_mac_address(struct net_device *net, void *p)
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index ed9403b..658c08f 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -201,9 +201,6 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
u8 buf[ETH_ALEN];
int i;
unsigned long gpio_bits = dev->driver_info->data;
- struct asix_data *data = (struct asix_data *)&dev->data;
-
- data->eeprom_len = AX88172_EEPROM_LEN;
usbnet_get_endpoints(dev,intf);
@@ -409,12 +406,9 @@ static const struct net_device_ops ax88772_netdev_ops = {
static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
{
int ret, embd_phy;
- struct asix_data *data = (struct asix_data *)&dev->data;
u8 buf[ETH_ALEN];
u32 phyid;
- data->eeprom_len = AX88772_EEPROM_LEN;
-
usbnet_get_endpoints(dev,intf);
/* Get the MAC address */
@@ -767,9 +761,6 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
{
int ret;
u8 buf[ETH_ALEN];
- struct asix_data *data = (struct asix_data *)&dev->data;
-
- data->eeprom_len = AX88772_EEPROM_LEN;
usbnet_get_endpoints(dev,intf);
diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c
index 3d0f8fa..97dce0f 100644
--- a/drivers/net/usb/ax88172a.c
+++ b/drivers/net/usb/ax88172a.c
@@ -228,12 +228,9 @@ err:
static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf)
{
int ret;
- struct asix_data *data = (struct asix_data *)&dev->data;
u8 buf[ETH_ALEN];
struct ax88172a_private *priv;
- data->eeprom_len = AX88772_EEPROM_LEN;
-
usbnet_get_endpoints(dev, intf);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
--
1.7.0.4
^ permalink raw reply related
* [PATCH] net: e100: ucode is optional in some cases
From: Bjørn Mork @ 2012-07-19 9:33 UTC (permalink / raw)
To: netdev
Cc: Jeff Kirsher, Jesse Brandeburg, Bruce Allan, Carolyn Wyborny,
Don Skidmore, Greg Rose, Peter P Waskiewicz Jr, Alex Duyck,
John Ronciak, e1000-devel, Bjørn Mork
commit 9ac32e1b firmware: convert e100 driver to request_firmware()
did a straight conversion of the in-driver ucode to external
files. This introduced the possibility of the driver failing
to enable an interface due to missing ucode. There was no
evaluation of the importance of the ucode at the time.
Based on the information available from
http://downloadmirror.intel.com/5154/eng/e100.htm
http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/dev/fxp/rcvbundl.h?rev=HEAD;content-type=text%2Fplain
we can assume that the ucode implements the "CPU Cycle Saver"
feature on the supported adapters. Although generally wanted,
this is an optional feature. The ucode source is not
available, preventing it from being included in free
distributions. This creates unnecessary problems for the end
users. Doing a network install based on a free distribution
installer requires the user to download and insert the ucode
into the installer.
Making the ucode optional when possible improves the user
experience and driver usability.
The ucode for some adapters include a bugfix, making it
essential. We continue to fail for these adapters unless the
ucode is available.
Signed-off-by: Bjørn Mork <bjorn@mork.no>
---
This was inspired by a recent bad experience trying to install
Debian wheezy on an older laptop using a Debian netboot image.
The image does of course not include any of the ucode files
due to the missing source. The installer supports inserting
firmware from a floppy or other medium, but I find that to be
unnecessary hassle given that the device can work without it.
drivers/net/ethernet/intel/e100.c | 41 +++++++++++++++++++++++++++++--------
1 file changed, 32 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c
index ada720b..81670d8 100644
--- a/drivers/net/ethernet/intel/e100.c
+++ b/drivers/net/ethernet/intel/e100.c
@@ -1249,20 +1249,36 @@ static const struct firmware *e100_request_firmware(struct nic *nic)
const struct firmware *fw = nic->fw;
u8 timer, bundle, min_size;
int err = 0;
+ bool required = false;
/* do not load u-code for ICH devices */
if (nic->flags & ich)
return NULL;
- /* Search for ucode match against h/w revision */
- if (nic->mac == mac_82559_D101M)
+ /* Search for ucode match against h/w revision
+ *
+ * The FIRMWARE_D102E ucode includes both CPUSaver and
+ *
+ * "fixes for bugs in the B-step hardware (specifically, bugs
+ * with Inline Receive)."
+ *
+ * according to
+ * http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/dev/fxp/rcvbundl.h?rev=HEAD;content-type=text%2Fplain
+ * So we must fail if it cannot be loaded.
+ *
+ * The other microcode files are only required for the optional
+ * CPUSaver feature. Nice to have, but no reason to fail.
+ */
+ if (nic->mac == mac_82559_D101M) {
fw_name = FIRMWARE_D101M;
- else if (nic->mac == mac_82559_D101S)
+ } else if (nic->mac == mac_82559_D101S) {
fw_name = FIRMWARE_D101S;
- else if (nic->mac == mac_82551_F || nic->mac == mac_82551_10)
+ } else if (nic->mac == mac_82551_F || nic->mac == mac_82551_10) {
fw_name = FIRMWARE_D102E;
- else /* No ucode on other devices */
+ required = true;
+ } else { /* No ucode on other devices */
return NULL;
+ }
/* If the firmware has not previously been loaded, request a pointer
* to it. If it was previously loaded, we are reinitializing the
@@ -1273,10 +1289,17 @@ static const struct firmware *e100_request_firmware(struct nic *nic)
err = request_firmware(&fw, fw_name, &nic->pdev->dev);
if (err) {
- netif_err(nic, probe, nic->netdev,
- "Failed to load firmware \"%s\": %d\n",
- fw_name, err);
- return ERR_PTR(err);
+ if (required) {
+ netif_err(nic, probe, nic->netdev,
+ "Failed to load firmware \"%s\": %d\n",
+ fw_name, err);
+ return ERR_PTR(err);
+ } else {
+ netif_info(nic, probe, nic->netdev,
+ "CPUSaver disabled. Needs \"%s\": %d\n",
+ fw_name, err);
+ return NULL;
+ }
}
/* Firmware should be precisely UCODE_SIZE (words) plus three bytes
--
1.7.10.4
^ permalink raw reply related
* [PATCH net-next] ipv4: tcp: remove per net tcp_sock
From: Eric Dumazet @ 2012-07-19 8:58 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Tom Herbert, Bill Sommerfeld
From: Eric Dumazet <edumazet@google.com>
tcp_v4_send_reset() and tcp_v4_send_ack() use a single socket
per network namespace.
This leads to bad behavior on multiqueue NICS, because many cpus
contend for the socket lock and once socket lock is acquired, extra
false sharing on various socket fields slow down the operations.
To better resist to attacks, we use a percpu socket. Each cpu can
run without contention, using appropriate memory (local node)
Additional features :
1) We also mirror the queue_mapping of the incoming skb, so that
answers use the same queue if possible.
2) Setting SOCK_USE_WRITE_QUEUE socket flag speedup sock_wfree()
3) We now limit the number of in-flight RST/ACK [1] packets
per cpu, instead of per namespace, and we honor the sysctl_wmem_default
limit dynamically. (Prior to this patch, sysctl_wmem_default value was
copied at boot time, so any further change would not affect tcp_sock
limit)
[1] These packets are only generated when no socket was matched for
the incoming packet.
Reported-by: Bill Sommerfeld <wsommerfeld@google.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Tom Herbert <therbert@google.com>
---
include/net/ip.h | 2 -
include/net/netns/ipv4.h | 1
net/ipv4/ip_output.c | 46 ++++++++++++++++++++++---------------
net/ipv4/tcp_ipv4.c | 8 ++----
4 files changed, 32 insertions(+), 25 deletions(-)
diff --git a/include/net/ip.h b/include/net/ip.h
index ec5cfde..bd5e444 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -158,7 +158,7 @@ static inline __u8 ip_reply_arg_flowi_flags(const struct ip_reply_arg *arg)
return (arg->flags & IP_REPLY_ARG_NOSRCCHECK) ? FLOWI_FLAG_ANYSRC : 0;
}
-void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr,
+void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
__be32 saddr, const struct ip_reply_arg *arg,
unsigned int len);
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index 2e089a9..d909c7f 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -38,7 +38,6 @@ struct netns_ipv4 {
struct sock *fibnl;
struct sock **icmp_sk;
- struct sock *tcp_sock;
struct inet_peer_base *peers;
struct tcpm_hash_bucket *tcp_metrics_hash;
unsigned int tcp_metrics_hash_mask;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index cc52679..3633136 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1463,20 +1463,29 @@ static int ip_reply_glue_bits(void *dptr, char *to, int offset,
/*
* Generic function to send a packet as reply to another packet.
- * Used to send TCP resets so far.
+ * Used to send some TCP resets/acks so far.
*
- * Should run single threaded per socket because it uses the sock
- * structure to pass arguments.
+ * Use a fake percpu inet socket to avoid false sharing and contention.
*/
-void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr,
+void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
__be32 saddr, const struct ip_reply_arg *arg,
unsigned int len)
{
- struct inet_sock *inet = inet_sk(sk);
struct ip_options_data replyopts;
struct ipcm_cookie ipc;
struct flowi4 fl4;
struct rtable *rt = skb_rtable(skb);
+ struct sk_buff *nskb;
+ struct sock *sk;
+ struct inet_sock *inet;
+ static DEFINE_PER_CPU(struct inet_sock, unicast_sock) = {
+ .sk = {
+ .sk_wmem_alloc = ATOMIC_INIT(1),
+ .sk_allocation = GFP_ATOMIC,
+ .sk_flags = (1UL << SOCK_USE_WRITE_QUEUE),
+ },
+ .pmtudisc = IP_PMTUDISC_WANT,
+ };
if (ip_options_echo(&replyopts.opt.opt, skb))
return;
@@ -1494,38 +1503,39 @@ void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr,
flowi4_init_output(&fl4, arg->bound_dev_if, 0,
RT_TOS(arg->tos),
- RT_SCOPE_UNIVERSE, sk->sk_protocol,
+ RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol,
ip_reply_arg_flowi_flags(arg),
daddr, saddr,
tcp_hdr(skb)->source, tcp_hdr(skb)->dest);
security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
- rt = ip_route_output_key(sock_net(sk), &fl4);
+ rt = ip_route_output_key(net, &fl4);
if (IS_ERR(rt))
return;
- /* And let IP do all the hard work.
+ inet = &get_cpu_var(unicast_sock);
- This chunk is not reenterable, hence spinlock.
- Note that it uses the fact, that this function is called
- with locally disabled BH and that sk cannot be already spinlocked.
- */
- bh_lock_sock(sk);
inet->tos = arg->tos;
+ sk = &inet->sk;
sk->sk_priority = skb->priority;
sk->sk_protocol = ip_hdr(skb)->protocol;
sk->sk_bound_dev_if = arg->bound_dev_if;
+ sock_net_set(sk, net);
+ __skb_queue_head_init(&sk->sk_write_queue);
+ sk->sk_sndbuf = sysctl_wmem_default;
ip_append_data(sk, &fl4, ip_reply_glue_bits, arg->iov->iov_base, len, 0,
&ipc, &rt, MSG_DONTWAIT);
- if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) {
+ nskb = skb_peek(&sk->sk_write_queue);
+ if (nskb) {
if (arg->csumoffset >= 0)
- *((__sum16 *)skb_transport_header(skb) +
- arg->csumoffset) = csum_fold(csum_add(skb->csum,
+ *((__sum16 *)skb_transport_header(nskb) +
+ arg->csumoffset) = csum_fold(csum_add(nskb->csum,
arg->csum));
- skb->ip_summed = CHECKSUM_NONE;
+ nskb->ip_summed = CHECKSUM_NONE;
+ skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
ip_push_pending_frames(sk, &fl4);
}
- bh_unlock_sock(sk);
+ put_cpu_var(unicast_sock);
ip_rt_put(rt);
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index d9caf5c..d7d2fa5 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -688,7 +688,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
net = dev_net(skb_dst(skb)->dev);
arg.tos = ip_hdr(skb)->tos;
- ip_send_unicast_reply(net->ipv4.tcp_sock, skb, ip_hdr(skb)->saddr,
+ ip_send_unicast_reply(net, skb, ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr, &arg, arg.iov[0].iov_len);
TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
@@ -771,7 +771,7 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
if (oif)
arg.bound_dev_if = oif;
arg.tos = tos;
- ip_send_unicast_reply(net->ipv4.tcp_sock, skb, ip_hdr(skb)->saddr,
+ ip_send_unicast_reply(net, skb, ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr, &arg, arg.iov[0].iov_len);
TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
@@ -2624,13 +2624,11 @@ EXPORT_SYMBOL(tcp_prot);
static int __net_init tcp_sk_init(struct net *net)
{
- return inet_ctl_sock_create(&net->ipv4.tcp_sock,
- PF_INET, SOCK_RAW, IPPROTO_TCP, net);
+ return 0;
}
static void __net_exit tcp_sk_exit(struct net *net)
{
- inet_ctl_sock_destroy(net->ipv4.tcp_sock);
}
static void __net_exit tcp_sk_exit_batch(struct list_head *net_exit_list)
^ permalink raw reply related
* RE: [net-next 9/9] ixgbe: Cleanup holes in flags after removing several of them
From: David Laight @ 2012-07-19 8:33 UTC (permalink / raw)
To: Jeff Kirsher, davem; +Cc: Alexander Duyck, netdev, gospo, sassmann
In-Reply-To: <1342643516-2696-10-git-send-email-jeffrey.t.kirsher@intel.com>
> This change is just meant to defragment the flags as there are several
hole
> that have been introduced since several features, or the flags for
them,
> have been removed.
Doesn't this sort of change just make it difficult for people who are
looking at hexdumps of memory but don't have exactly the right header
file to hand?
It doesn't really gain anything much either.
I can (just) imagine reordering flags so that the commonly
tested ones are in the low bits so that they can be tested
with small immediate constants - saving an instruction.
But that isn't what is being done here.
David
^ permalink raw reply
* [PATCH net-next V1 0/4] net/mlx4_en: Add accelerated RFS support
From: Or Gerlitz @ 2012-07-19 8:33 UTC (permalink / raw)
To: davem; +Cc: roland, netdev, oren, yevgenyp, Or Gerlitz, Amir Vadai
Hi Dave,
This series from Amir Vadai adds support for Accelerated RFS
to the mlx4_en Ethernet driver.
The code uses the Accelerated RFS infrastructure and HW flow steering
to keep CPU affinity of rx interrupts and applications per TCP stream.
To do so, we had to add little protection to cpu_rmap.h against double
inclusion. Also, added linking between CPU to IRQ using rmap in the
mlx4_core driver.
changes from V0:
- always use CONFIG_RFS_ACCEL instead of using twice CONFIG_CPU_RMAP directly
Or.
Amir Vadai (4):
net/mlx4: Move MAC_MASK to a common place
net/rps: Protect cpu_rmap.h from double inclusion
{NET,IB}/mlx4: Add rmap support to mlx4_assign_eq
net/mlx4_en: Add accelerated RFS support
drivers/infiniband/hw/mlx4/main.c | 3 +-
drivers/net/ethernet/mellanox/mlx4/en_cq.c | 9 +-
drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 6 +-
drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 316 ++++++++++++++++++++
drivers/net/ethernet/mellanox/mlx4/en_rx.c | 3 +
drivers/net/ethernet/mellanox/mlx4/eq.c | 12 +-
drivers/net/ethernet/mellanox/mlx4/mcg.c | 1 -
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 16 +
drivers/net/ethernet/mellanox/mlx4/port.c | 1 -
.../net/ethernet/mellanox/mlx4/resource_tracker.c | 3 +-
include/linux/cpu_rmap.h | 4 +
include/linux/mlx4/device.h | 4 +-
include/linux/mlx4/driver.h | 2 +
13 files changed, 369 insertions(+), 11 deletions(-)
CC: Amir Vadai <amirv@mellanox.com>
^ permalink raw reply
* [PATCH net-next V1 2/4] net/rps: Protect cpu_rmap.h from double inclusion
From: Or Gerlitz @ 2012-07-19 8:33 UTC (permalink / raw)
To: davem; +Cc: roland, netdev, oren, yevgenyp, Amir Vadai, Or Gerlitz
In-Reply-To: <1342686832-21406-1-git-send-email-ogerlitz@mellanox.com>
From: Amir Vadai <amirv@mellanox.com>
Signed-off-by: Amir Vadai <amirv@mellanox.com>
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
---
include/linux/cpu_rmap.h | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/include/linux/cpu_rmap.h b/include/linux/cpu_rmap.h
index 473771a..ac3bbb5 100644
--- a/include/linux/cpu_rmap.h
+++ b/include/linux/cpu_rmap.h
@@ -1,3 +1,6 @@
+#ifndef __LINUX_CPU_RMAP_H
+#define __LINUX_CPU_RMAP_H
+
/*
* cpu_rmap.c: CPU affinity reverse-map support
* Copyright 2011 Solarflare Communications Inc.
@@ -71,3 +74,4 @@ extern void free_irq_cpu_rmap(struct cpu_rmap *rmap);
extern int irq_cpu_rmap_add(struct cpu_rmap *rmap, int irq);
#endif
+#endif /* __LINUX_CPU_RMAP_H */
--
1.7.1
^ permalink raw reply related
* [PATCH net-next V1 4/4] net/mlx4_en: Add accelerated RFS support
From: Or Gerlitz @ 2012-07-19 8:33 UTC (permalink / raw)
To: davem; +Cc: roland, netdev, oren, yevgenyp, Amir Vadai, Or Gerlitz
In-Reply-To: <1342686832-21406-1-git-send-email-ogerlitz@mellanox.com>
From: Amir Vadai <amirv@mellanox.com>
Use RFS infrastructure and flow steering in HW to keep CPU
affinity of rx interrupts and application per TCP stream.
A flow steering filter is added to the HW whenever the RFS
ndo callback is invoked by core networking code.
Because the invocation takes place in interrupt context, the
actual setup of HW is done using workqueue. Whenever new filter
is added, the driver checks for expiry of existing filters.
Since there's window in time between the point where the core
RFS code invoked the ndo callback, to the point where the HW
is configured from the workqueue context, the 2nd, 3rd etc
packets from that stream will cause the net core to invoke
the callback again and again.
To prevent inefficient/double configuration of the HW, the filters
are kept in a database which is indexed using hash function to enable
fast access.
Signed-off-by: Amir Vadai <amirv@mellanox.com>
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
---
drivers/net/ethernet/mellanox/mlx4/en_cq.c | 8 +-
drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 316 ++++++++++++++++++++++++
drivers/net/ethernet/mellanox/mlx4/en_rx.c | 3 +
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 16 ++
4 files changed, 342 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
index 0ef6156..aa9c2f6 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
@@ -77,6 +77,12 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
struct mlx4_en_dev *mdev = priv->mdev;
int err = 0;
char name[25];
+ struct cpu_rmap *rmap =
+#ifdef CONFIG_RFS_ACCEL
+ priv->dev->rx_cpu_rmap;
+#else
+ NULL;
+#endif
cq->dev = mdev->pndev[priv->port];
cq->mcq.set_ci_db = cq->wqres.db.db;
@@ -91,7 +97,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
sprintf(name, "%s-%d", priv->dev->name,
cq->ring);
/* Set IRQ for specific name (per ring) */
- if (mlx4_assign_eq(mdev->dev, name, NULL,
+ if (mlx4_assign_eq(mdev->dev, name, rmap,
&cq->vector)) {
cq->vector = (cq->ring + 1 + priv->port)
% mdev->dev->caps.num_comp_vectors;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 4ce5ca8..8864d8b 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -36,6 +36,8 @@
#include <linux/if_vlan.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/hash.h>
+#include <net/ip.h>
#include <linux/mlx4/driver.h>
#include <linux/mlx4/device.h>
@@ -66,6 +68,299 @@ static int mlx4_en_setup_tc(struct net_device *dev, u8 up)
return 0;
}
+#ifdef CONFIG_RFS_ACCEL
+
+struct mlx4_en_filter {
+ struct list_head next;
+ struct work_struct work;
+
+ __be32 src_ip;
+ __be32 dst_ip;
+ __be16 src_port;
+ __be16 dst_port;
+
+ int rxq_index;
+ struct mlx4_en_priv *priv;
+ u32 flow_id; /* RFS infrastructure id */
+ int id; /* mlx4_en driver id */
+ u64 reg_id; /* Flow steering API id */
+ u8 activated; /* Used to prevent expiry before filter
+ * is attached
+ */
+ struct hlist_node filter_chain;
+};
+
+static void mlx4_en_filter_rfs_expire(struct mlx4_en_priv *priv);
+
+static void mlx4_en_filter_work(struct work_struct *work)
+{
+ struct mlx4_en_filter *filter = container_of(work,
+ struct mlx4_en_filter,
+ work);
+ struct mlx4_en_priv *priv = filter->priv;
+ struct mlx4_spec_list spec_tcp = {
+ .id = MLX4_NET_TRANS_RULE_ID_TCP,
+ {
+ .tcp_udp = {
+ .dst_port = filter->dst_port,
+ .dst_port_msk = (__force __be16)-1,
+ .src_port = filter->src_port,
+ .src_port_msk = (__force __be16)-1,
+ },
+ },
+ };
+ struct mlx4_spec_list spec_ip = {
+ .id = MLX4_NET_TRANS_RULE_ID_IPV4,
+ {
+ .ipv4 = {
+ .dst_ip = filter->dst_ip,
+ .dst_ip_msk = (__force __be32)-1,
+ .src_ip = filter->src_ip,
+ .src_ip_msk = (__force __be32)-1,
+ },
+ },
+ };
+ struct mlx4_spec_list spec_eth = {
+ .id = MLX4_NET_TRANS_RULE_ID_ETH,
+ };
+ struct mlx4_net_trans_rule rule = {
+ .list = LIST_HEAD_INIT(rule.list),
+ .queue_mode = MLX4_NET_TRANS_Q_LIFO,
+ .exclusive = 1,
+ .allow_loopback = 1,
+ .promisc_mode = MLX4_FS_PROMISC_NONE,
+ .port = priv->port,
+ .priority = MLX4_DOMAIN_RFS,
+ };
+ int rc;
+ __be64 mac;
+ __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16);
+
+ list_add_tail(&spec_eth.list, &rule.list);
+ list_add_tail(&spec_ip.list, &rule.list);
+ list_add_tail(&spec_tcp.list, &rule.list);
+
+ mac = cpu_to_be64((priv->mac & MLX4_MAC_MASK) << 16);
+
+ rule.qpn = priv->rss_map.qps[filter->rxq_index].qpn;
+ memcpy(spec_eth.eth.dst_mac, &mac, ETH_ALEN);
+ memcpy(spec_eth.eth.dst_mac_msk, &mac_mask, ETH_ALEN);
+
+ filter->activated = 0;
+
+ if (filter->reg_id) {
+ rc = mlx4_flow_detach(priv->mdev->dev, filter->reg_id);
+ if (rc && rc != -ENOENT)
+ en_err(priv, "Error detaching flow. rc = %d\n", rc);
+ }
+
+ rc = mlx4_flow_attach(priv->mdev->dev, &rule, &filter->reg_id);
+ if (rc)
+ en_err(priv, "Error attaching flow. err = %d\n", rc);
+
+ mlx4_en_filter_rfs_expire(priv);
+
+ filter->activated = 1;
+}
+
+static inline struct hlist_head *
+filter_hash_bucket(struct mlx4_en_priv *priv, __be32 src_ip, __be32 dst_ip,
+ __be16 src_port, __be16 dst_port)
+{
+ unsigned long l;
+ int bucket_idx;
+
+ l = (__force unsigned long)src_port |
+ ((__force unsigned long)dst_port << 2);
+ l ^= (__force unsigned long)(src_ip ^ dst_ip);
+
+ bucket_idx = hash_long(l, MLX4_EN_FILTER_HASH_SHIFT);
+
+ return &priv->filter_hash[bucket_idx];
+}
+
+static struct mlx4_en_filter *
+mlx4_en_filter_alloc(struct mlx4_en_priv *priv, int rxq_index, __be32 src_ip,
+ __be32 dst_ip, __be16 src_port, __be16 dst_port,
+ u32 flow_id)
+{
+ struct mlx4_en_filter *filter = NULL;
+
+ filter = kzalloc(sizeof(struct mlx4_en_filter), GFP_ATOMIC);
+ if (!filter)
+ return NULL;
+
+ filter->priv = priv;
+ filter->rxq_index = rxq_index;
+ INIT_WORK(&filter->work, mlx4_en_filter_work);
+
+ filter->src_ip = src_ip;
+ filter->dst_ip = dst_ip;
+ filter->src_port = src_port;
+ filter->dst_port = dst_port;
+
+ filter->flow_id = flow_id;
+
+ filter->id = priv->last_filter_id++;
+
+ list_add_tail(&filter->next, &priv->filters);
+ hlist_add_head(&filter->filter_chain,
+ filter_hash_bucket(priv, src_ip, dst_ip, src_port,
+ dst_port));
+
+ return filter;
+}
+
+static void mlx4_en_filter_free(struct mlx4_en_filter *filter)
+{
+ struct mlx4_en_priv *priv = filter->priv;
+ int rc;
+
+ list_del(&filter->next);
+
+ rc = mlx4_flow_detach(priv->mdev->dev, filter->reg_id);
+ if (rc && rc != -ENOENT)
+ en_err(priv, "Error detaching flow. rc = %d\n", rc);
+
+ kfree(filter);
+}
+
+static inline struct mlx4_en_filter *
+mlx4_en_filter_find(struct mlx4_en_priv *priv, __be32 src_ip, __be32 dst_ip,
+ __be16 src_port, __be16 dst_port)
+{
+ struct hlist_node *elem;
+ struct mlx4_en_filter *filter;
+ struct mlx4_en_filter *ret = NULL;
+
+ hlist_for_each_entry(filter, elem,
+ filter_hash_bucket(priv, src_ip, dst_ip,
+ src_port, dst_port),
+ filter_chain) {
+ if (filter->src_ip == src_ip &&
+ filter->dst_ip == dst_ip &&
+ filter->src_port == src_port &&
+ filter->dst_port == dst_port) {
+ ret = filter;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int
+mlx4_en_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
+ u16 rxq_index, u32 flow_id)
+{
+ struct mlx4_en_priv *priv = netdev_priv(net_dev);
+ struct mlx4_en_filter *filter;
+ const struct iphdr *ip;
+ const __be16 *ports;
+ __be32 src_ip;
+ __be32 dst_ip;
+ __be16 src_port;
+ __be16 dst_port;
+ int nhoff = skb_network_offset(skb);
+ int ret = 0;
+
+ if (skb->protocol != htons(ETH_P_IP))
+ return -EPROTONOSUPPORT;
+
+ ip = (const struct iphdr *)(skb->data + nhoff);
+ if (ip_is_fragment(ip))
+ return -EPROTONOSUPPORT;
+
+ ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl);
+
+ src_ip = ip->saddr;
+ dst_ip = ip->daddr;
+ src_port = ports[0];
+ dst_port = ports[1];
+
+ if (ip->protocol != IPPROTO_TCP)
+ return -EPROTONOSUPPORT;
+
+ spin_lock_bh(&priv->filters_lock);
+ filter = mlx4_en_filter_find(priv, src_ip, dst_ip, src_port, dst_port);
+ if (filter) {
+ if (filter->rxq_index == rxq_index)
+ goto out;
+
+ filter->rxq_index = rxq_index;
+ } else {
+ filter = mlx4_en_filter_alloc(priv, rxq_index,
+ src_ip, dst_ip,
+ src_port, dst_port, flow_id);
+ if (!filter) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+
+ queue_work(priv->mdev->workqueue, &filter->work);
+
+out:
+ ret = filter->id;
+err:
+ spin_unlock_bh(&priv->filters_lock);
+
+ return ret;
+}
+
+void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv,
+ struct mlx4_en_rx_ring *rx_ring)
+{
+ struct mlx4_en_filter *filter, *tmp;
+ LIST_HEAD(del_list);
+
+ spin_lock_bh(&priv->filters_lock);
+ list_for_each_entry_safe(filter, tmp, &priv->filters, next) {
+ list_move(&filter->next, &del_list);
+ hlist_del(&filter->filter_chain);
+ }
+ spin_unlock_bh(&priv->filters_lock);
+
+ list_for_each_entry_safe(filter, tmp, &del_list, next) {
+ cancel_work_sync(&filter->work);
+ mlx4_en_filter_free(filter);
+ }
+}
+
+static void mlx4_en_filter_rfs_expire(struct mlx4_en_priv *priv)
+{
+ struct mlx4_en_filter *filter = NULL, *tmp, *last_filter = NULL;
+ LIST_HEAD(del_list);
+ int i = 0;
+
+ spin_lock_bh(&priv->filters_lock);
+ list_for_each_entry_safe(filter, tmp, &priv->filters, next) {
+ if (i > MLX4_EN_FILTER_EXPIRY_QUOTA)
+ break;
+
+ if (filter->activated &&
+ !work_pending(&filter->work) &&
+ rps_may_expire_flow(priv->dev,
+ filter->rxq_index, filter->flow_id,
+ filter->id)) {
+ list_move(&filter->next, &del_list);
+ hlist_del(&filter->filter_chain);
+ } else
+ last_filter = filter;
+
+ i++;
+ }
+
+ if (last_filter && (&last_filter->next != priv->filters.next))
+ list_move(&priv->filters, &last_filter->next);
+
+ spin_unlock_bh(&priv->filters_lock);
+
+ list_for_each_entry_safe(filter, tmp, &del_list, next)
+ mlx4_en_filter_free(filter);
+}
+#endif
+
static int mlx4_en_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
@@ -1079,6 +1374,11 @@ void mlx4_en_free_resources(struct mlx4_en_priv *priv)
{
int i;
+#ifdef CONFIG_RFS_ACCEL
+ free_irq_cpu_rmap(priv->dev->rx_cpu_rmap);
+ priv->dev->rx_cpu_rmap = NULL;
+#endif
+
for (i = 0; i < priv->tx_ring_num; i++) {
if (priv->tx_ring[i].tx_info)
mlx4_en_destroy_tx_ring(priv, &priv->tx_ring[i]);
@@ -1134,6 +1434,15 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
goto err;
}
+#ifdef CONFIG_RFS_ACCEL
+ priv->dev->rx_cpu_rmap = alloc_irq_cpu_rmap(priv->rx_ring_num);
+ if (!priv->dev->rx_cpu_rmap)
+ goto err;
+
+ INIT_LIST_HEAD(&priv->filters);
+ spin_lock_init(&priv->filters_lock);
+#endif
+
return 0;
err:
@@ -1241,6 +1550,9 @@ static const struct net_device_ops mlx4_netdev_ops = {
#endif
.ndo_set_features = mlx4_en_set_features,
.ndo_setup_tc = mlx4_en_setup_tc,
+#ifdef CONFIG_RFS_ACCEL
+ .ndo_rx_flow_steer = mlx4_en_filter_rfs,
+#endif
};
int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
@@ -1358,6 +1670,10 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
NETIF_F_HW_VLAN_FILTER;
dev->hw_features |= NETIF_F_LOOPBACK;
+ if (mdev->dev->caps.steering_mode ==
+ MLX4_STEERING_MODE_DEVICE_MANAGED)
+ dev->hw_features |= NETIF_F_NTUPLE;
+
mdev->pndev[port] = dev;
netif_carrier_off(dev);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index a04cbf7..796cd58 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -389,6 +389,9 @@ void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv,
mlx4_free_hwq_res(mdev->dev, &ring->wqres, size * stride + TXBB_SIZE);
vfree(ring->rx_info);
ring->rx_info = NULL;
+#ifdef CONFIG_RFS_ACCEL
+ mlx4_en_cleanup_filters(priv, ring);
+#endif
}
void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv,
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index a126321..af34c98 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -43,6 +43,7 @@
#ifdef CONFIG_MLX4_EN_DCB
#include <linux/dcbnl.h>
#endif
+#include <linux/cpu_rmap.h>
#include <linux/mlx4/device.h>
#include <linux/mlx4/qp.h>
@@ -77,6 +78,9 @@
#define STATS_DELAY (HZ / 4)
#define MAX_NUM_OF_FS_RULES 256
+#define MLX4_EN_FILTER_HASH_SHIFT 4
+#define MLX4_EN_FILTER_EXPIRY_QUOTA 60
+
/* Typical TSO descriptor with 16 gather entries is 352 bytes... */
#define MAX_DESC_SIZE 512
#define MAX_DESC_TXBBS (MAX_DESC_SIZE / TXBB_SIZE)
@@ -523,6 +527,13 @@ struct mlx4_en_priv {
struct ieee_ets ets;
u16 maxrate[IEEE_8021QAZ_MAX_TCS];
#endif
+#ifdef CONFIG_RFS_ACCEL
+ spinlock_t filters_lock;
+ int last_filter_id;
+ struct list_head filters;
+ struct hlist_head filter_hash[1 << MLX4_EN_FILTER_HASH_SHIFT];
+#endif
+
};
enum mlx4_en_wol {
@@ -602,6 +613,11 @@ int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port);
extern const struct dcbnl_rtnl_ops mlx4_en_dcbnl_ops;
#endif
+#ifdef CONFIG_RFS_ACCEL
+void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv,
+ struct mlx4_en_rx_ring *rx_ring);
+#endif
+
#define MLX4_EN_NUM_SELF_TEST 5
void mlx4_en_ex_selftest(struct net_device *dev, u32 *flags, u64 *buf);
u64 mlx4_en_mac_to_u64(u8 *addr);
--
1.7.1
^ permalink raw reply related
* [PATCH net-next V1 3/4] {NET,IB}/mlx4: Add rmap support to mlx4_assign_eq
From: Or Gerlitz @ 2012-07-19 8:33 UTC (permalink / raw)
To: davem; +Cc: roland, netdev, oren, yevgenyp, Amir Vadai, Or Gerlitz
In-Reply-To: <1342686832-21406-1-git-send-email-ogerlitz@mellanox.com>
From: Amir Vadai <amirv@mellanox.com>
Enable callers of mlx4_assign_eq to supply a pointer to cpu_rmap.
If supplied, the assigned IRQ is tracked using rmap infrastructure.
Signed-off-by: Amir Vadai <amirv@mellanox.com>
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
---
drivers/infiniband/hw/mlx4/main.c | 3 ++-
drivers/net/ethernet/mellanox/mlx4/en_cq.c | 3 ++-
drivers/net/ethernet/mellanox/mlx4/eq.c | 12 +++++++++++-
include/linux/mlx4/device.h | 4 +++-
4 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index 8a3a203..a07b774 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -1159,7 +1159,8 @@ static void mlx4_ib_alloc_eqs(struct mlx4_dev *dev, struct mlx4_ib_dev *ibdev)
sprintf(name, "mlx4-ib-%d-%d@%s",
i, j, dev->pdev->bus->name);
/* Set IRQ for specific name (per ring) */
- if (mlx4_assign_eq(dev, name, &ibdev->eq_table[eq])) {
+ if (mlx4_assign_eq(dev, name, NULL,
+ &ibdev->eq_table[eq])) {
/* Use legacy (same as mlx4_en driver) */
pr_warn("Can't allocate EQ %d; reverting to legacy\n", eq);
ibdev->eq_table[eq] =
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
index 908a460..0ef6156 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
@@ -91,7 +91,8 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
sprintf(name, "%s-%d", priv->dev->name,
cq->ring);
/* Set IRQ for specific name (per ring) */
- if (mlx4_assign_eq(mdev->dev, name, &cq->vector)) {
+ if (mlx4_assign_eq(mdev->dev, name, NULL,
+ &cq->vector)) {
cq->vector = (cq->ring + 1 + priv->port)
% mdev->dev->caps.num_comp_vectors;
mlx4_warn(mdev, "Failed Assigning an EQ to "
diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c
index bce98d9..cd48337 100644
--- a/drivers/net/ethernet/mellanox/mlx4/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/eq.c
@@ -39,6 +39,7 @@
#include <linux/dma-mapping.h>
#include <linux/mlx4/cmd.h>
+#include <linux/cpu_rmap.h>
#include "mlx4.h"
#include "fw.h"
@@ -1060,7 +1061,8 @@ int mlx4_test_interrupts(struct mlx4_dev *dev)
}
EXPORT_SYMBOL(mlx4_test_interrupts);
-int mlx4_assign_eq(struct mlx4_dev *dev, char* name, int * vector)
+int mlx4_assign_eq(struct mlx4_dev *dev, char *name, struct cpu_rmap *rmap,
+ int *vector)
{
struct mlx4_priv *priv = mlx4_priv(dev);
@@ -1074,6 +1076,14 @@ int mlx4_assign_eq(struct mlx4_dev *dev, char* name, int * vector)
snprintf(priv->eq_table.irq_names +
vec * MLX4_IRQNAME_SIZE,
MLX4_IRQNAME_SIZE, "%s", name);
+#ifdef CONFIG_RFS_ACCEL
+ if (rmap) {
+ err = irq_cpu_rmap_add(rmap,
+ priv->eq_table.eq[vec].irq);
+ if (err)
+ mlx4_warn(dev, "Failed adding irq rmap\n");
+ }
+#endif
err = request_irq(priv->eq_table.eq[vec].irq,
mlx4_msi_x_interrupt, 0,
&priv->eq_table.irq_names[vec<<5],
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h
index 6f0d133..4d7761f 100644
--- a/include/linux/mlx4/device.h
+++ b/include/linux/mlx4/device.h
@@ -36,6 +36,7 @@
#include <linux/pci.h>
#include <linux/completion.h>
#include <linux/radix-tree.h>
+#include <linux/cpu_rmap.h>
#include <linux/atomic.h>
@@ -784,7 +785,8 @@ void mlx4_fmr_unmap(struct mlx4_dev *dev, struct mlx4_fmr *fmr,
int mlx4_fmr_free(struct mlx4_dev *dev, struct mlx4_fmr *fmr);
int mlx4_SYNC_TPT(struct mlx4_dev *dev);
int mlx4_test_interrupts(struct mlx4_dev *dev);
-int mlx4_assign_eq(struct mlx4_dev *dev, char* name , int* vector);
+int mlx4_assign_eq(struct mlx4_dev *dev, char *name, struct cpu_rmap *rmap,
+ int *vector);
void mlx4_release_eq(struct mlx4_dev *dev, int vec);
int mlx4_wol_read(struct mlx4_dev *dev, u64 *config, int port);
--
1.7.1
^ permalink raw reply related
* [PATCH net-next V1 1/4] net/mlx4: Move MAC_MASK to a common place
From: Or Gerlitz @ 2012-07-19 8:33 UTC (permalink / raw)
To: davem; +Cc: roland, netdev, oren, yevgenyp, Amir Vadai, Or Gerlitz
In-Reply-To: <1342686832-21406-1-git-send-email-ogerlitz@mellanox.com>
From: Amir Vadai <amirv@mellanox.com>
Define this macro is one common place instead of duplicating it over the code
Signed-off-by: Amir Vadai <amirv@mellanox.com>
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
---
drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 6 +++---
drivers/net/ethernet/mellanox/mlx4/mcg.c | 1 -
drivers/net/ethernet/mellanox/mlx4/port.c | 1 -
.../net/ethernet/mellanox/mlx4/resource_tracker.c | 3 +--
include/linux/mlx4/driver.h | 2 ++
5 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index dd6a77b..9d0b88e 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -34,12 +34,12 @@
#include <linux/kernel.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
+#include <linux/mlx4/driver.h>
#include "mlx4_en.h"
#include "en_port.h"
#define EN_ETHTOOL_QP_ATTACH (1ull << 63)
-#define EN_ETHTOOL_MAC_MASK 0xffffffffffffULL
#define EN_ETHTOOL_SHORT_MASK cpu_to_be16(0xffff)
#define EN_ETHTOOL_WORD_MASK cpu_to_be32(0xffffffff)
@@ -751,7 +751,7 @@ static int mlx4_en_ethtool_to_net_trans_rule(struct net_device *dev,
struct ethhdr *eth_spec;
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_spec_list *spec_l2;
- __be64 mac_msk = cpu_to_be64(EN_ETHTOOL_MAC_MASK << 16);
+ __be64 mac_msk = cpu_to_be64(MLX4_MAC_MASK << 16);
err = mlx4_en_validate_flow(dev, cmd);
if (err)
@@ -761,7 +761,7 @@ static int mlx4_en_ethtool_to_net_trans_rule(struct net_device *dev,
if (!spec_l2)
return -ENOMEM;
- mac = priv->mac & EN_ETHTOOL_MAC_MASK;
+ mac = priv->mac & MLX4_MAC_MASK;
be_mac = cpu_to_be64(mac << 16);
spec_l2->id = MLX4_NET_TRANS_RULE_ID_ETH;
diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c
index 5bac0df..4ec3835 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mcg.c
+++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c
@@ -41,7 +41,6 @@
#define MGM_QPN_MASK 0x00FFFFFF
#define MGM_BLCK_LB_BIT 30
-#define MLX4_MAC_MASK 0xffffffffffffULL
static const u8 zero_gid[16]; /* automatically initialized to 0 */
diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c
index a51d1b9..028833f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/port.c
@@ -39,7 +39,6 @@
#include "mlx4.h"
#define MLX4_MAC_VALID (1ull << 63)
-#define MLX4_MAC_MASK 0xffffffffffffULL
#define MLX4_VLAN_VALID (1u << 31)
#define MLX4_VLAN_MASK 0xfff
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index c3fa919..94ceddd 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -41,13 +41,12 @@
#include <linux/slab.h>
#include <linux/mlx4/cmd.h>
#include <linux/mlx4/qp.h>
+#include <linux/if_ether.h>
#include "mlx4.h"
#include "fw.h"
#define MLX4_MAC_VALID (1ull << 63)
-#define MLX4_MAC_MASK 0x7fffffffffffffffULL
-#define ETH_ALEN 6
struct mac_res {
struct list_head list;
diff --git a/include/linux/mlx4/driver.h b/include/linux/mlx4/driver.h
index 5f1298b..8dc485f 100644
--- a/include/linux/mlx4/driver.h
+++ b/include/linux/mlx4/driver.h
@@ -37,6 +37,8 @@
struct mlx4_dev;
+#define MLX4_MAC_MASK 0xffffffffffffULL
+
enum mlx4_dev_event {
MLX4_DEV_EVENT_CATASTROPHIC_ERROR,
MLX4_DEV_EVENT_PORT_UP,
--
1.7.1
^ permalink raw reply related
* Re: [PATCH net-next V1 1/9] IB/ipoib: Add support for clones / multiple childs on the same partition
From: Or Gerlitz @ 2012-07-19 8:11 UTC (permalink / raw)
To: John Fastabend
Cc: David Miller, roland, netdev, ali, sean.hefty, shlomop, erezsh,
Or Gerlitz
In-Reply-To: <50073484.9070501@intel.com>
On Thu, Jul 19, 2012 at 1:11 AM, John Fastabend
<john.r.fastabend@intel.com> wrote:
> [...] Also what is a "pkey"
Hi John,
pkey (pronounced PEE KEY) stands for "partition keys" where partitions are
in a way IB's vlans, so the functionality provided by IPoIB child devices
is similar to what done by Ethernet 8021q vlan devices. Dave suggests
that we use rtnl_link_ops to create these childs instead of the proprietary
sysfs which was introduced when IPoIB was merged and is described here
Documentation/infiniband/ipoib.txt
Or.
^ permalink raw reply
* [PATCH] ipv4: fix address selection in fib_compute_spec_dst
From: Julian Anastasov @ 2012-07-19 7:35 UTC (permalink / raw)
To: David Miller; +Cc: netdev
ip_options_compile can be called for forwarded packets,
make sure the specific-destionation address is a local one as
specified in RFC 1812, 4.2.2.2 Addresses in Options
Signed-off-by: Julian Anastasov <ja@ssi.bg>
---
net/ipv4/fib_frontend.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 7a31194..b832036 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -206,7 +206,8 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb)
int scope;
rt = skb_rtable(skb);
- if (!(rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)))
+ if ((rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST | RTCF_LOCAL)) ==
+ RTCF_LOCAL)
return ip_hdr(skb)->daddr;
in_dev = __in_dev_get_rcu(dev);
--
1.7.3.4
^ permalink raw reply related
* [PATCH] ipv4: optimize fib_compute_spec_dst call in ip_options_echo
From: Julian Anastasov @ 2012-07-19 7:34 UTC (permalink / raw)
To: David Miller; +Cc: netdev
Move fib_compute_spec_dst at the only place where it
is needed.
Signed-off-by: Julian Anastasov <ja@ssi.bg>
---
net/ipv4/ip_options.c | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index a19d647..1dc01f9 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -93,7 +93,6 @@ int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb)
unsigned char *sptr, *dptr;
int soffset, doffset;
int optlen;
- __be32 daddr;
memset(dopt, 0, sizeof(struct ip_options));
@@ -105,8 +104,6 @@ int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb)
sptr = skb_network_header(skb);
dptr = dopt->__data;
- daddr = fib_compute_spec_dst(skb);
-
if (sopt->rr) {
optlen = sptr[sopt->rr+1];
soffset = sptr[sopt->rr+2];
@@ -180,6 +177,8 @@ int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb)
doffset -= 4;
}
if (doffset > 3) {
+ __be32 daddr = fib_compute_spec_dst(skb);
+
memcpy(&start[doffset-1], &daddr, 4);
dopt->faddr = faddr;
dptr[0] = start[0];
--
1.7.3.4
^ permalink raw reply related
* Re: [PATCHv1] net: stmmac: Add ip version to dts bindings
From: Stefan Roese @ 2012-07-19 7:25 UTC (permalink / raw)
To: dinguyen
Cc: netdev, dinh.linux, peppe.cavallaro, shiraz.hashim, deepak.sikri,
pavel, arnd
In-Reply-To: <1342654106-8163-1-git-send-email-dinguyen@altera.com>
On Thursday 19 July 2012 01:28:26 dinguyen@altera.com wrote:
> From: Dinh Nguyen <dinguyen@altera.com>
>
> Because there are multiple variants to the stmmac/dwmac driver, the
> dts bindings should be updated to include version of the IP used.
>
> Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
Acked-by: Stefan Roese <sr@denx.de>
Thanks,
Stefan
^ permalink raw reply
* Re: [RFC PATCH] net: Add support for virtual machine device queues (VMDQ)
From: Jiri Pirko @ 2012-07-19 6:42 UTC (permalink / raw)
To: John Fastabend
Cc: or.gerlitz, davem, roland, netdev, ali, sean.hefty, shlomop
In-Reply-To: <20120718220544.22619.97136.stgit@i40e.jf1>
Thu, Jul 19, 2012 at 12:05:44AM CEST, john.r.fastabend@intel.com wrote:
>This adds support to allow virtual net devices to be created. These
>devices can be managed independtly of the physical function but
>use the same physical link.
>
>This is analagous to an offloaded macvlan device. The primary
>advantage to VMDQ net devices over virtual functions is they can
>be added and removed dynamically as needed.
>
>Sending this for Or Gerlitz to take a peak at and see if this
>could be used for his ipoib bits. Its not pretty as is and
>likely needs some work its just an idea at this point use at
>your own risk I believe it compiles.
>---
>
> drivers/net/Kconfig | 7 ++
> drivers/net/Makefile | 1
> drivers/net/vmdq.c | 130 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/netdevice.h | 6 ++
> include/net/rtnetlink.h | 2 +
> net/core/rtnetlink.c | 10 +++
> 6 files changed, 155 insertions(+), 1 deletions(-)
> create mode 100644 drivers/net/vmdq.c
>
>diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
>index 0c2bd80..f28d951 100644
>--- a/drivers/net/Kconfig
>+++ b/drivers/net/Kconfig
>@@ -337,6 +337,13 @@ config VMXNET3
> To compile this driver as a module, choose M here: the
> module will be called vmxnet3.
>
>+config VMDQ
>+ tristate "Support Embedded bridge devices and child devices"
>+ help
>+ This supports chipsets with embedded switching components and
>+ allows us to create more net_devices that are logically slaves
>+ of a master net device.
>+
> source "drivers/net/hyperv/Kconfig"
>
> endif # NETDEVICES
>diff --git a/drivers/net/Makefile b/drivers/net/Makefile
>index 3d375ca..1eb5605 100644
>--- a/drivers/net/Makefile
>+++ b/drivers/net/Makefile
>@@ -21,6 +21,7 @@ obj-$(CONFIG_NET_TEAM) += team/
> obj-$(CONFIG_TUN) += tun.o
> obj-$(CONFIG_VETH) += veth.o
> obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
>+obj-$(CONFIG_VMDQ) += vmdq.o
>
> #
> # Networking Drivers
>diff --git a/drivers/net/vmdq.c b/drivers/net/vmdq.c
>new file mode 100644
>index 0000000..9acc429
>--- /dev/null
>+++ b/drivers/net/vmdq.c
>@@ -0,0 +1,130 @@
>+/*******************************************************************************
>+
>+ vmdq - Support virtual machine device queues (VMDQ)
>+ Copyright(c) 2012 Intel Corporation.
>+
>+ This program is free software; you can redistribute it and/or modify it
>+ under the terms and conditions of the GNU General Public License,
>+ version 2, as published by the Free Software Foundation.
>+
>+ This program is distributed in the hope 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.
>+
>+ You should have received a copy of the GNU General Public License along with
>+ this program; if not, write to the Free Software Foundation, Inc.,
>+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
>+
>+ The full GNU General Public License is included in this distribution in
>+ the file called "COPYING".
>+
>+ Contact Information:
>+ John Fastabend <john.r.fastabend@intel.com>
>+
>+*******************************************************************************/
>+
>+#include <linux/module.h>
>+#include <net/rtnetlink.h>
>+#include <linux/etherdevice.h>
>+
>+static int vmdq_newlink(struct net *src_net, struct net_device *dev,
>+ struct nlattr *tb[], struct nlattr *data[])
>+{
>+ struct net_device *lowerdev;
>+ int err = -EOPNOTSUPP;
>+
>+ if (!tb[IFLA_LINK])
>+ return -EINVAL;
>+
>+ lowerdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
>+ if (!lowerdev)
>+ return -ENODEV;
>+
>+ if (!tb[IFLA_MTU])
>+ dev->mtu = lowerdev->mtu;
>+ else if (dev->mtu > lowerdev->mtu)
>+ return -EINVAL;
>+
>+ if (lowerdev->netdev_ops->ndo_add_vmdq)
>+ err = lowerdev->netdev_ops->ndo_add_vmdq(lowerdev, dev);
>+
>+ if (err < 0)
>+ return err;
>+
>+ err = register_netdevice(dev);
>+ if (err < 0)
>+ lowerdev->netdev_ops->ndo_del_vmdq(lowerdev, dev);
>+ else
>+ netif_stacked_transfer_operstate(lowerdev, dev);
>+
>+ return err;
>+}
>+
>+void vmdq_dellink(struct net_device *dev, struct list_head *head)
>+{
>+ struct net_device *lowerdev = __dev_get_by_index(dev_net(dev), dev->iflink);
>+
>+ if (lowerdev && lowerdev->netdev_ops->ndo_del_vmdq)
>+ lowerdev->netdev_ops->ndo_del_vmdq(lowerdev, dev);
>+}
>+
>+static void vmdq_setup(struct net_device *dev)
>+{
>+ ether_setup(dev);
>+}
>+
>+size_t vmdq_getpriv_size(struct net *src_net, struct nlattr *tb[])
>+{
>+ struct net_device *lowerdev;
>+
>+ if (!tb[IFLA_LINK])
>+ return -EINVAL;
>+
>+ lowerdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
>+ if (!lowerdev)
>+ return -ENODEV;
>+
>+ return sizeof(netdev_priv(lowerdev));
>+}
Why exactly do you need to have the priv of same size as lowerdev? I do
not see you use that anywhere...
>+
>+int vmdq_get_tx_queues(struct net *net, struct nlattr *tb[])
>+{
>+ struct net_device *lowerdev;
>+
>+ if (!tb[IFLA_LINK])
>+ return -EINVAL;
>+
>+ lowerdev = __dev_get_by_index(net, nla_get_u32(tb[IFLA_LINK]));
>+ if (!lowerdev)
>+ return -ENODEV;
>+
>+ return lowerdev->num_tx_queues;
>+}
>+
>+static struct rtnl_link_ops vmdq_link_ops __read_mostly = {
>+ .kind = "vmdq",
>+ .setup = vmdq_setup,
>+ .newlink = vmdq_newlink,
>+ .dellink = vmdq_dellink,
>+ .get_priv_size = vmdq_getpriv_size,
>+ .get_tx_queues = vmdq_get_tx_queues,
>+};
>+
>+static int __init vmdq_init_module(void)
>+{
>+ return rtnl_link_register(&vmdq_link_ops);
>+}
>+
>+static void __exit vmdq_cleanup_module(void)
>+{
>+ rtnl_link_unregister(&vmdq_link_ops);
>+}
>+
>+module_init(vmdq_init_module);
>+module_exit(vmdq_cleanup_module);
>+
>+MODULE_LICENSE("GPL");
>+MODULE_AUTHOR("John Fastabend <john.r.fastabend@intel.com>");
>+MODULE_DESCRIPTION("Driver for embedded switch chipsets");
>+MODULE_ALIAS_RTNL_LINK("vmdq");
>diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>index ab0251d..d879c4d 100644
>--- a/include/linux/netdevice.h
>+++ b/include/linux/netdevice.h
>@@ -972,6 +972,12 @@ struct net_device_ops {
> struct nlattr *port[]);
> int (*ndo_get_vf_port)(struct net_device *dev,
> int vf, struct sk_buff *skb);
>+
>+ int (*ndo_add_vmdq)(struct net_device *lowerdev,
>+ struct net_device *dev);
>+ int (*ndo_del_vmdq)(struct net_device *lowerdev,
>+ struct net_device *dev);
>+
> int (*ndo_setup_tc)(struct net_device *dev, u8 tc);
> #if IS_ENABLED(CONFIG_FCOE)
> int (*ndo_fcoe_enable)(struct net_device *dev);
>diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h
>index bbcfd09..e9f903c 100644
>--- a/include/net/rtnetlink.h
>+++ b/include/net/rtnetlink.h
>@@ -79,6 +79,8 @@ struct rtnl_link_ops {
> const struct net_device *dev);
> int (*get_tx_queues)(struct net *net,
> struct nlattr *tb[]);
>+ size_t (*get_priv_size)(struct net *net,
>+ struct nlattr *tb[]);
> };
>
> extern int __rtnl_link_register(struct rtnl_link_ops *ops);
>diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
>index 2b325c3..2e33b9a 100644
>--- a/net/core/rtnetlink.c
>+++ b/net/core/rtnetlink.c
>@@ -1627,6 +1627,7 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net,
> int err;
> struct net_device *dev;
> unsigned int num_queues = 1;
>+ size_t priv_size = ops->priv_size;
>
> if (ops->get_tx_queues) {
> err = ops->get_tx_queues(src_net, tb);
>@@ -1635,8 +1636,15 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net,
> num_queues = err;
> }
>
>+ if (ops->get_priv_size) {
>+ err = ops->get_priv_size(src_net, tb);
>+ if (err < 0)
>+ goto err;
>+ priv_size = err;
>+ }
>+
> err = -ENOMEM;
>- dev = alloc_netdev_mq(ops->priv_size, ifname, ops->setup, num_queues);
>+ dev = alloc_netdev_mq(priv_size, ifname, ops->setup, num_queues);
> if (!dev)
> goto err;
>
>
>--
>To unsubscribe from this list: send the line "unsubscribe netdev" in
>the body of a message to majordomo@vger.kernel.org
>More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] Crash in tun
From: Eric Dumazet @ 2012-07-19 6:09 UTC (permalink / raw)
To: Mikulas Patocka; +Cc: Maxim Krasnyansky, vtun, netdev, davem
In-Reply-To: <alpine.DEB.2.00.1207190213001.16299@artax.karlin.mff.cuni.cz>
On Thu, 2012-07-19 at 03:12 +0200, Mikulas Patocka wrote:
> Hi
>
> I want to report a crash when using the tun driver. The crash can be
> reproduced by starting and stopping miredo in the loop:
>
> while ! dmesg | grep -q BUG; do
> /etc/init.d/miredo start;/etc/init.d/miredo stop
> done
>
> The crash happens in iput_final because inode->i_sb->s_op is NULL.
>
> The crash happens in 3.4, 3.4.5, 3.5-rc7. The crash does not happen in
> 3.3. When I attempted to bisect, unrelated changes in completely different
> driver regarding memory allocation triggered the crash --- so it is
> likely buggy even in 3.3 and before, it just didn't show up.
>
>
> What is obviously wrong:
> in the tun driver "struct socket" is embedded in "struct tun_struct".
>
> The backtrace goes through:
> netdev_run_todo -> tun_free_netdev -> sk_release_kernel -> sock_release.
>
> sock_release calls iput(SOCK_INODE(sock)). SOCK_INODE assumes that struct
> socket is embedded in "struct socket_alloc" (which is not true, it is
> embedded in "struct tun_struct"), gets a pointer to non-existing inode ---
> and there goes the crash.
>
>
> The crash can be fixed by writing any non-NULL value to tun->socket.file
> to prevent sock_release from calling iput(SOCK_INODE(sock)). Or maybe you
> come up with a better fix.
>
>
> Note another bug - when you are repeatedly starting and stopping miredo,
> even if it doesn't crash, the value "sockets: used" in
> /proc/*/net/sockstat keeps on decreasing. That's because sock_release
> decrements sockets_in_use, but there was no sock_alloc to increment it.
>
>
> Mikulas
>
> ---
>
> Signed-off-by: Mikulas Patocka <mikulas@artax.karlin.mff.cuni.cz>
>
> ---
> drivers/net/tun.c | 2 ++
> net/socket.c | 4 +++-
> 2 files changed, 5 insertions(+), 1 deletion(-)
>
> Index: linux-3.4.5-fast/drivers/net/tun.c
> ===================================================================
> --- linux-3.4.5-fast.orig/drivers/net/tun.c 2012-07-19 02:42:56.000000000 +0200
> +++ linux-3.4.5-fast/drivers/net/tun.c 2012-07-19 02:50:13.000000000 +0200
> @@ -358,6 +358,8 @@ static void tun_free_netdev(struct net_d
> {
> struct tun_struct *tun = netdev_priv(dev);
>
> + /* Prevent the code in sock_release from calling iput. */
> + tun->socket.file = (void *)1;
> sk_release_kernel(tun->socket.sk);
> }
>
> Index: linux-3.4.5-fast/net/socket.c
> ===================================================================
> --- linux-3.4.5-fast.orig/net/socket.c 2012-07-19 03:00:30.000000000 +0200
> +++ linux-3.4.5-fast/net/socket.c 2012-07-19 03:05:36.000000000 +0200
> @@ -522,7 +522,9 @@ void sock_release(struct socket *sock)
> if (rcu_dereference_protected(sock->wq, 1)->fasync_list)
> printk(KERN_ERR "sock_release: fasync list not empty!\n");
>
> - percpu_sub(sockets_in_use, 1);
> + /* a hack - sockets_in_use should not be decremented when tun calls this */
> + if (sock->file != (void *)1)
> + percpu_sub(sockets_in_use, 1);
> if (!sock->file) {
> iput(SOCK_INODE(sock));
> return;
>
> ---
>
> BUG: unable to handle kernel NULL pointer dereference at 0000000000000020
> IP: [<ffffffff81113246>] iput+0x76/0x230
> PGD 43e5e9067 PUD 4468d5067 PMD 0
> Oops: 0000 [#1] PREEMPT SMP
> CPU 1
> Modules linked in: ip6table_filter ip6_tables iptable_filter ip_tables
> ebtable_nat ebtables x_tables kvm_amd kvm tun cpufreq_userspace
> cpufreq_stats cpufreq_powersave cpufreq_ondemand cpufreq_conservative ipv6
> fuse raid0 md_mod lm85 hwmon_vid snd_usb_audio snd_pcm_oss snd_mixer_oss
> snd_pcm snd_timer snd_page_alloc snd_hwdep snd_usbmidi_lib snd_rawmidi snd
> soundcore ide_cd_mod cdrom ohci_hcd sata_svw libata serverworks ide_core
> powernow_k8 ehci_hcd usbcore tg3 usb_common floppy freq_table e100 skge
> mii i2c_piix4 libphy mperf k10temp rtc_cmos processor button hwmon
> microcode unix
>
> Pid: 10826, comm: miredo Not tainted 3.4.5 #85 empty empty/S3992-E
> RIP: 0010:[<ffffffff81113246>] [<ffffffff81113246>] iput+0x76/0x230
> RSP: 0018:ffff88043dc73e48 EFLAGS: 00010246
> RAX: 0000000000000001 RBX: ffff88023eba3f20 RCX: ffff880447d80000
> RDX: 0000000000000001 RSI: ffff88023eba3f98 RDI: ffff88023eba3f98
> RBP: ffff88023eba3f98 R08: ffffffff814804d8 R09: 0000000000000000
> R10: 0000000000000096 R11: dead000000100100 R12: ffff88023eba3f48
> R13: 0000000000000000 R14: ffff88043dc73e88 R15: 00000000ffffad3a
> FS: 00007fe71bbb9700(0000) GS:ffff880247c80000(0000)
> knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
> CR2: 0000000000000020 CR3: 000000043dcf0000 CR4: 00000000000007f0
> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> Process miredo (pid: 10826, threadinfo ffff88043dc72000, task
> ffff88043dc1b8d0) Stack:
> ffff88043dc73e88 ffff88023efef000 ffff88023eba3800 ffff88023eba3c00
> ffff88024749ecc0 ffffffff81281d93 ffff88023e8fa500 ffffffff81295438
> ffff88043dc73e88 ffff88043dc73e88 ffff88023eba38f8 ffff88023e8fa500
> Call Trace:
> [<ffffffff81281d93>] ? sk_release_kernel+0x23/0x40
> [<ffffffff81295438>] ? netdev_run_todo+0x1a8/0x260
> [<ffffffffa02522a3>] ? tun_chr_close+0x93/0xb0 [tun]
> [<ffffffff810fc3ed>] ? fput+0xdd/0x260
> [<ffffffff810f8aff>] ? filp_close+0x5f/0x90
> [<ffffffff810f8bc7>] ? sys_close+0x97/0x100
> [<ffffffff81311f22>] ? system_call_fastpath+0x16/0x1b
> Code: 6c 24 10 4c 8b 64 24 18 4c 8b 6c 24 20 48 83 c4 28 c3 0f 1f 00 f6 83
> 90 00 00 00 08 4c 8b 63 28 4d 8b 6c 24 30 0f 85 7d 01 00 00 <49> 8b 45 20
> 48 85 c0 0f 84 9d 00 00 00 48 89 df ff d0 85 c0 0f
> RIP [<ffffffff81113246>] iput+0x76/0x230
> RSP <ffff88043dc73e48>
> CR2: 0000000000000020
> ---[ end trace 6bd5160ffd3ba7a2 ]---
> note: miredo[10826] exited with preempt_count 1
Hi Mikulas
A fix for this problem is : http://patchwork.ozlabs.org/patch/170440/
^ permalink raw reply
* Re: [RFC PATCH] tun: don't zeroize sock->file on detach
From: Eric Dumazet @ 2012-07-19 6:06 UTC (permalink / raw)
To: Stanislav Kinsbursky; +Cc: davem, netdev, ruanzhijie, linux-kernel
In-Reply-To: <20120711114753.24395.53193.stgit@localhost6.localdomain6>
On Wed, 2012-07-11 at 15:48 +0400, Stanislav Kinsbursky wrote:
> This is a fix for bug, introduced in 3.4 kernel by commit
> 1ab5ecb90cb6a3df1476e052f76a6e8f6511cb3d, which, among other things, replaced
> simple sock_put() by sk_release_kernel(). Below is sequence, which leads to
> oops for non-persistent devices:
>
> tun_chr_close()
> tun_detach() <== tun->socket.file = NULL
> tun_free_netdev()
> sk_release_sock()
> sock_release(sock->file == NULL)
> iput(SOCK_INODE(sock)) <== dereference on NULL pointer
>
> This patch just removes zeroing of socket's file from __tun_detach().
> sock_release() will do this.
>
> Signed-off-by: Stanislav Kinsbursky <skinsbursky@parallels.com>
> ---
> drivers/net/tun.c | 1 -
> 1 files changed, 0 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> index 987aeef..c1639f3 100644
> --- a/drivers/net/tun.c
> +++ b/drivers/net/tun.c
> @@ -185,7 +185,6 @@ static void __tun_detach(struct tun_struct *tun)
> netif_tx_lock_bh(tun->dev);
> netif_carrier_off(tun->dev);
> tun->tfile = NULL;
> - tun->socket.file = NULL;
> netif_tx_unlock_bh(tun->dev);
>
> /* Drop read queue */
>
Acked-by: Eric Dumazet <edumazet@google.com>
Thanks !
^ permalink raw reply
* Re: [PATCH net-next] netns: correctly use per-netns ipv4 sysctl_tcp_mem
From: Eric Dumazet @ 2012-07-19 6:03 UTC (permalink / raw)
To: Huang Qiang
Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
David Miller
In-Reply-To: <50079D47.6040001-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
On Thu, 2012-07-19 at 13:38 +0800, Huang Qiang wrote:
> From: Yang Zhenzhang <yangzhenzhang-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>
> Now, kernel allows each net namespace to independently set up its levels
> for tcp memory pressure thresholds.
>
> But it seems there is a bug, as using the following steps:
>
> [root@host socket]# lxc-start -n test -f config /bin/bash
> [root@net-test socket]# ip route add default via 192.168.58.2
> [root@net-test socket]# echo 0 0 0 > /proc/sys/net/ipv4/tcp_mem
> [root@net-test socket]# scp root-Q0ErXNX1RuabR28l3DCWlg@public.gmane.org:/home/tcp_mem_test .
>
> and it still can transport the "tcp_mem_test" file which we hope it
> would not.
>
> It's because inet_init() (net/ipv4/af_inet.c)initialize the tcp_prot.sysctl_mem:
> tcp_prot.sysctl_mem = init_net.ipv4.sysctl_tcp_mem;
>
> So when the protocal is TCP, sk->sk_prot->sysctl_mem(following code)
> always use the ipv4 sysctl_tcp_mem of init_net namespace rather than
> it's own net namespace.
> This patch simply set "prot" equal to net->ipv4.sysctl_tcp_mem when
> the protocol type is TCP.
>
> Signed-off-by: Yang Zhenzhang <yangzhenzhang-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Huang Qiang <h.huangqiang-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> ---
> include/net/sock.h | 24 ++++++++++++++++--------
> 1 files changed, 16 insertions(+), 8 deletions(-)
>
> diff --git a/include/net/sock.h b/include/net/sock.h
> index 88de092..61f4363 100644
> --- a/include/net/sock.h
> +++ b/include/net/sock.h
> @@ -59,6 +59,7 @@
> #include <linux/static_key.h>
> #include <linux/aio.h>
> #include <linux/sched.h>
> +#include <linux/in.h>
>
> #include <linux/filter.h>
> #include <linux/rculist_nulls.h>
> @@ -1064,14 +1065,6 @@ static inline void sk_enter_memory_pressure(struct sock *sk)
> sk->sk_prot->enter_memory_pressure(sk);
> }
>
> -static inline long sk_prot_mem_limits(const struct sock *sk, int index)
> -{
> - long *prot = sk->sk_prot->sysctl_mem;
> - if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
> - prot = sk->sk_cgrp->sysctl_mem;
> - return prot[index];
> -}
> -
> static inline void memcg_memory_allocated_add(struct cg_proto *prot,
> unsigned long amt,
> int *parent_status)
> @@ -2155,6 +2148,21 @@ static inline void sk_change_net(struct sock *sk, struct net *net)
> sock_net_set(sk, hold_net(net));
> }
>
> +static inline long sk_prot_mem_limits(const struct sock *sk, int index)
> +{
> + long *prot = sk->sk_prot->sysctl_mem;
> +
> + if (sk->sk_protocol == IPPROTO_TCP) {
> + struct net *net = sock_net(sk);
> + prot = net->ipv4.sysctl_tcp_mem;
> + }
> +
if (sk->sk_protocol == IPPROTO_TCP)
prot = sock_net(sk)->ipv4.sysctl_tcp_mem;
> + if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
> + prot = sk->sk_cgrp->sysctl_mem;
> +
> + return prot[index];
> +}
> +
> static inline struct sock *skb_steal_sock(struct sk_buff *skb)
> {
> if (skb->sk) {
^ permalink raw reply
* [PATCH] sctp: Make "Invalid Stream Identifier" ERROR follows SACK when bundling
From: xufengzhang.main @ 2012-07-19 5:57 UTC (permalink / raw)
To: vyasevich, sri, davem; +Cc: linux-sctp, netdev, linux-kernel
When "Invalid Stream Identifier" ERROR happens after process the
received DATA chunks, this ERROR chunk is enqueued into outqueue
before SACK chunk, so when bundling ERROR chunk with SACK chunk,
the ERROR chunk is always placed first in the packet because of
the chunk's position in the outqueue.
This violates sctp specification:
RFC 4960 6.5. Stream Identifier and Stream Sequence Number
...The endpoint may bundle the ERROR chunk in the same
packet as the SACK as long as the ERROR follows the SACK.
So we must place SACK first when bundling "Invalid Stream Identifier"
ERROR and SACK in one packet.
Although we can do that by enqueue SACK chunk into outqueue before
ERROR chunk, it will violate the side-effect interpreter processing.
It's easy to do this job when dequeue chunks from the outqueue,
by this way, we introduce a flag 'has_isi_err' which indicate
whether or not the "Invalid Stream Identifier" ERROR happens.
Signed-off-by: Xufeng Zhang <xufeng.zhang@windriver.com>
---
include/net/sctp/structs.h | 2 ++
net/sctp/output.c | 26 ++++++++++++++++++++++++++
2 files changed, 28 insertions(+), 0 deletions(-)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 88949a9..5adf4de 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -842,6 +842,8 @@ struct sctp_packet {
has_sack:1, /* This packet contains a SACK chunk. */
has_auth:1, /* This packet contains an AUTH chunk */
has_data:1, /* This packet contains at least 1 DATA chunk */
+ has_isi_err:1, /* This packet contains a "Invalid Stream
+ * Identifier" ERROR chunk */
ipfragok:1, /* So let ip fragment this packet */
malloced:1; /* Is it malloced? */
};
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 817174e..77fb1ae 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -79,6 +79,7 @@ static void sctp_packet_reset(struct sctp_packet *packet)
packet->has_sack = 0;
packet->has_data = 0;
packet->has_auth = 0;
+ packet->has_isi_err = 0;
packet->ipfragok = 0;
packet->auth = NULL;
}
@@ -267,6 +268,7 @@ static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt,
sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
struct sctp_chunk *chunk)
{
+ struct sctp_chunk *lchunk;
sctp_xmit_t retval = SCTP_XMIT_OK;
__u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length));
@@ -316,7 +318,31 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
packet->has_cookie_echo = 1;
break;
+ case SCTP_CID_ERROR:
+ if (chunk->subh.err_hdr->cause & SCTP_ERROR_INV_STRM)
+ packet->has_isi_err = 1;
+ break;
+
case SCTP_CID_SACK:
+ /* RFC 4960
+ * 6.5 Stream Identifier and Stream Sequence Number
+ * The endpoint may bundle the ERROR chunk in the same
+ * packet as the SACK as long as the ERROR follows the SACK.
+ */
+ if (packet->has_isi_err) {
+ if (list_is_singular(&packet->chunk_list))
+ list_add(&chunk->list, &packet->chunk_list);
+ else {
+ lchunk = list_first_entry(&packet->chunk_list,
+ struct sctp_chunk, list);
+ list_add(&chunk->list, &lchunk->list);
+ }
+ packet->size += chunk_len;
+ chunk->transport = packet->transport;
+ packet->has_sack = 1;
+ goto finish;
+ }
+
packet->has_sack = 1;
break;
--
1.7.0.2
^ permalink raw reply related
* [PATCH net-next] netns: correctly use per-netns ipv4 sysctl_tcp_mem
From: Huang Qiang @ 2012-07-19 5:38 UTC (permalink / raw)
To: David Miller, glommer; +Cc: netdev, containers, yangzhenzhang
From: Yang Zhenzhang <yangzhenzhang@huawei.com>
Now, kernel allows each net namespace to independently set up its levels
for tcp memory pressure thresholds.
But it seems there is a bug, as using the following steps:
[root@host socket]# lxc-start -n test -f config /bin/bash
[root@net-test socket]# ip route add default via 192.168.58.2
[root@net-test socket]# echo 0 0 0 > /proc/sys/net/ipv4/tcp_mem
[root@net-test socket]# scp root@192.168.58.174:/home/tcp_mem_test .
and it still can transport the "tcp_mem_test" file which we hope it
would not.
It's because inet_init() (net/ipv4/af_inet.c)initialize the tcp_prot.sysctl_mem:
tcp_prot.sysctl_mem = init_net.ipv4.sysctl_tcp_mem;
So when the protocal is TCP, sk->sk_prot->sysctl_mem(following code)
always use the ipv4 sysctl_tcp_mem of init_net namespace rather than
it's own net namespace.
This patch simply set "prot" equal to net->ipv4.sysctl_tcp_mem when
the protocol type is TCP.
Signed-off-by: Yang Zhenzhang <yangzhenzhang@huawei.com>
Signed-off-by: Huang Qiang <h.huangqiang@huawei.com>
---
include/net/sock.h | 24 ++++++++++++++++--------
1 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/include/net/sock.h b/include/net/sock.h
index 88de092..61f4363 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -59,6 +59,7 @@
#include <linux/static_key.h>
#include <linux/aio.h>
#include <linux/sched.h>
+#include <linux/in.h>
#include <linux/filter.h>
#include <linux/rculist_nulls.h>
@@ -1064,14 +1065,6 @@ static inline void sk_enter_memory_pressure(struct sock *sk)
sk->sk_prot->enter_memory_pressure(sk);
}
-static inline long sk_prot_mem_limits(const struct sock *sk, int index)
-{
- long *prot = sk->sk_prot->sysctl_mem;
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
- prot = sk->sk_cgrp->sysctl_mem;
- return prot[index];
-}
-
static inline void memcg_memory_allocated_add(struct cg_proto *prot,
unsigned long amt,
int *parent_status)
@@ -2155,6 +2148,21 @@ static inline void sk_change_net(struct sock *sk, struct net *net)
sock_net_set(sk, hold_net(net));
}
+static inline long sk_prot_mem_limits(const struct sock *sk, int index)
+{
+ long *prot = sk->sk_prot->sysctl_mem;
+
+ if (sk->sk_protocol == IPPROTO_TCP) {
+ struct net *net = sock_net(sk);
+ prot = net->ipv4.sysctl_tcp_mem;
+ }
+
+ if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
+ prot = sk->sk_cgrp->sysctl_mem;
+
+ return prot[index];
+}
+
static inline struct sock *skb_steal_sock(struct sk_buff *skb)
{
if (skb->sk) {
--
1.7.1
^ permalink raw reply related
* [PATCH RESEND 9/9] x86: OLPC: move s/r-related EC cmds to EC driver
From: Andres Salomon @ 2012-07-19 4:44 UTC (permalink / raw)
To: Andrew Morton
Cc: Paul Fox, Daniel Drake, Richard A. Smith, linux-kernel,
libertas-dev, linux-wireless, netdev, platform-driver-x86, devel,
Thomas Gleixner, Ingo Molnar, H. Peter Anvin, x86, Dan Williams,
John W. Linville, Matthew Garrett, Anton Vorontsov,
David Woodhouse, Chris Ball, Jon Nettleton, Greg Kroah-Hartman
In-Reply-To: <20120718213713.232e4161@dev.queued.net>
The new EC driver calls platform-specific suspend and resume hooks; run
XO-1-specific EC commands from there, rather than deep in s/r code. If we
attempt to run EC commands after the new EC driver has suspended, it is
refused by the ec->suspended checks.
Signed-off-by: Andres Salomon <dilinger@queued.net>
---
arch/x86/platform/olpc/olpc-xo1-pm.c | 15 ------------
arch/x86/platform/olpc/olpc.c | 43 ++++++++++++++++++++++++++++-----
2 files changed, 36 insertions(+), 22 deletions(-)
diff --git a/arch/x86/platform/olpc/olpc-xo1-pm.c b/arch/x86/platform/olpc/olpc-xo1-pm.c
index 8054b64..d75582d 100644
--- a/arch/x86/platform/olpc/olpc-xo1-pm.c
+++ b/arch/x86/platform/olpc/olpc-xo1-pm.c
@@ -52,16 +52,11 @@ EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_clear);
static int xo1_power_state_enter(suspend_state_t pm_state)
{
unsigned long saved_sci_mask;
- int r;
/* Only STR is supported */
if (pm_state != PM_SUSPEND_MEM)
return -EINVAL;
- r = olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
- if (r)
- return r;
-
/*
* Save SCI mask (this gets lost since PM1_EN is used as a mask for
* wakeup events, which is not necessarily the same event set)
@@ -77,16 +72,6 @@ static int xo1_power_state_enter(suspend_state_t pm_state)
/* Restore SCI mask (using dword access to CS5536_PM1_EN) */
outl(saved_sci_mask, acpi_base + CS5536_PM1_STS);
- /* Tell the EC to stop inhibiting SCIs */
- olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
-
- /*
- * Tell the wireless module to restart USB communication.
- * Must be done twice.
- */
- olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
- olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
-
return 0;
}
diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c
index ed41b43..2737608 100644
--- a/arch/x86/platform/olpc/olpc.c
+++ b/arch/x86/platform/olpc/olpc.c
@@ -263,11 +263,6 @@ int olpc_ec_sci_query(u16 *sci_value)
}
EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
-static int olpc_ec_suspend(struct platform_device *pdev)
-{
- return olpc_ec_mask_write(ec_wakeup_mask);
-}
-
static bool __init check_ofw_architecture(struct device_node *root)
{
const char *olpc_arch;
@@ -339,9 +334,40 @@ static int olpc_xo1_ec_probe(struct platform_device *pdev)
return 0;
}
+static int olpc_xo1_ec_suspend(struct platform_device *pdev)
+{
+ olpc_ec_mask_write(ec_wakeup_mask);
+
+ /*
+ * Squelch SCIs while suspended. This is a fix for
+ * <http://dev.laptop.org/ticket/1835>.
+ */
+ return olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
+}
+
+static int olpc_xo1_ec_resume(struct platform_device *pdev)
+{
+ /* Tell the EC to stop inhibiting SCIs */
+ olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
+
+ /*
+ * Tell the wireless module to restart USB communication.
+ * Must be done twice.
+ */
+ olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
+ olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
+
+ return 0;
+}
static struct olpc_ec_driver ec_xo1_driver = {
- .suspend = olpc_ec_suspend,
+ .probe = olpc_xo1_ec_probe,
+ .suspend = olpc_xo1_ec_suspend,
+ .resume = olpc_xo1_ec_resume,
+ .ec_cmd = olpc_xo1_ec_cmd,
+};
+
+static struct olpc_ec_driver ec_xo1_5_driver = {
.probe = olpc_xo1_ec_probe,
.ec_cmd = olpc_xo1_ec_cmd,
};
@@ -354,7 +380,10 @@ static int __init olpc_init(void)
return 0;
/* register the XO-1 and 1.5-specific EC handler */
- olpc_ec_driver_register(&ec_xo1_driver, NULL);
+ if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */
+ olpc_ec_driver_register(&ec_xo1_driver, NULL);
+ else
+ olpc_ec_driver_register(&ec_xo1_5_driver, NULL);
platform_device_register_simple("olpc-ec", -1, NULL, 0);
/* assume B1 and above models always have a DCON */
--
1.7.2.5
^ permalink raw reply related
* [PATCH RESEND 8/9] Platform: OLPC: move global variables into priv struct
From: Andres Salomon @ 2012-07-19 4:44 UTC (permalink / raw)
To: Andrew Morton
Cc: devel, Daniel Drake, libertas-dev, Dan Williams, netdev,
Jon Nettleton, x86, linux-wireless, linux-kernel,
platform-driver-x86, Richard A. Smith, Paul Fox,
Greg Kroah-Hartman, Anton Vorontsov, H. Peter Anvin,
Thomas Gleixner, Chris Ball, David Woodhouse, Ingo Molnar,
John W. Linville, Matthew Garrett
In-Reply-To: <20120718213713.232e4161@dev.queued.net>
Populate olpc_ec_priv with variables that were previously global. This
makes things a tad bit clearer, IMO.
Signed-off-by: Andres Salomon <dilinger@queued.net>
---
drivers/platform/olpc/olpc-ec.c | 48 ++++++++++++++++++++++----------------
1 files changed, 28 insertions(+), 20 deletions(-)
diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c
index 1a15a79..0f9f859 100644
--- a/drivers/platform/olpc/olpc-ec.c
+++ b/drivers/platform/olpc/olpc-ec.c
@@ -31,6 +31,12 @@ struct ec_cmd_desc {
struct olpc_ec_priv {
struct olpc_ec_driver *drv;
+ struct work_struct worker;
+ struct mutex cmd_lock;
+
+ /* Pending EC commands */
+ struct list_head cmd_q;
+ spinlock_t cmd_q_lock;
struct dentry *dbgfs_dir;
@@ -46,16 +52,9 @@ struct olpc_ec_priv {
bool suspended;
};
-static void olpc_ec_worker(struct work_struct *w);
-
-static DECLARE_WORK(ec_worker, olpc_ec_worker);
-static LIST_HEAD(ec_cmd_q);
-static DEFINE_SPINLOCK(ec_cmd_q_lock);
-
static struct olpc_ec_driver *ec_driver;
static struct olpc_ec_priv *ec_priv;
static void *ec_cb_arg;
-static DEFINE_MUTEX(ec_cb_lock);
void olpc_ec_driver_register(struct olpc_ec_driver *drv, void *arg)
{
@@ -66,49 +65,51 @@ EXPORT_SYMBOL_GPL(olpc_ec_driver_register);
static void olpc_ec_worker(struct work_struct *w)
{
+ struct olpc_ec_priv *ec = container_of(w, struct olpc_ec_priv, worker);
struct ec_cmd_desc *desc = NULL;
unsigned long flags;
/* Grab the first pending command from the queue */
- spin_lock_irqsave(&ec_cmd_q_lock, flags);
- if (!list_empty(&ec_cmd_q)) {
- desc = list_first_entry(&ec_cmd_q, struct ec_cmd_desc, node);
+ spin_lock_irqsave(&ec->cmd_q_lock, flags);
+ if (!list_empty(&ec->cmd_q)) {
+ desc = list_first_entry(&ec->cmd_q, struct ec_cmd_desc, node);
list_del(&desc->node);
}
- spin_unlock_irqrestore(&ec_cmd_q_lock, flags);
+ spin_unlock_irqrestore(&ec->cmd_q_lock, flags);
/* Do we actually have anything to do? */
if (!desc)
return;
/* Protect the EC hw with a mutex; only run one cmd at a time */
- mutex_lock(&ec_cb_lock);
+ mutex_lock(&ec->cmd_lock);
desc->err = ec_driver->ec_cmd(desc->cmd, desc->inbuf, desc->inlen,
desc->outbuf, desc->outlen, ec_cb_arg);
- mutex_unlock(&ec_cb_lock);
+ mutex_unlock(&ec->cmd_lock);
/* Finished, wake up olpc_ec_cmd() */
complete(&desc->finished);
/* Run the worker thread again in case there are more cmds pending */
- schedule_work(&ec_worker);
+ schedule_work(&ec->worker);
}
/*
* Throw a cmd descripter onto the list. We now have SMP OLPC machines, so
* locking is pretty critical.
*/
-static void queue_ec_descriptor(struct ec_cmd_desc *desc)
+static void queue_ec_descriptor(struct ec_cmd_desc *desc,
+ struct olpc_ec_priv *ec)
{
unsigned long flags;
INIT_LIST_HEAD(&desc->node);
- spin_lock_irqsave(&ec_cmd_q_lock, flags);
- list_add_tail(&desc->node, &ec_cmd_q);
- spin_unlock_irqrestore(&ec_cmd_q_lock, flags);
+ spin_lock_irqsave(&ec->cmd_q_lock, flags);
+ list_add_tail(&desc->node, &ec->cmd_q);
+ spin_unlock_irqrestore(&ec->cmd_q_lock, flags);
- schedule_work(&ec_worker);
+ schedule_work(&ec->worker);
}
int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
@@ -137,7 +138,7 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
desc.err = 0;
init_completion(&desc.finished);
- queue_ec_descriptor(&desc);
+ queue_ec_descriptor(&desc, ec);
/* Timeouts must be handled in the platform-specific EC hook */
wait_for_completion(&desc.finished);
@@ -266,7 +267,14 @@ static int olpc_ec_probe(struct platform_device *pdev)
ec = kzalloc(sizeof(*ec), GFP_KERNEL);
if (!ec)
return -ENOMEM;
+
ec->drv = ec_driver;
+ INIT_WORK(&ec->worker, olpc_ec_worker);
+ mutex_init(&ec->cmd_lock);
+
+ INIT_LIST_HEAD(&ec->cmd_q);
+ spin_lock_init(&ec->cmd_q_lock);
+
ec_priv = ec;
platform_set_drvdata(pdev, ec);
--
1.7.2.5
^ permalink raw reply related
* [PATCH RESEND 7/9] Platform: OLPC: move debugfs support from x86 EC driver
From: Andres Salomon @ 2012-07-19 4:43 UTC (permalink / raw)
To: Andrew Morton
Cc: devel, Daniel Drake, libertas-dev, Dan Williams, netdev,
Jon Nettleton, x86, linux-wireless, linux-kernel,
platform-driver-x86, Richard A. Smith, Paul Fox,
Greg Kroah-Hartman, Anton Vorontsov, H. Peter Anvin,
Thomas Gleixner, Chris Ball, David Woodhouse, Ingo Molnar,
John W. Linville, Matthew Garrett
In-Reply-To: <20120718213713.232e4161@dev.queued.net>
There's nothing about the debugfs interface for the EC driver that is
architecture-specific, so move it into the arch-independent driver.
The code is mostly unchanged with the exception of renamed variables, coding
style changes, and API updates.
Signed-off-by: Andres Salomon <dilinger@queued.net>
---
arch/x86/platform/olpc/olpc.c | 97 --------------------------------
drivers/platform/olpc/olpc-ec.c | 117 +++++++++++++++++++++++++++++++++++++++
2 files changed, 117 insertions(+), 97 deletions(-)
diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c
index 4590096..ed41b43 100644
--- a/arch/x86/platform/olpc/olpc.c
+++ b/arch/x86/platform/olpc/olpc.c
@@ -19,7 +19,6 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/syscore_ops.h>
-#include <linux/debugfs.h>
#include <linux/mutex.h>
#include <linux/olpc-ec.h>
@@ -31,15 +30,6 @@
struct olpc_platform_t olpc_platform_info;
EXPORT_SYMBOL_GPL(olpc_platform_info);
-/* debugfs interface to EC commands */
-#define EC_MAX_CMD_ARGS (5 + 1) /* cmd byte + 5 args */
-#define EC_MAX_CMD_REPLY (8)
-
-static struct dentry *ec_debugfs_dir;
-static DEFINE_MUTEX(ec_debugfs_cmd_lock);
-static unsigned char ec_debugfs_resp[EC_MAX_CMD_REPLY];
-static unsigned int ec_debugfs_resp_bytes;
-
/* EC event mask to be applied during suspend (defining wakeup sources). */
static u16 ec_wakeup_mask;
@@ -273,91 +263,6 @@ int olpc_ec_sci_query(u16 *sci_value)
}
EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
-static ssize_t ec_debugfs_cmd_write(struct file *file, const char __user *buf,
- size_t size, loff_t *ppos)
-{
- int i, m;
- unsigned char ec_cmd[EC_MAX_CMD_ARGS];
- unsigned int ec_cmd_int[EC_MAX_CMD_ARGS];
- char cmdbuf[64];
- int ec_cmd_bytes;
-
- mutex_lock(&ec_debugfs_cmd_lock);
-
- size = simple_write_to_buffer(cmdbuf, sizeof(cmdbuf), ppos, buf, size);
-
- m = sscanf(cmdbuf, "%x:%u %x %x %x %x %x", &ec_cmd_int[0],
- &ec_debugfs_resp_bytes,
- &ec_cmd_int[1], &ec_cmd_int[2], &ec_cmd_int[3],
- &ec_cmd_int[4], &ec_cmd_int[5]);
- if (m < 2 || ec_debugfs_resp_bytes > EC_MAX_CMD_REPLY) {
- /* reset to prevent overflow on read */
- ec_debugfs_resp_bytes = 0;
-
- printk(KERN_DEBUG "olpc-ec: bad ec cmd: "
- "cmd:response-count [arg1 [arg2 ...]]\n");
- size = -EINVAL;
- goto out;
- }
-
- /* convert scanf'd ints to char */
- ec_cmd_bytes = m - 2;
- for (i = 0; i <= ec_cmd_bytes; i++)
- ec_cmd[i] = ec_cmd_int[i];
-
- printk(KERN_DEBUG "olpc-ec: debugfs cmd 0x%02x with %d args "
- "%02x %02x %02x %02x %02x, want %d returns\n",
- ec_cmd[0], ec_cmd_bytes, ec_cmd[1], ec_cmd[2], ec_cmd[3],
- ec_cmd[4], ec_cmd[5], ec_debugfs_resp_bytes);
-
- olpc_ec_cmd(ec_cmd[0], (ec_cmd_bytes == 0) ? NULL : &ec_cmd[1],
- ec_cmd_bytes, ec_debugfs_resp, ec_debugfs_resp_bytes);
-
- printk(KERN_DEBUG "olpc-ec: response "
- "%02x %02x %02x %02x %02x %02x %02x %02x (%d bytes expected)\n",
- ec_debugfs_resp[0], ec_debugfs_resp[1], ec_debugfs_resp[2],
- ec_debugfs_resp[3], ec_debugfs_resp[4], ec_debugfs_resp[5],
- ec_debugfs_resp[6], ec_debugfs_resp[7], ec_debugfs_resp_bytes);
-
-out:
- mutex_unlock(&ec_debugfs_cmd_lock);
- return size;
-}
-
-static ssize_t ec_debugfs_cmd_read(struct file *file, char __user *buf,
- size_t size, loff_t *ppos)
-{
- unsigned int i, r;
- char *rp;
- char respbuf[64];
-
- mutex_lock(&ec_debugfs_cmd_lock);
- rp = respbuf;
- rp += sprintf(rp, "%02x", ec_debugfs_resp[0]);
- for (i = 1; i < ec_debugfs_resp_bytes; i++)
- rp += sprintf(rp, ", %02x", ec_debugfs_resp[i]);
- mutex_unlock(&ec_debugfs_cmd_lock);
- rp += sprintf(rp, "\n");
-
- r = rp - respbuf;
- return simple_read_from_buffer(buf, size, ppos, respbuf, r);
-}
-
-static const struct file_operations ec_debugfs_genops = {
- .write = ec_debugfs_cmd_write,
- .read = ec_debugfs_cmd_read,
-};
-
-static void setup_debugfs(void)
-{
- ec_debugfs_dir = debugfs_create_dir("olpc-ec", 0);
- if (ec_debugfs_dir == ERR_PTR(-ENODEV))
- return;
-
- debugfs_create_file("cmd", 0600, ec_debugfs_dir, NULL,
- &ec_debugfs_genops);
-}
-
static int olpc_ec_suspend(struct platform_device *pdev)
{
return olpc_ec_mask_write(ec_wakeup_mask);
@@ -470,8 +375,6 @@ static int __init olpc_init(void)
return r;
}
- setup_debugfs();
-
return 0;
}
diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c
index a3d32c2..1a15a79 100644
--- a/drivers/platform/olpc/olpc-ec.c
+++ b/drivers/platform/olpc/olpc-ec.c
@@ -6,6 +6,7 @@
* Licensed under the GPL v2 or later.
*/
#include <linux/completion.h>
+#include <linux/debugfs.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
@@ -31,6 +32,8 @@ struct ec_cmd_desc {
struct olpc_ec_priv {
struct olpc_ec_driver *drv;
+ struct dentry *dbgfs_dir;
+
/*
* Running an EC command while suspending means we don't always finish
* the command before the machine suspends. This means that the EC
@@ -144,6 +147,114 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
}
EXPORT_SYMBOL_GPL(olpc_ec_cmd);
+#ifdef CONFIG_DEBUG_FS
+
+/*
+ * debugfs support for "generic commands", to allow sending
+ * arbitrary EC commands from userspace.
+ */
+
+#define EC_MAX_CMD_ARGS (5 + 1) /* cmd byte + 5 args */
+#define EC_MAX_CMD_REPLY (8)
+
+static DEFINE_MUTEX(ec_dbgfs_lock);
+static unsigned char ec_dbgfs_resp[EC_MAX_CMD_REPLY];
+static unsigned int ec_dbgfs_resp_bytes;
+
+static ssize_t ec_dbgfs_cmd_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ int i, m;
+ unsigned char ec_cmd[EC_MAX_CMD_ARGS];
+ unsigned int ec_cmd_int[EC_MAX_CMD_ARGS];
+ char cmdbuf[64];
+ int ec_cmd_bytes;
+
+ mutex_lock(&ec_dbgfs_lock);
+
+ size = simple_write_to_buffer(cmdbuf, sizeof(cmdbuf), ppos, buf, size);
+
+ m = sscanf(cmdbuf, "%x:%u %x %x %x %x %x", &ec_cmd_int[0],
+ &ec_dbgfs_resp_bytes, &ec_cmd_int[1], &ec_cmd_int[2],
+ &ec_cmd_int[3], &ec_cmd_int[4], &ec_cmd_int[5]);
+ if (m < 2 || ec_dbgfs_resp_bytes > EC_MAX_CMD_REPLY) {
+ /* reset to prevent overflow on read */
+ ec_dbgfs_resp_bytes = 0;
+
+ pr_debug("olpc-ec: bad ec cmd: cmd:response-count [arg1 [arg2 ...]]\n");
+ size = -EINVAL;
+ goto out;
+ }
+
+ /* convert scanf'd ints to char */
+ ec_cmd_bytes = m - 2;
+ for (i = 0; i <= ec_cmd_bytes; i++)
+ ec_cmd[i] = ec_cmd_int[i];
+
+ pr_debug("olpc-ec: debugfs cmd 0x%02x with %d args %02x %02x %02x %02x %02x, want %d returns\n",
+ ec_cmd[0], ec_cmd_bytes, ec_cmd[1], ec_cmd[2],
+ ec_cmd[3], ec_cmd[4], ec_cmd[5], ec_dbgfs_resp_bytes);
+
+ olpc_ec_cmd(ec_cmd[0], (ec_cmd_bytes == 0) ? NULL : &ec_cmd[1],
+ ec_cmd_bytes, ec_dbgfs_resp, ec_dbgfs_resp_bytes);
+
+ pr_debug("olpc-ec: response %02x %02x %02x %02x %02x %02x %02x %02x (%d bytes expected)\n",
+ ec_dbgfs_resp[0], ec_dbgfs_resp[1], ec_dbgfs_resp[2],
+ ec_dbgfs_resp[3], ec_dbgfs_resp[4], ec_dbgfs_resp[5],
+ ec_dbgfs_resp[6], ec_dbgfs_resp[7],
+ ec_dbgfs_resp_bytes);
+
+out:
+ mutex_unlock(&ec_dbgfs_lock);
+ return size;
+}
+
+static ssize_t ec_dbgfs_cmd_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ unsigned int i, r;
+ char *rp;
+ char respbuf[64];
+
+ mutex_lock(&ec_dbgfs_lock);
+ rp = respbuf;
+ rp += sprintf(rp, "%02x", ec_dbgfs_resp[0]);
+ for (i = 1; i < ec_dbgfs_resp_bytes; i++)
+ rp += sprintf(rp, ", %02x", ec_dbgfs_resp[i]);
+ mutex_unlock(&ec_dbgfs_lock);
+ rp += sprintf(rp, "\n");
+
+ r = rp - respbuf;
+ return simple_read_from_buffer(buf, size, ppos, respbuf, r);
+}
+
+static const struct file_operations ec_dbgfs_ops = {
+ .write = ec_dbgfs_cmd_write,
+ .read = ec_dbgfs_cmd_read,
+};
+
+static struct dentry *olpc_ec_setup_debugfs(void)
+{
+ struct dentry *dbgfs_dir;
+
+ dbgfs_dir = debugfs_create_dir("olpc-ec", NULL);
+ if (IS_ERR_OR_NULL(dbgfs_dir))
+ return NULL;
+
+ debugfs_create_file("cmd", 0600, dbgfs_dir, NULL, &ec_dbgfs_ops);
+
+ return dbgfs_dir;
+}
+
+#else
+
+static struct dentry *olpc_ec_setup_debugfs(void)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
static int olpc_ec_probe(struct platform_device *pdev)
{
struct olpc_ec_priv *ec;
@@ -160,6 +271,12 @@ static int olpc_ec_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ec);
err = ec_driver->probe ? ec_driver->probe(pdev) : 0;
+ if (err) {
+ ec_priv = NULL;
+ kfree(ec);
+ } else {
+ ec->dbgfs_dir = olpc_ec_setup_debugfs();
+ }
return err;
}
--
1.7.2.5
^ permalink raw reply related
* [PATCH RESEND 6/9] x86: OLPC: switch over to using new EC driver on x86
From: Andres Salomon @ 2012-07-19 4:42 UTC (permalink / raw)
To: Andrew Morton
Cc: Paul Fox, Daniel Drake, Richard A. Smith, linux-kernel,
libertas-dev, linux-wireless, netdev, platform-driver-x86, devel,
Thomas Gleixner, Ingo Molnar, H. Peter Anvin, x86, Dan Williams,
John W. Linville, Matthew Garrett, Anton Vorontsov,
David Woodhouse, Chris Ball, Jon Nettleton, Greg Kroah-Hartman
In-Reply-To: <20120718213713.232e4161@dev.queued.net>
This uses the new EC driver framework in drivers/platform/olpc. The
XO-1 and XO-1.5-specific code is still in arch/x86, but the generic stuff
(including a new workqueue; no more running EC commands with IRQs disabled!)
can be shared with other architectures.
Signed-off-by: Andres Salomon <dilinger@queued.net>
---
arch/x86/include/asm/olpc.h | 5 ---
arch/x86/platform/olpc/olpc.c | 53 ++++++++++++++++++++-------------------
drivers/platform/olpc/olpc-ec.c | 5 ---
3 files changed, 27 insertions(+), 36 deletions(-)
diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index 5b28f3e..72f9adf6 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -100,11 +100,6 @@ extern void olpc_xo1_pm_wakeup_clear(u16 value);
extern int pci_olpc_init(void);
-/* EC related functions */
-
-extern int olpc_ec_cmd_x86(unsigned char cmd, unsigned char *inbuf,
- size_t inlen, unsigned char *outbuf, size_t outlen);
-
/* SCI source values */
#define EC_SCI_SRC_EMPTY 0x00
diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c
index a3fa180..4590096 100644
--- a/arch/x86/platform/olpc/olpc.c
+++ b/arch/x86/platform/olpc/olpc.c
@@ -14,7 +14,6 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
-#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/platform_device.h>
@@ -32,8 +31,6 @@
struct olpc_platform_t olpc_platform_info;
EXPORT_SYMBOL_GPL(olpc_platform_info);
-static DEFINE_SPINLOCK(ec_lock);
-
/* debugfs interface to EC commands */
#define EC_MAX_CMD_ARGS (5 + 1) /* cmd byte + 5 args */
#define EC_MAX_CMD_REPLY (8)
@@ -126,16 +123,13 @@ static int __wait_on_obf(unsigned int line, unsigned int port, int desired)
* <http://wiki.laptop.org/go/Ec_specification>. Unfortunately, while
* OpenFirmware's source is available, the EC's is not.
*/
-int olpc_ec_cmd_x86(unsigned char cmd, unsigned char *inbuf, size_t inlen,
- unsigned char *outbuf, size_t outlen)
+static int olpc_xo1_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
+ size_t outlen, void *arg)
{
- unsigned long flags;
int ret = -EIO;
int i;
int restarts = 0;
- spin_lock_irqsave(&ec_lock, flags);
-
/* Clear OBF */
for (i = 0; i < 10 && (obf_status(0x6c) == 1); i++)
inb(0x68);
@@ -199,10 +193,8 @@ restart:
ret = 0;
err:
- spin_unlock_irqrestore(&ec_lock, flags);
return ret;
}
-EXPORT_SYMBOL_GPL(olpc_ec_cmd_x86);
void olpc_ec_wakeup_set(u16 value)
{
@@ -366,7 +358,7 @@ static void setup_debugfs(void)
&ec_debugfs_genops);
}
-static int olpc_ec_suspend(void)
+static int olpc_ec_suspend(struct platform_device *pdev)
{
return olpc_ec_mask_write(ec_wakeup_mask);
}
@@ -425,8 +417,28 @@ static int __init add_xo1_platform_devices(void)
return 0;
}
-static struct syscore_ops olpc_syscore_ops = {
+static int olpc_xo1_ec_probe(struct platform_device *pdev)
+{
+ /* get the EC revision */
+ olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
+ (unsigned char *) &olpc_platform_info.ecver, 1);
+
+ /* EC version 0x5f adds support for wide SCI mask */
+ if (olpc_platform_info.ecver >= 0x5f)
+ olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI;
+
+ pr_info("OLPC board revision %s%X (EC=%x)\n",
+ ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
+ olpc_platform_info.boardrev >> 4,
+ olpc_platform_info.ecver);
+
+ return 0;
+}
+
+static struct olpc_ec_driver ec_xo1_driver = {
.suspend = olpc_ec_suspend,
+ .probe = olpc_xo1_ec_probe,
+ .ec_cmd = olpc_xo1_ec_cmd,
};
static int __init olpc_init(void)
@@ -436,16 +448,14 @@ static int __init olpc_init(void)
if (!olpc_ofw_present() || !platform_detect())
return 0;
- spin_lock_init(&ec_lock);
+ /* register the XO-1 and 1.5-specific EC handler */
+ olpc_ec_driver_register(&ec_xo1_driver, NULL);
+ platform_device_register_simple("olpc-ec", -1, NULL, 0);
/* assume B1 and above models always have a DCON */
if (olpc_board_at_least(olpc_board(0xb1)))
olpc_platform_info.flags |= OLPC_F_DCON;
- /* get the EC revision */
- olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
- (unsigned char *) &olpc_platform_info.ecver, 1);
-
#ifdef CONFIG_PCI_OLPC
/* If the VSA exists let it emulate PCI, if not emulate in kernel.
* XO-1 only. */
@@ -453,14 +463,6 @@ static int __init olpc_init(void)
!cs5535_has_vsa2())
x86_init.pci.arch_init = pci_olpc_init;
#endif
- /* EC version 0x5f adds support for wide SCI mask */
- if (olpc_platform_info.ecver >= 0x5f)
- olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI;
-
- printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
- ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
- olpc_platform_info.boardrev >> 4,
- olpc_platform_info.ecver);
if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) { /* XO-1 */
r = add_xo1_platform_devices();
@@ -468,7 +470,6 @@ static int __init olpc_init(void)
return r;
}
- register_syscore_ops(&olpc_syscore_ops);
setup_debugfs();
return 0;
diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c
index cfba41f..a3d32c2 100644
--- a/drivers/platform/olpc/olpc-ec.c
+++ b/drivers/platform/olpc/olpc-ec.c
@@ -113,11 +113,6 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
struct olpc_ec_priv *ec = ec_priv;
struct ec_cmd_desc desc;
- /* XXX: this will be removed in later patches */
- /* Are we using old-style callers? */
- if (!ec_driver || !ec_driver->ec_cmd)
- return olpc_ec_cmd_x86(cmd, inbuf, inlen, outbuf, outlen);
-
/* Ensure a driver and ec hook have been registered */
if (WARN_ON(!ec_driver || !ec_driver->ec_cmd))
return -ENODEV;
--
1.7.2.5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox