* [RFC 0/2] Software implementation of IEEE 802.15.4
From: Dmitry Eremin-Solenikov @ 2009-08-10 14:16 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
linux-zigbee-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
Hi,
In this (small) patchset we'd like to present the grounds of our software
IEEE 802.15.4 MAC implementation. Especially this patchset contains only
packet dissection and data path (in generic parts) and virtual loopback
driver (as an example user).
Please review.
Dmitry Eremin-Solenikov (2):
mac802154: add a software MAC 802.15.4 implementation
ieee802154: add virtual loopback driver
MAINTAINERS | 1 +
drivers/ieee802154/Kconfig | 13 +
drivers/ieee802154/Makefile | 1 +
drivers/ieee802154/fakelb.c | 255 ++++++++++++
include/linux/mac802154.h | 35 ++
include/net/mac802154.h | 106 +++++
net/Kconfig | 1 +
net/Makefile | 1 +
net/mac802154/Kconfig | 17 +
net/mac802154/Makefile | 4 +
net/mac802154/dev.c | 924 +++++++++++++++++++++++++++++++++++++++++++
net/mac802154/mac802154.h | 85 ++++
net/mac802154/mac_cmd.c | 99 +++++
net/mac802154/mdev.c | 295 ++++++++++++++
net/mac802154/mib.h | 32 ++
net/mac802154/rx.c | 98 +++++
16 files changed, 1967 insertions(+), 0 deletions(-)
create mode 100644 drivers/ieee802154/fakelb.c
create mode 100644 include/linux/mac802154.h
create mode 100644 include/net/mac802154.h
create mode 100644 net/mac802154/Kconfig
create mode 100644 net/mac802154/Makefile
create mode 100644 net/mac802154/dev.c
create mode 100644 net/mac802154/mac802154.h
create mode 100644 net/mac802154/mac_cmd.c
create mode 100644 net/mac802154/mdev.c
create mode 100644 net/mac802154/mib.h
create mode 100644 net/mac802154/rx.c
------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now. http://p.sf.net/sfu/bobj-july
^ permalink raw reply
* [PATCH 1/2] mac802154: add a software MAC 802.15.4 implementation
From: Dmitry Eremin-Solenikov @ 2009-08-10 14:16 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA
Cc: linux-zigbee-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
linux-wireless-u79uwXL29TY76Z2rM5mHXA, Sergey Lapin
In-Reply-To: <1249913800-10176-1-git-send-email-dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Some of available devices are just dump radios implementing IEEE 802.15.4
PHY layer. This commit adds a common library that acts like an intermediate
layer between our socket family and drivers for those dumb devices.
Currently this is data-only part (no commands, no beacons). Control
interfaces will follow up shortly.
Note this implementaion is neither certified, nor feature complete!
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Sergey Lapin <slapin-9cOl001CZnBAfugRpC6u6w@public.gmane.org>
---
MAINTAINERS | 1 +
include/linux/mac802154.h | 35 ++
include/net/mac802154.h | 106 ++++++
net/Kconfig | 1 +
net/Makefile | 1 +
net/mac802154/Kconfig | 17 +
net/mac802154/Makefile | 4 +
net/mac802154/dev.c | 924 +++++++++++++++++++++++++++++++++++++++++++++
net/mac802154/mac802154.h | 85 +++++
net/mac802154/mac_cmd.c | 99 +++++
net/mac802154/mdev.c | 295 +++++++++++++++
net/mac802154/mib.h | 32 ++
net/mac802154/rx.c | 98 +++++
13 files changed, 1698 insertions(+), 0 deletions(-)
create mode 100644 include/linux/mac802154.h
create mode 100644 include/net/mac802154.h
create mode 100644 net/mac802154/Kconfig
create mode 100644 net/mac802154/Makefile
create mode 100644 net/mac802154/dev.c
create mode 100644 net/mac802154/mac802154.h
create mode 100644 net/mac802154/mac_cmd.c
create mode 100644 net/mac802154/mdev.c
create mode 100644 net/mac802154/mib.h
create mode 100644 net/mac802154/rx.c
diff --git a/MAINTAINERS b/MAINTAINERS
index d6befb2..5bdb64e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2529,6 +2529,7 @@ W: http://apps.sourceforge.net/trac/linux-zigbee
T: git git://git.kernel.org/pub/scm/linux/kernel/git/lowpan/lowpan.git
S: Maintained
F: net/ieee802154/
+F: net/mac802154/
F: drivers/ieee802154/
INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
diff --git a/include/linux/mac802154.h b/include/linux/mac802154.h
new file mode 100644
index 0000000..e95e38d
--- /dev/null
+++ b/include/linux/mac802154.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007, 2008, 2009 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef LINUX_MAC802154_H
+#define LINUX_MAC802154_H
+
+enum {
+ IFLA_WPAN_UNSPEC,
+ IFLA_WPAN_CHANNEL,
+ IFLA_WPAN_PAN_ID,
+ IFLA_WPAN_SHORT_ADDR,
+ IFLA_WPAN_COORD_SHORT_ADDR,
+ IFLA_WPAN_COORD_EXT_ADDR,
+ __IFLA_WPAN_MAX,
+};
+
+#define IFLA_WPAN_MAX (__IFLA_WPAN_MAX - 1)
+
+#endif
+
diff --git a/include/net/mac802154.h b/include/net/mac802154.h
new file mode 100644
index 0000000..db76799
--- /dev/null
+++ b/include/net/mac802154.h
@@ -0,0 +1,106 @@
+/*
+ * IEEE802.15.4-2003 specification
+ *
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ */
+#ifndef NET_MAC802154_H
+#define NET_MAC802154_H
+
+struct ieee802154_dev {
+ int extra_tx_headroom; /* headroom to reserve for tx skb */
+ void *priv; /* driver-specific data */
+ u32 channel_mask;
+ u8 current_channel;
+ u32 flags; /* Flags for device to set */
+ struct device *parent;
+};
+
+/* Checksum is in hardware and is omitted from packet */
+/**
+ * enum ieee802154_hw_flags - hardware flags
+ *
+ * These flags are used to indicate hardware capabilities to
+ * the stack. Generally, flags here should have their meaning
+ * done in a way that the simplest hardware doesn't need setting
+ * any particular flags. There are some exceptions to this rule,
+ * however, so you are advised to review these flags carefully.
+ *
+ * @IEEE802154_HW_OMIT_CKSUM:
+ * Indicates that receiver omits FCS and transmitter will add
+ * FCS on it's own.
+ *
+ * @IEEE802154_HW_AACK:
+ * Indicates that receiver will autorespond with ACK frames.
+ */
+enum ieee802154_hw_flags {
+ IEEE802154_HW_OMIT_CKSUM = 1 << 0,
+ IEEE802154_HW_AACK = 1 << 1,
+};
+
+struct sk_buff;
+
+/**
+ * struct ieee802154_ops - callbacks from mac802154 to the driver
+ *
+ * This structure contains various callbacks that the driver may
+ * handle or, in some cases, must handle, for example to transmit
+ * a frame.
+ *
+ * @start: Handler that 802.15.4 module calls for device initialisation.
+ * This function is called before the first interface is attached.
+ *
+ * @stop: Handler that 802.15.4 module calls for device cleanup
+ * This function is called after the last interface is removed.
+ *
+ * @tx: Handler that 802.15.4 module calls for each transmitted frame.
+ * skb cntains the buffer starting from the IEEE 802.15.4 header.
+ * The low-level driver should send the frame based on available
+ * configuration.
+ * This function should return zero or negative errno.
+ *
+ * @ed: Handler that 802.15.4 module calls for Energy Detection.
+ * This function should place the value for detected energy
+ * (usually device-dependant) in the level pointer and return
+ * either zero or negative errno.
+ *
+ * @set_channel: Set radio for listening on specific channel.
+ * Set the device for listening on specified channel.
+ * Returns either zero, or negative errno.
+ */
+struct ieee802154_ops {
+ struct module *owner;
+ int (*start)(struct ieee802154_dev *dev);
+ void (*stop)(struct ieee802154_dev *dev);
+ int (*xmit)(struct ieee802154_dev *dev,
+ struct sk_buff *skb);
+ int (*ed)(struct ieee802154_dev *dev, u8 *level);
+ int (*set_channel)(struct ieee802154_dev *dev,
+ int channel);
+};
+
+struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size,
+ struct ieee802154_ops *ops);
+int ieee802154_register_device(struct ieee802154_dev *dev);
+void ieee802154_unregister_device(struct ieee802154_dev *dev);
+void ieee802154_free_device(struct ieee802154_dev *dev);
+
+void ieee802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi);
+void ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb,
+ u8 lqi);
+#endif
+
diff --git a/net/Kconfig b/net/Kconfig
index 7051b97..b42d325 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -180,6 +180,7 @@ source "net/econet/Kconfig"
source "net/wanrouter/Kconfig"
source "net/phonet/Kconfig"
source "net/ieee802154/Kconfig"
+source "net/mac802154/Kconfig"
source "net/sched/Kconfig"
source "net/dcb/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index ba324ae..81115f6 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -61,6 +61,7 @@ ifneq ($(CONFIG_DCB),)
obj-y += dcb/
endif
obj-y += ieee802154/
+obj-y += mac802154/
ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_SYSCTL) += sysctl_net.o
diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig
new file mode 100644
index 0000000..2dd7f99
--- /dev/null
+++ b/net/mac802154/Kconfig
@@ -0,0 +1,17 @@
+config MAC802154
+ tristate "Generic IEEE 802.15.4 Soft Networking Stack (mac802154)"
+ depends on IEEE802154 && EXPERIMENTAL
+ select CRC_CCITT
+ ---help---
+ This option enables the hardware independent IEEE 802.15.4
+ networking stack for SoftMAC devices (the ones implementing
+ only PHY level of IEEE 802.15.4 standard).
+
+ Note: this implementation is neither certified, nor feature
+ complete! We do not garantee that it is compatible w/ other
+ implementations, etc.
+
+ If you plan to use HardMAC IEEE 802.15.4 devices, you can
+ say N here. Alternatievly you can say M to compile it as
+ module.
+
diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile
new file mode 100644
index 0000000..07b7821
--- /dev/null
+++ b/net/mac802154/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_MAC802154) += mac802154.o
+mac802154-objs := rx.o mdev.o dev.o mac_cmd.o
+
+EXTRA_CFLAGS += -Wall -DDEBUG
diff --git a/net/mac802154/dev.c b/net/mac802154/dev.c
new file mode 100644
index 0000000..b40f6fd
--- /dev/null
+++ b/net/mac802154/dev.c
@@ -0,0 +1,924 @@
+/*
+ * Copyright 2007, 2008, 2009 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <slapin-9cOl001CZnBAfugRpC6u6w@public.gmane.org>
+ * Maxim Gorbachyov <maxim.gorbachev-kv7WeFo6aLtBDgjK7y7TUQ@public.gmane.org>
+ */
+
+#include <linux/net.h>
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/if_arp.h>
+#include <linux/rculist.h>
+#include <linux/random.h>
+#include <linux/crc-ccitt.h>
+#include <linux/mac802154.h>
+#include <net/rtnetlink.h>
+
+#include <net/af_ieee802154.h>
+#include <net/mac802154.h>
+#include <net/ieee802154_netdev.h>
+#include <net/ieee802154.h>
+
+#include "mac802154.h"
+#include "mib.h"
+
+static int ieee802154_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *priv;
+ priv = netdev_priv(dev);
+
+ if (!(priv->hw->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
+ u16 crc = crc_ccitt(0, skb->data, skb->len);
+ u8 *data = skb_put(skb, 2);
+ data[0] = crc & 0xff;
+ data[1] = crc >> 8;
+ }
+
+ read_lock(&priv->mib_lock);
+ phy_cb(skb)->chan = priv->chan;
+ read_unlock(&priv->mib_lock);
+
+ skb->iif = dev->ifindex;
+ skb->dev = priv->hw->netdev;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+
+ dev->trans_start = jiffies;
+ dev_queue_xmit(skb);
+
+ return 0;
+}
+
+static int ieee802154_slave_open(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+ int res = 0;
+
+ if (priv->hw->open_count++ == 0) {
+ res = dev_open(priv->hw->netdev);
+ WARN_ON(res);
+ if (res)
+ goto err;
+ }
+
+ netif_start_queue(dev);
+ return 0;
+err:
+ priv->hw->open_count--;
+
+ return res;
+}
+
+static int ieee802154_slave_close(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+
+ if ((--priv->hw->open_count) == 0) {
+ if (netif_running(priv->hw->netdev))
+ dev_close(priv->hw->netdev);
+ }
+
+ return 0;
+}
+
+
+static int ieee802154_slave_ioctl(struct net_device *dev, struct ifreq *ifr,
+ int cmd)
+{
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+ struct sockaddr_ieee802154 *sa =
+ (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
+ int err = -ENOIOCTLCMD;
+
+ read_lock(&priv->mib_lock);
+
+ switch (cmd) {
+ case SIOCGIFADDR:
+ if (priv->pan_id == IEEE802154_PANID_BROADCAST ||
+ priv->short_addr == IEEE802154_ADDR_BROADCAST) {
+ err = -EADDRNOTAVAIL;
+ break;
+ }
+
+ sa->family = AF_IEEE802154;
+ sa->addr.addr_type = IEEE802154_ADDR_SHORT;
+ sa->addr.pan_id = priv->pan_id;
+ sa->addr.short_addr = priv->short_addr;
+
+ err = 0;
+ break;
+ case SIOCSIFADDR:
+ dev_warn(&dev->dev,
+ "Using DEBUGing ioctl SIOCSIFADDR isn't recommened!\n");
+ if (sa->family != AF_IEEE802154 ||
+ sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
+ sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
+ sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
+ sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
+ err = -EINVAL;
+ break;
+ }
+
+ priv->pan_id = sa->addr.pan_id;
+ priv->short_addr = sa->addr.short_addr;
+ err = 0;
+ break;
+ }
+ read_unlock(&priv->mib_lock);
+ return err;
+}
+
+static int ieee802154_slave_mac_addr(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+
+ if (netif_running(dev))
+ return -EBUSY;
+ /* FIXME: validate addr */
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ return 0;
+}
+
+static void ieee802154_haddr_copy_swap(u8 *dest, const u8 *src)
+{
+ int i;
+ for (i = 0; i < IEEE802154_ADDR_LEN; i++)
+ dest[IEEE802154_ADDR_LEN - i - 1] = src[i];
+}
+
+static int ieee802154_header_create(struct sk_buff *skb,
+ struct net_device *dev,
+ unsigned short type, const void *_daddr,
+ const void *_saddr, unsigned len)
+{
+ u8 head[24] = {};
+ int pos = 0;
+
+ u16 fc;
+ const struct ieee802154_addr *saddr = _saddr;
+ const struct ieee802154_addr *daddr = _daddr;
+ struct ieee802154_addr dev_addr;
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+ fc = mac_cb_type(skb);
+ if (mac_cb_is_ackreq(skb))
+ fc |= IEEE802154_FC_ACK_REQ;
+
+ pos = 2;
+
+ head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */
+
+ if (!daddr)
+ return -EINVAL;
+
+ if (!saddr) {
+ read_lock(&priv->mib_lock);
+ if (priv->short_addr == IEEE802154_ADDR_BROADCAST ||
+ priv->short_addr == IEEE802154_ADDR_UNDEF ||
+ priv->pan_id == IEEE802154_PANID_BROADCAST) {
+ dev_addr.addr_type = IEEE802154_ADDR_LONG;
+ memcpy(dev_addr.hwaddr, dev->dev_addr,
+ IEEE802154_ADDR_LEN);
+ } else {
+ dev_addr.addr_type = IEEE802154_ADDR_SHORT;
+ dev_addr.short_addr = priv->short_addr;
+ }
+
+ dev_addr.pan_id = priv->pan_id;
+ saddr = &dev_addr;
+
+ read_unlock(&priv->mib_lock);
+ }
+
+ if (daddr->addr_type != IEEE802154_ADDR_NONE) {
+ fc |= (daddr->addr_type << IEEE802154_FC_DAMODE_SHIFT);
+
+ head[pos++] = daddr->pan_id & 0xff;
+ head[pos++] = daddr->pan_id >> 8;
+
+ if (daddr->addr_type == IEEE802154_ADDR_SHORT) {
+ head[pos++] = daddr->short_addr & 0xff;
+ head[pos++] = daddr->short_addr >> 8;
+ } else {
+ ieee802154_haddr_copy_swap(head + pos, daddr->hwaddr);
+ pos += IEEE802154_ADDR_LEN;
+ }
+ }
+
+ if (saddr->addr_type != IEEE802154_ADDR_NONE) {
+ fc |= (saddr->addr_type << IEEE802154_FC_SAMODE_SHIFT);
+
+ if ((saddr->pan_id == daddr->pan_id) &&
+ (saddr->pan_id != IEEE802154_PANID_BROADCAST))
+ /* PANID compression/ intra PAN */
+ fc |= IEEE802154_FC_INTRA_PAN;
+ else {
+ head[pos++] = saddr->pan_id & 0xff;
+ head[pos++] = saddr->pan_id >> 8;
+ }
+
+ if (saddr->addr_type == IEEE802154_ADDR_SHORT) {
+ head[pos++] = saddr->short_addr & 0xff;
+ head[pos++] = saddr->short_addr >> 8;
+ } else {
+ ieee802154_haddr_copy_swap(head + pos, saddr->hwaddr);
+ pos += IEEE802154_ADDR_LEN;
+ }
+ }
+
+ head[0] = fc;
+ head[1] = fc >> 8;
+
+ memcpy(skb_push(skb, pos), head, pos);
+
+ return pos;
+}
+
+static int ieee802154_header_parse(const struct sk_buff *skb,
+ unsigned char *haddr)
+{
+ const u8 *hdr = skb_mac_header(skb), *tail = skb_tail_pointer(skb);
+ struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
+ u16 fc;
+ int da_type;
+
+ if (hdr + 3 > tail)
+ goto malformed;
+
+ fc = hdr[0] | (hdr[1] << 8);
+
+ hdr += 3;
+
+ da_type = IEEE802154_FC_DAMODE(fc);
+ addr->addr_type = IEEE802154_FC_SAMODE(fc);
+
+ switch (da_type) {
+ case IEEE802154_ADDR_NONE:
+ if (fc & IEEE802154_FC_INTRA_PAN)
+ goto malformed;
+ break;
+
+ case IEEE802154_ADDR_LONG:
+ if (hdr + 2 > tail)
+ goto malformed;
+ if (fc & IEEE802154_FC_INTRA_PAN) {
+ addr->pan_id = hdr[0] | (hdr[1] << 8);
+ hdr += 2;
+ }
+
+ if (hdr + IEEE802154_ADDR_LEN > tail)
+ goto malformed;
+ hdr += IEEE802154_ADDR_LEN;
+ break;
+
+ case IEEE802154_ADDR_SHORT:
+ if (hdr + 2 > tail)
+ goto malformed;
+ if (fc & IEEE802154_FC_INTRA_PAN) {
+ addr->pan_id = hdr[0] | (hdr[1] << 8);
+ hdr += 2;
+ }
+
+ if (hdr + 2 > tail)
+ goto malformed;
+ hdr += 2;
+ break;
+
+ default:
+ goto malformed;
+
+ }
+
+ switch (addr->addr_type) {
+ case IEEE802154_ADDR_NONE:
+ break;
+
+ case IEEE802154_ADDR_LONG:
+ if (hdr + 2 > tail)
+ goto malformed;
+ if (!(fc & IEEE802154_FC_INTRA_PAN)) {
+ addr->pan_id = hdr[0] | (hdr[1] << 8);
+ hdr += 2;
+ }
+
+ if (hdr + IEEE802154_ADDR_LEN > tail)
+ goto malformed;
+ memcpy(addr->hwaddr, hdr, IEEE802154_ADDR_LEN);
+ hdr += IEEE802154_ADDR_LEN;
+ break;
+
+ case IEEE802154_ADDR_SHORT:
+ if (hdr + 2 > tail)
+ goto malformed;
+ if (!(fc & IEEE802154_FC_INTRA_PAN)) {
+ addr->pan_id = hdr[0] | (hdr[1] << 8);
+ hdr += 2;
+ }
+
+ if (hdr + 2 > tail)
+ goto malformed;
+ addr->short_addr = hdr[0] | (hdr[1] << 8);
+ hdr += 2;
+ break;
+
+ default:
+ goto malformed;
+
+ }
+
+ return sizeof(struct ieee802154_addr);
+
+malformed:
+ pr_debug("malformed packet\n");
+ return 0;
+}
+
+static struct header_ops ieee802154_header_ops = {
+ .create = ieee802154_header_create,
+ .parse = ieee802154_header_parse,
+};
+
+static const struct net_device_ops ieee802154_slave_ops = {
+ .ndo_open = ieee802154_slave_open,
+ .ndo_stop = ieee802154_slave_close,
+ .ndo_start_xmit = ieee802154_net_xmit,
+ .ndo_do_ioctl = ieee802154_slave_ioctl,
+ .ndo_set_mac_address = ieee802154_slave_mac_addr,
+};
+
+static void ieee802154_netdev_setup(struct net_device *dev)
+{
+ dev->addr_len = IEEE802154_ADDR_LEN;
+ memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
+ dev->features = NETIF_F_NO_CSUM;
+ dev->hard_header_len = 2 + 1 + 20 + 14;
+ dev->header_ops = &ieee802154_header_ops;
+ dev->needed_tailroom = 2; /* FCS */
+ dev->mtu = 127;
+ dev->tx_queue_len = 10;
+ dev->type = ARPHRD_IEEE802154;
+ dev->flags = IFF_NOARP | IFF_BROADCAST;
+ dev->watchdog_timeo = 0;
+
+ dev->destructor = free_netdev;
+ dev->netdev_ops = &ieee802154_slave_ops;
+ dev->ml_priv = &mac802154_mlme;
+}
+
+/*
+ * This is for hw unregistration only, as it doesn't do RCU locking
+ */
+void ieee802154_drop_slaves(struct ieee802154_dev *hw)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(hw);
+ struct ieee802154_sub_if_data *sdata, *next;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry_safe(sdata, next, &priv->slaves, list) {
+ mutex_lock(&sdata->hw->slaves_mtx);
+ list_del(&sdata->list);
+ mutex_unlock(&sdata->hw->slaves_mtx);
+
+ dev_put(sdata->hw->netdev);
+
+ unregister_netdevice(sdata->dev);
+ }
+}
+
+static int ieee802154_netdev_validate(struct nlattr *tb[],
+ struct nlattr *data[])
+{
+ if (tb[IFLA_ADDRESS])
+ if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN)
+ return -EINVAL;
+
+ if (tb[IFLA_BROADCAST])
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ieee802154_netdev_newlink(struct net_device *dev,
+ struct nlattr *tb[],
+ struct nlattr *data[])
+{
+ struct net_device *mdev;
+ struct ieee802154_sub_if_data *priv;
+ struct ieee802154_priv *ipriv;
+ int err;
+
+ if (!tb[IFLA_LINK])
+ return -EOPNOTSUPP;
+
+ mdev = __dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK]));
+ if (!mdev)
+ return -ENODEV;
+
+ if (mdev->type != ARPHRD_IEEE802154_PHY)
+ return -EINVAL;
+
+ ipriv = netdev_priv(mdev);
+
+ priv = netdev_priv(dev);
+ priv->dev = dev;
+ priv->hw = ipriv;
+
+ rwlock_init(&priv->mib_lock);
+
+ get_random_bytes(&priv->bsn, 1);
+ get_random_bytes(&priv->dsn, 1);
+
+ priv->pan_id = IEEE802154_PANID_BROADCAST;
+ priv->short_addr = IEEE802154_ADDR_BROADCAST;
+
+ dev_hold(ipriv->netdev);
+
+ dev->needed_headroom = ipriv->hw.extra_tx_headroom;
+
+ SET_NETDEV_DEV(dev, &ipriv->netdev->dev);
+
+ err = register_netdevice(dev);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&ipriv->slaves_mtx);
+ list_add_tail_rcu(&priv->list, &ipriv->slaves);
+ mutex_unlock(&ipriv->slaves_mtx);
+
+ return 0;
+}
+
+static void ieee802154_netdev_dellink(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata;
+ ASSERT_RTNL();
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ sdata = netdev_priv(dev);
+
+ mutex_lock(&sdata->hw->slaves_mtx);
+ list_del_rcu(&sdata->list);
+ mutex_unlock(&sdata->hw->slaves_mtx);
+
+ dev_put(sdata->hw->netdev);
+
+ synchronize_rcu();
+ unregister_netdevice(sdata->dev);
+}
+
+static size_t ieee802154_netdev_get_size(const struct net_device *dev)
+{
+ return nla_total_size(2) + /* IFLA_WPAN_CHANNEL */
+ nla_total_size(2) + /* IFLA_WPAN_PAN_ID */
+ nla_total_size(2) + /* IFLA_WPAN_SHORT_ADDR */
+ nla_total_size(2) + /* IFLA_WPAN_COORD_SHORT_ADDR */
+ nla_total_size(8); /* IFLA_WPAN_COORD_EXT_ADDR */
+}
+
+static int ieee802154_netdev_fill_info(struct sk_buff *skb,
+ const struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+ read_lock(&priv->mib_lock);
+
+ NLA_PUT_U16(skb, IFLA_WPAN_CHANNEL, priv->chan);
+ NLA_PUT_U16(skb, IFLA_WPAN_PAN_ID, priv->pan_id);
+ NLA_PUT_U16(skb, IFLA_WPAN_SHORT_ADDR, priv->short_addr);
+ /* TODO: IFLA_WPAN_COORD_SHORT_ADDR */
+ /* TODO: IFLA_WPAN_COORD_EXT_ADDR */
+
+ read_unlock(&priv->mib_lock);
+
+ return 0;
+
+nla_put_failure:
+ read_unlock(&priv->mib_lock);
+ return -EMSGSIZE;
+}
+
+static struct rtnl_link_ops wpan_link_ops __read_mostly = {
+ .kind = "wpan",
+ .priv_size = sizeof(struct ieee802154_sub_if_data),
+ .setup = ieee802154_netdev_setup,
+ .validate = ieee802154_netdev_validate,
+ .newlink = ieee802154_netdev_newlink,
+ .dellink = ieee802154_netdev_dellink,
+ .get_size = ieee802154_netdev_get_size,
+ .fill_info = ieee802154_netdev_fill_info,
+};
+
+static int ieee802154_process_beacon(struct net_device *dev,
+ struct sk_buff *skb)
+{
+ pr_warning("ieee802154: beacon frames are not yet supported\n");
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static int ieee802154_process_ack(struct net_device *dev, struct sk_buff *skb)
+{
+ pr_debug("got ACK for SEQ=%d\n", mac_cb(skb)->seq);
+
+ kfree_skb(skb);
+ return NET_RX_SUCCESS;
+}
+
+static int ieee802154_process_data(struct net_device *dev, struct sk_buff *skb)
+{
+ return netif_rx(skb);
+}
+
+static int ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ pr_debug("%s Getting packet via slave interface %s\n",
+ __func__, sdata->dev->name);
+
+ read_lock(&sdata->mib_lock);
+
+ switch (mac_cb(skb)->da.addr_type) {
+ case IEEE802154_ADDR_NONE:
+ if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE)
+ /* FIXME: check if we are PAN coordinator :) */
+ skb->pkt_type = PACKET_OTHERHOST;
+ else
+ /* ACK comes with both addresses empty */
+ skb->pkt_type = PACKET_HOST;
+ break;
+ case IEEE802154_ADDR_LONG:
+ if (mac_cb(skb)->da.pan_id != sdata->pan_id &&
+ mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST)
+ skb->pkt_type = PACKET_OTHERHOST;
+ else if (!memcmp(mac_cb(skb)->da.hwaddr, sdata->dev->dev_addr,
+ IEEE802154_ADDR_LEN))
+ skb->pkt_type = PACKET_HOST;
+ else
+ skb->pkt_type = PACKET_OTHERHOST;
+ break;
+ case IEEE802154_ADDR_SHORT:
+ if (mac_cb(skb)->da.pan_id != sdata->pan_id &&
+ mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST)
+ skb->pkt_type = PACKET_OTHERHOST;
+ else if (mac_cb(skb)->da.short_addr == sdata->short_addr)
+ skb->pkt_type = PACKET_HOST;
+ else if (mac_cb(skb)->da.short_addr ==
+ IEEE802154_ADDR_BROADCAST)
+ skb->pkt_type = PACKET_BROADCAST;
+ else
+ skb->pkt_type = PACKET_OTHERHOST;
+ break;
+ }
+
+ read_unlock(&sdata->mib_lock);
+
+ skb->dev = sdata->dev;
+
+ if (skb->pkt_type == PACKET_HOST && mac_cb_is_ackreq(skb) &&
+ !(sdata->hw->hw.flags & IEEE802154_HW_AACK))
+ dev_warn(&sdata->dev->dev,
+ "ACK requested, however AACK not supported.\n");
+
+ switch (mac_cb_type(skb)) {
+ case IEEE802154_FC_TYPE_BEACON:
+ return ieee802154_process_beacon(sdata->dev, skb);
+ case IEEE802154_FC_TYPE_ACK:
+ return ieee802154_process_ack(sdata->dev, skb);
+ case IEEE802154_FC_TYPE_MAC_CMD:
+ return ieee802154_process_cmd(sdata->dev, skb);
+ case IEEE802154_FC_TYPE_DATA:
+ return ieee802154_process_data(sdata->dev, skb);
+ default:
+ pr_warning("ieee802154: Bad frame received (type = %d)\n",
+ mac_cb_type(skb));
+ kfree_skb(skb);
+ return NET_RX_DROP;
+ }
+}
+
+static u8 fetch_skb_u8(struct sk_buff *skb)
+{
+ u8 ret;
+
+ BUG_ON(skb->len < 1);
+
+ ret = skb->data[0];
+ skb_pull(skb, 1);
+
+ return ret;
+}
+
+static u16 fetch_skb_u16(struct sk_buff *skb)
+{
+ u16 ret;
+
+ BUG_ON(skb->len < 2);
+
+ ret = skb->data[0] + (skb->data[1] * 256);
+ skb_pull(skb, 2);
+ return ret;
+}
+
+static void fetch_skb_u64(struct sk_buff *skb, void *data)
+{
+ BUG_ON(skb->len < IEEE802154_ADDR_LEN);
+
+ memcpy(data, skb->data, IEEE802154_ADDR_LEN);
+ skb_pull(skb, IEEE802154_ADDR_LEN);
+}
+
+#define IEEE802154_FETCH_U8(skb, var) \
+ do { \
+ if (skb->len < 1) \
+ goto exit_error; \
+ var = fetch_skb_u8(skb); \
+ } while (0)
+
+#define IEEE802154_FETCH_U16(skb, var) \
+ do { \
+ if (skb->len < 2) \
+ goto exit_error; \
+ var = fetch_skb_u16(skb); \
+ } while (0)
+
+#define IEEE802154_FETCH_U64(skb, var) \
+ do { \
+ if (skb->len < IEEE802154_ADDR_LEN) \
+ goto exit_error; \
+ fetch_skb_u64(skb, &var); \
+ } while (0)
+
+static int parse_frame_start(struct sk_buff *skb)
+{
+ u8 *head = skb->data;
+ u16 fc;
+
+ if (skb->len < 3) {
+ pr_debug("frame size %d bytes is too short\n", skb->len);
+ return -EINVAL;
+ }
+
+ IEEE802154_FETCH_U16(skb, fc);
+ IEEE802154_FETCH_U8(skb, mac_cb(skb)->seq);
+
+ pr_debug("%s: %04x dsn%02x\n", __func__, fc, head[2]);
+
+ mac_cb(skb)->flags = IEEE802154_FC_TYPE(fc);
+
+ if (fc & IEEE802154_FC_ACK_REQ) {
+ pr_debug("%s(): ACKNOWLEDGE required\n", __func__);
+ mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
+ }
+
+ if (fc & IEEE802154_FC_SECEN)
+ mac_cb(skb)->flags |= MAC_CB_FLAG_SECEN;
+
+ if (fc & IEEE802154_FC_INTRA_PAN)
+ mac_cb(skb)->flags |= MAC_CB_FLAG_INTRAPAN;
+
+ /* TODO */
+ if (mac_cb_is_secen(skb)) {
+ pr_info("security support is not implemented\n");
+ return -EINVAL;
+ }
+
+ mac_cb(skb)->sa.addr_type = IEEE802154_FC_SAMODE(fc);
+ if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_NONE)
+ pr_debug("%s(): src addr_type is NONE\n", __func__);
+
+ mac_cb(skb)->da.addr_type = IEEE802154_FC_DAMODE(fc);
+ if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_NONE)
+ pr_debug("%s(): dst addr_type is NONE\n", __func__);
+
+ if (IEEE802154_FC_TYPE(fc) == IEEE802154_FC_TYPE_ACK) {
+ /* ACK can only have NONE-type addresses */
+ if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE ||
+ mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE)
+ return -EINVAL;
+ }
+
+ if (mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE) {
+ IEEE802154_FETCH_U16(skb, mac_cb(skb)->da.pan_id);
+
+ if (mac_cb_is_intrapan(skb)) { /* ! panid compress */
+ pr_debug("%s(): src IEEE802154_FC_INTRA_PAN\n",
+ __func__);
+ mac_cb(skb)->sa.pan_id = mac_cb(skb)->da.pan_id;
+ pr_debug("%s(): src PAN address %04x\n",
+ __func__, mac_cb(skb)->sa.pan_id);
+ }
+
+ pr_debug("%s(): dst PAN address %04x\n",
+ __func__, mac_cb(skb)->da.pan_id);
+
+ if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_SHORT) {
+ IEEE802154_FETCH_U16(skb, mac_cb(skb)->da.short_addr);
+ pr_debug("%s(): dst SHORT address %04x\n",
+ __func__, mac_cb(skb)->da.short_addr);
+
+ } else {
+ IEEE802154_FETCH_U64(skb, mac_cb(skb)->da.hwaddr);
+ pr_debug("%s(): dst hardware addr\n", __func__);
+ }
+ }
+
+ if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) {
+ pr_debug("%s(): got src non-NONE address\n", __func__);
+ if (!(mac_cb_is_intrapan(skb))) { /* ! panid compress */
+ IEEE802154_FETCH_U16(skb, mac_cb(skb)->sa.pan_id);
+ pr_debug("%s(): src IEEE802154_FC_INTRA_PAN\n",
+ __func__);
+ }
+
+ if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_SHORT) {
+ IEEE802154_FETCH_U16(skb, mac_cb(skb)->sa.short_addr);
+ pr_debug("%s(): src IEEE802154_ADDR_SHORT\n",
+ __func__);
+ } else {
+ IEEE802154_FETCH_U64(skb, mac_cb(skb)->sa.hwaddr);
+ pr_debug("%s(): src hardware addr\n", __func__);
+ }
+ }
+
+ return 0;
+
+exit_error:
+ return -EINVAL;
+}
+
+void ieee802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(hw);
+ struct ieee802154_sub_if_data *sdata, *prev = NULL;
+ int ret;
+
+ BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb));
+ pr_debug("%s()\n", __func__);
+
+ if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
+ u16 crc;
+
+ if (skb->len < 2) {
+ pr_debug("%s(): Got invalid frame\n", __func__);
+ goto out;
+ }
+ crc = crc_ccitt(0, skb->data, skb->len);
+ if (crc) {
+ pr_debug("%s(): CRC mismatch\n", __func__);
+ goto out;
+ }
+ skb_trim(skb, skb->len - 2); /* CRC */
+ }
+
+ ret = parse_frame_start(skb); /* 3 bytes pulled after this */
+ if (ret) {
+ pr_debug("%s(): Got invalid frame\n", __func__);
+ goto out;
+ }
+
+ pr_debug("%s() frame %d\n", __func__, mac_cb_type(skb));
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &priv->slaves, list)
+ {
+ if (prev) {
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2)
+ ieee802154_subif_frame(prev, skb2);
+ }
+
+ prev = sdata;
+ }
+
+ if (prev) {
+ ieee802154_subif_frame(prev, skb);
+ skb = NULL;
+ }
+
+ rcu_read_unlock();
+
+out:
+ dev_kfree_skb(skb);
+ return;
+}
+
+u16 ieee802154_dev_get_pan_id(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+ u16 ret;
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ read_lock(&priv->mib_lock);
+ ret = priv->pan_id;
+ read_unlock(&priv->mib_lock);
+
+ return ret;
+}
+
+u16 ieee802154_dev_get_short_addr(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+ u16 ret;
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ read_lock(&priv->mib_lock);
+ ret = priv->short_addr;
+ read_unlock(&priv->mib_lock);
+
+ return ret;
+}
+
+void ieee802154_dev_set_pan_id(struct net_device *dev, u16 val)
+{
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ write_lock(&priv->mib_lock);
+ priv->pan_id = val;
+ write_unlock(&priv->mib_lock);
+}
+void ieee802154_dev_set_short_addr(struct net_device *dev, u16 val)
+{
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ write_lock(&priv->mib_lock);
+ priv->short_addr = val;
+ write_unlock(&priv->mib_lock);
+}
+void ieee802154_dev_set_channel(struct net_device *dev, u8 val)
+{
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ write_lock(&priv->mib_lock);
+ priv->chan = val;
+ write_unlock(&priv->mib_lock);
+}
+
+u8 ieee802154_dev_get_dsn(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+ u16 ret;
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ write_lock(&priv->mib_lock);
+ ret = priv->dsn++;
+ write_unlock(&priv->mib_lock);
+
+ return ret;
+}
+
+u8 ieee802154_dev_get_bsn(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *priv = netdev_priv(dev);
+ u16 ret;
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ write_lock(&priv->mib_lock);
+ ret = priv->bsn++;
+ write_unlock(&priv->mib_lock);
+
+ return ret;
+}
+
+static int __init ieee802154_dev_init(void)
+{
+ return rtnl_link_register(&wpan_link_ops);
+}
+module_init(ieee802154_dev_init);
+
+static void __exit ieee802154_dev_exit(void)
+{
+ rtnl_link_unregister(&wpan_link_ops);
+}
+module_exit(ieee802154_dev_exit);
+
+MODULE_ALIAS_RTNL_LINK("wpan");
+
diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h
new file mode 100644
index 0000000..f4e8d29
--- /dev/null
+++ b/net/mac802154/mac802154.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Pavel Smolenskiy <pavel.smolenskiy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ * Maxim Gorbachyov <maxim.gorbachev-kv7WeFo6aLtBDgjK7y7TUQ@public.gmane.org>
+ * Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ */
+#ifndef MAC802154_H
+#define MAC802154_H
+
+#include <linux/spinlock.h>
+struct ieee802154_priv {
+ struct ieee802154_dev hw;
+ struct ieee802154_ops *ops;
+
+ struct net_device *netdev; /* mwpanX device */
+ int open_count;
+ /* As in mac80211 slaves list is modified:
+ * 1) under the RTNL
+ * 2) protected by slaves_mtx;
+ * 3) in an RCU manner
+ *
+ * So atomic readers can use any of this protection methods
+ */
+ struct list_head slaves;
+ struct mutex slaves_mtx;
+ /* This one is used for scanning and other
+ * jobs not to be interfered with serial driver */
+ struct workqueue_struct *dev_workqueue;
+};
+
+#define ieee802154_to_priv(_hw) container_of(_hw, struct ieee802154_priv, hw)
+
+struct ieee802154_sub_if_data {
+ struct list_head list; /* the ieee802154_priv->slaves list */
+
+ struct ieee802154_priv *hw;
+ struct net_device *dev;
+
+ rwlock_t mib_lock;
+
+ u16 pan_id;
+ u16 short_addr;
+
+ u8 chan;
+
+ /* MAC BSN field */
+ u8 bsn;
+ /* MAC BSN field */
+ u8 dsn;
+};
+
+void ieee802154_drop_slaves(struct ieee802154_dev *hw);
+
+void ieee802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb);
+
+struct ieee802154_phy_cb {
+ u8 lqi;
+ u8 chan;
+};
+
+static inline struct ieee802154_phy_cb *phy_cb(struct sk_buff *skb)
+{
+ return (struct ieee802154_phy_cb *)skb->cb;
+}
+
+extern struct ieee802154_mlme_ops mac802154_mlme;
+
+int ieee802154_process_cmd(struct net_device *dev, struct sk_buff *skb);
+
+#endif
diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
new file mode 100644
index 0000000..8941628
--- /dev/null
+++ b/net/mac802154/mac_cmd.c
@@ -0,0 +1,99 @@
+/*
+ * MAC commands interface
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <slapin-9cOl001CZnBAfugRpC6u6w@public.gmane.org>
+ * Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <net/af_ieee802154.h>
+#include <net/mac802154.h>
+#include <net/ieee802154.h>
+#include <net/ieee802154_netdev.h>
+#include <net/nl802154.h>
+
+#include "mac802154.h"
+#include "mib.h"
+
+int ieee802154_process_cmd(struct net_device *dev, struct sk_buff *skb)
+{
+ pr_warning("ieee802154: command frames are not yet supported\n");
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+
+static int ieee802154_mlme_assoc_req(struct net_device *dev,
+ struct ieee802154_addr *addr, u8 channel, u8 cap)
+{
+ /* We simply emulate it here */
+ return ieee802154_nl_assoc_confirm(dev,
+ ieee802154_dev_get_short_addr(dev),
+ IEEE802154_SUCCESS);
+}
+
+static int ieee802154_mlme_assoc_resp(struct net_device *dev,
+ struct ieee802154_addr *addr, u16 short_addr, u8 status)
+{
+ return 0;
+}
+
+static int ieee802154_mlme_disassoc_req(struct net_device *dev,
+ struct ieee802154_addr *addr, u8 reason)
+{
+ return ieee802154_nl_disassoc_confirm(dev, IEEE802154_SUCCESS);
+}
+
+static int ieee802154_mlme_start_req(struct net_device *dev,
+ struct ieee802154_addr *addr,
+ u8 channel,
+ u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
+ u8 coord_realign)
+{
+ /* We don't emulate beacons here at all, so START should fail */
+ ieee802154_nl_start_confirm(dev, IEEE802154_INVALID_PARAMETER);
+ return 0;
+}
+
+static int ieee802154_mlme_scan_req(struct net_device *dev, u8 type,
+ u32 channels,
+ u8 duration)
+{
+ u8 edl[27] = {};
+ return ieee802154_nl_scan_confirm(dev, IEEE802154_SUCCESS, type,
+ channels,
+ type == IEEE802154_MAC_SCAN_ED ? edl : NULL);
+}
+
+
+struct ieee802154_mlme_ops mac802154_mlme = {
+ .assoc_req = ieee802154_mlme_assoc_req,
+ .assoc_resp = ieee802154_mlme_assoc_resp,
+ .disassoc_req = ieee802154_mlme_disassoc_req,
+ .start_req = ieee802154_mlme_start_req,
+ .scan_req = ieee802154_mlme_scan_req,
+
+ .get_pan_id = ieee802154_dev_get_pan_id,
+ .get_short_addr = ieee802154_dev_get_short_addr,
+ .get_dsn = ieee802154_dev_get_dsn,
+ .get_bsn = ieee802154_dev_get_bsn,
+};
+
diff --git a/net/mac802154/mdev.c b/net/mac802154/mdev.c
new file mode 100644
index 0000000..191e942
--- /dev/null
+++ b/net/mac802154/mdev.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <net/route.h>
+
+#include <net/af_ieee802154.h>
+#include <net/mac802154.h>
+
+#include "mac802154.h"
+
+struct xmit_work {
+ struct sk_buff *skb;
+ struct work_struct work;
+ struct ieee802154_priv *priv;
+};
+
+static void ieee802154_xmit_worker(struct work_struct *work)
+{
+ struct xmit_work *xw = container_of(work, struct xmit_work, work);
+ int res;
+
+ if (xw->priv->hw.current_channel != phy_cb(xw->skb)->chan) {
+ res = xw->priv->ops->set_channel(&xw->priv->hw,
+ phy_cb(xw->skb)->chan);
+ if (res) {
+ pr_debug("set_channel failed\n");
+ goto out;
+ }
+ }
+
+ res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb);
+
+out:
+ /* FIXME: result processing and/or requeue!!! */
+ dev_kfree_skb(xw->skb);
+
+ kfree(xw);
+}
+
+static int ieee802154_master_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ieee802154_priv *priv = netdev_priv(dev);
+ struct xmit_work *work;
+
+ if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) {
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC);
+ if (!work)
+ return NETDEV_TX_BUSY;
+
+ INIT_WORK(&work->work, ieee802154_xmit_worker);
+ work->skb = skb;
+ work->priv = priv;
+
+ queue_work(priv->dev_workqueue, &work->work);
+
+ return NETDEV_TX_OK;
+}
+
+static int ieee802154_master_open(struct net_device *dev)
+{
+ int rc;
+ struct ieee802154_priv *priv = netdev_priv(dev);
+
+ if (!priv) {
+ pr_debug("%s:%s: unable to get master private data\n",
+ __FILE__, __func__);
+ return -ENODEV;
+ }
+
+ if (!priv->open_count)
+ return -EOPNOTSUPP;
+
+ rc = priv->ops->start(&priv->hw);
+
+ if (!rc)
+ netif_tx_start_all_queues(dev);
+
+ return rc;
+}
+
+static int ieee802154_master_close(struct net_device *dev)
+{
+ struct ieee802154_priv *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata;
+
+ ASSERT_RTNL();
+
+ /* We are under RTNL, so it's fine to do this */
+ list_for_each_entry(sdata, &priv->slaves, list)
+ if (netif_running(sdata->dev))
+ dev_close(sdata->dev);
+
+ priv->ops->stop(&priv->hw);
+
+ return 0;
+}
+
+static ssize_t ieee802154_netdev_show(const struct device *dev,
+ struct device_attribute *attr, char *buf,
+ ssize_t (*format)(const struct net_device *, char *))
+{
+ struct net_device *netdev = to_net_dev(dev);
+ ssize_t ret = -EINVAL;
+
+ if (netdev->reg_state <= NETREG_REGISTERED)
+ ret = (*format)(netdev, buf);
+
+ return ret;
+}
+#define MASTER_SHOW(field, format_string) \
+static ssize_t format_##field(const struct net_device *dev, char *buf) \
+{ \
+ struct ieee802154_priv *priv = netdev_priv(dev); \
+ return sprintf(buf, format_string, priv->hw.field); \
+} \
+static ssize_t show_##field(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return ieee802154_netdev_show(dev, attr, buf, format_##field); \
+} \
+static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL)
+
+static const char fmt_long_hex[] = "%#lx\n";
+static const char fmt_hex[] = "%#x\n";
+static const char fmt_dec[] = "%d\n";
+
+MASTER_SHOW(current_channel, fmt_dec);
+MASTER_SHOW(channel_mask, fmt_hex);
+
+static struct attribute *pmib_attrs[] = {
+ &dev_attr_current_channel.attr,
+ &dev_attr_channel_mask.attr,
+ NULL
+};
+
+static struct attribute_group pmib_group = {
+ .name = "pib",
+ .attrs = pmib_attrs,
+};
+
+static const struct net_device_ops ieee802154_master_ops = {
+ .ndo_open = ieee802154_master_open,
+ .ndo_stop = ieee802154_master_close,
+ .ndo_start_xmit = ieee802154_master_hard_start_xmit,
+};
+
+static void ieee802154_netdev_setup_master(struct net_device *dev)
+{
+ dev->addr_len = 0;
+ dev->features = NETIF_F_NO_CSUM;
+ dev->hard_header_len = 0;
+ dev->mtu = 127;
+ dev->tx_queue_len = 0;
+ dev->type = ARPHRD_IEEE802154_PHY;
+ dev->flags = IFF_NOARP | IFF_BROADCAST;
+ dev->watchdog_timeo = 0;
+
+ dev->netdev_ops = &ieee802154_master_ops;
+}
+
+struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size,
+ struct ieee802154_ops *ops)
+{
+ struct net_device *dev;
+ struct ieee802154_priv *priv;
+
+ dev = alloc_netdev(ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_size,
+ "mwpan%d", ieee802154_netdev_setup_master);
+ if (!dev) {
+ printk(KERN_ERR
+ "Failure to initialize master IEEE802154 device\n");
+ return NULL;
+ }
+ priv = netdev_priv(dev);
+ priv->netdev = dev;
+ priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN);
+
+ BUG_ON(!dev);
+ BUG_ON(!ops);
+ BUG_ON(!ops->xmit);
+ BUG_ON(!ops->ed);
+ BUG_ON(!ops->start);
+ BUG_ON(!ops->stop);
+
+ if (!try_module_get(ops->owner)) {
+ free_netdev(dev);
+ return NULL;
+ }
+
+ priv->ops = ops;
+
+ INIT_LIST_HEAD(&priv->slaves);
+ mutex_init(&priv->slaves_mtx);
+
+ return &priv->hw;
+}
+EXPORT_SYMBOL(ieee802154_alloc_device);
+
+void ieee802154_free_device(struct ieee802154_dev *hw)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(hw);
+
+ BUG_ON(!list_empty(&priv->slaves));
+ BUG_ON(!priv->netdev);
+
+ module_put(priv->ops->owner);
+
+ free_netdev(priv->netdev);
+}
+EXPORT_SYMBOL(ieee802154_free_device);
+
+int ieee802154_register_device(struct ieee802154_dev *dev)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+ struct net_device *ndev = priv->netdev;
+
+ int rc;
+
+ rtnl_lock();
+ if (strchr(ndev->name, '%')) {
+ rc = dev_alloc_name(ndev, ndev->name);
+ if (rc < 0)
+ goto out_unlock;
+ }
+
+ priv->dev_workqueue =
+ create_singlethread_workqueue(ndev->name);
+ if (!priv->dev_workqueue) {
+ rc = -ENOMEM;
+ goto out_unlock;
+ }
+
+ ndev->needed_headroom = priv->hw.extra_tx_headroom;
+ SET_NETDEV_DEV(ndev, priv->hw.parent);
+
+ ndev->sysfs_groups[1] = &pmib_group;
+
+ rc = register_netdevice(ndev);
+ if (rc < 0)
+ goto out_wq;
+
+ rtnl_unlock();
+
+ return 0;
+
+out_wq:
+ destroy_workqueue(priv->dev_workqueue);
+out_unlock:
+ rtnl_unlock();
+ return rc;
+}
+EXPORT_SYMBOL(ieee802154_register_device);
+
+void ieee802154_unregister_device(struct ieee802154_dev *dev)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+
+ flush_workqueue(priv->dev_workqueue);
+ destroy_workqueue(priv->dev_workqueue);
+
+ rtnl_lock();
+
+ ieee802154_drop_slaves(dev);
+ unregister_netdevice(priv->netdev);
+
+ rtnl_unlock();
+}
+EXPORT_SYMBOL(ieee802154_unregister_device);
+
+MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
+MODULE_LICENSE("GPL v2");
+
diff --git a/net/mac802154/mib.h b/net/mac802154/mib.h
new file mode 100644
index 0000000..57bf03f
--- /dev/null
+++ b/net/mac802154/mib.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef MIB802154_H
+#define MIB802154_H
+
+/* FIXME: should be dropped in favour of generic MIB API */
+u8 ieee802154_dev_get_dsn(struct net_device *dev);
+u8 ieee802154_dev_get_bsn(struct net_device *dev);
+u16 ieee802154_dev_get_pan_id(struct net_device *dev);
+u16 ieee802154_dev_get_short_addr(struct net_device *dev);
+void ieee802154_dev_set_pan_id(struct net_device *dev, u16 val);
+void ieee802154_dev_set_short_addr(struct net_device *dev, u16 val);
+void ieee802154_dev_set_channel(struct net_device *dev, u8 chan);
+
+
+#endif
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
new file mode 100644
index 0000000..81a269e
--- /dev/null
+++ b/net/mac802154/rx.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2007, 2008, 2009 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Pavel Smolenskiy <pavel.smolenskiy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ * Maxim Gorbachyov <maxim.gorbachev-kv7WeFo6aLtBDgjK7y7TUQ@public.gmane.org>
+ * Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+
+#include <net/mac802154.h>
+
+#include "mac802154.h"
+
+static void __ieee802154_rx_prepare(struct ieee802154_dev *dev,
+ struct sk_buff *skb, u8 lqi)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+
+ BUG_ON(!skb);
+
+ phy_cb(skb)->lqi = lqi;
+
+ skb->dev = priv->netdev;
+
+ skb->iif = skb->dev->ifindex;
+
+ skb->protocol = htons(ETH_P_IEEE802154);
+
+ skb_reset_mac_header(skb);
+}
+
+void ieee802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi)
+{
+ struct sk_buff *skb2;
+
+ __ieee802154_rx_prepare(dev, skb, lqi);
+
+ skb2 = skb_clone(skb, GFP_KERNEL);
+ netif_rx(skb2);
+
+ ieee802154_subif_rx(dev, skb);
+}
+EXPORT_SYMBOL(ieee802154_rx);
+
+struct rx_work {
+ struct sk_buff *skb;
+ struct work_struct work;
+ struct ieee802154_dev *dev;
+};
+
+static void ieee802154_rx_worker(struct work_struct *work)
+{
+ struct rx_work *rw = container_of(work, struct rx_work, work);
+ struct sk_buff *skb = rw->skb;
+
+ struct sk_buff *skb2 = skb_clone(skb, GFP_KERNEL);
+ netif_rx(skb2);
+
+ ieee802154_subif_rx(rw->dev, skb);
+ kfree(rw);
+}
+
+void ieee802154_rx_irqsafe(struct ieee802154_dev *dev,
+ struct sk_buff *skb, u8 lqi)
+{
+ struct ieee802154_priv *priv = ieee802154_to_priv(dev);
+ struct rx_work *work = kzalloc(sizeof(struct rx_work), GFP_ATOMIC);
+
+ if (!work)
+ return;
+
+ __ieee802154_rx_prepare(dev, skb, lqi);
+
+ INIT_WORK(&work->work, ieee802154_rx_worker);
+ work->skb = skb;
+ work->dev = dev;
+
+ queue_work(priv->dev_workqueue, &work->work);
+}
+EXPORT_SYMBOL(ieee802154_rx_irqsafe);
--
1.6.3.3
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 2/2] ieee802154: add virtual loopback driver
From: Dmitry Eremin-Solenikov @ 2009-08-10 14:16 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
linux-zigbee-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
In-Reply-To: <1249913800-10176-2-git-send-email-dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
fakelb is a virtual loopback driver implementing one or several
interconnected radios. Packets from the radio are either sent
back to the node (if no other fake radio are registered) or to
all other fake radio.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Sergey Lapin <slapin-9cOl001CZnBAfugRpC6u6w@public.gmane.org>
---
drivers/ieee802154/Kconfig | 13 ++
drivers/ieee802154/Makefile | 1 +
drivers/ieee802154/fakelb.c | 255 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 269 insertions(+), 0 deletions(-)
create mode 100644 drivers/ieee802154/fakelb.c
diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig
index 9b9f43a..b68b5c5 100644
--- a/drivers/ieee802154/Kconfig
+++ b/drivers/ieee802154/Kconfig
@@ -20,3 +20,16 @@ config IEEE802154_FAKEHARD
This driver can also be built as a module. To do so say M here.
The module will be called 'fakehard'.
+
+if IEEE802154_DRIVERS && MAC802154
+config IEEE802154_FAKELB
+ tristate "Fake LR-WPAN driver with several interconnected devices"
+ ---help---
+ Say Y here to enable the fake driver that can emulate a net
+ of several interconnected radio devices.
+
+ This driver can also be built as a module. To do so say M here.
+ The module will be called 'fakelb'.
+
+endif
+
diff --git a/drivers/ieee802154/Makefile b/drivers/ieee802154/Makefile
index e0e8e1a..2bd7bdf 100644
--- a/drivers/ieee802154/Makefile
+++ b/drivers/ieee802154/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_IEEE802154_FAKEHARD) += fakehard.o
+obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o
EXTRA_CFLAGS += -DDEBUG -DCONFIG_FFD
diff --git a/drivers/ieee802154/fakelb.c b/drivers/ieee802154/fakelb.c
new file mode 100644
index 0000000..9a135a9
--- /dev/null
+++ b/drivers/ieee802154/fakelb.c
@@ -0,0 +1,255 @@
+/*
+ * Loopback IEEE 802.15.4 interface
+ *
+ * Copyright 2007, 2008 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by:
+ * Sergey Lapin <slapin-9cOl001CZnBAfugRpC6u6w@public.gmane.org>
+ * Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+#include <net/mac802154.h>
+
+struct fake_dev_priv {
+ struct ieee802154_dev *dev;
+
+ struct list_head list;
+ struct fake_priv *fake;
+
+ unsigned int working:1;
+};
+
+static int radios = 3;
+module_param(radios, int, 0444);
+MODULE_PARM_DESC(radios, "Number of simulated radios");
+
+struct fake_priv {
+ struct list_head list;
+ rwlock_t lock;
+};
+
+static int
+hw_ed(struct ieee802154_dev *dev, u8 *level)
+{
+ pr_debug("%s\n", __func__);
+ might_sleep();
+ BUG_ON(!level);
+ *level = 0xbe;
+ return 0;
+}
+
+static int
+hw_channel(struct ieee802154_dev *dev, int channel)
+{
+ pr_debug("%s %d\n", __func__, channel);
+ might_sleep();
+ dev->current_channel = channel;
+ return 0;
+}
+
+static void
+hw_deliver(struct fake_dev_priv *priv, struct sk_buff *skb)
+{
+ struct sk_buff *newskb;
+
+ if (!priv->working)
+ return;
+
+ newskb = pskb_copy(skb, GFP_ATOMIC);
+
+ ieee802154_rx_irqsafe(priv->dev, newskb, 0xcc);
+}
+
+static int
+hw_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
+{
+ struct fake_dev_priv *priv = dev->priv;
+ struct fake_priv *fake = priv->fake;
+
+ might_sleep();
+
+ read_lock_bh(&fake->lock);
+ if (priv->list.next == priv->list.prev) {
+ /* we are the only one device */
+ hw_deliver(priv, skb);
+ } else {
+ struct fake_dev_priv *dp;
+ list_for_each_entry(dp, &priv->fake->list, list)
+ if (dp != priv &&
+ dp->dev->current_channel ==
+ priv->dev->current_channel)
+ hw_deliver(dp, skb);
+ }
+ read_unlock_bh(&fake->lock);
+
+ return 0;
+}
+
+static int
+hw_start(struct ieee802154_dev *dev) {
+ struct fake_dev_priv *priv = dev->priv;
+
+ if (priv->working)
+ return -EBUSY;
+
+ priv->working = 1;
+
+ return 0;
+}
+
+static void
+hw_stop(struct ieee802154_dev *dev) {
+ struct fake_dev_priv *priv = dev->priv;
+
+ priv->working = 0;
+}
+
+static struct ieee802154_ops fake_ops = {
+ .owner = THIS_MODULE,
+ .xmit = hw_xmit,
+ .ed = hw_ed,
+ .set_channel = hw_channel,
+ .start = hw_start,
+ .stop = hw_stop,
+};
+
+static int ieee802154fake_add_priv(struct device *dev, struct fake_priv *fake)
+{
+ struct fake_dev_priv *priv;
+ int err = -ENOMEM;
+ struct ieee802154_dev *ieee;
+
+ ieee = ieee802154_alloc_device(sizeof(*priv), &fake_ops);
+ if (!dev)
+ goto err_alloc_dev;
+
+ priv = ieee->priv;
+ priv->dev = ieee;
+
+ INIT_LIST_HEAD(&priv->list);
+ priv->fake = fake;
+
+ ieee->parent = dev;
+
+ err = ieee802154_register_device(ieee);
+ if (err)
+ goto err_reg;
+
+ write_lock_bh(&fake->lock);
+ list_add_tail(&priv->list, &fake->list);
+ write_unlock_bh(&fake->lock);
+
+ return 0;
+
+err_reg:
+ ieee802154_free_device(priv->dev);
+err_alloc_dev:
+ return err;
+}
+
+static void ieee802154fake_del_priv(struct fake_dev_priv *priv)
+{
+ write_lock_bh(&priv->fake->lock);
+ list_del(&priv->list);
+ write_unlock_bh(&priv->fake->lock);
+
+ ieee802154_unregister_device(priv->dev);
+ ieee802154_free_device(priv->dev);
+}
+
+static int __devinit ieee802154fake_probe(struct platform_device *pdev)
+{
+ struct fake_priv *priv;
+ struct fake_dev_priv *dp;
+ int err = -ENOMEM;
+ int i;
+
+ if (radios < 1 || radios > 100)
+ return -EINVAL;
+
+ priv = kzalloc(sizeof(struct fake_priv), GFP_KERNEL);
+ if (!priv)
+ goto err_alloc;
+
+ INIT_LIST_HEAD(&priv->list);
+ rwlock_init(&priv->lock);
+
+ for (i = 0; i < radios; i++) {
+ err = ieee802154fake_add_priv(&pdev->dev, priv);
+ if (err < 0)
+ goto err_slave;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ dev_info(&pdev->dev, "Added ieee802154 hardware\n");
+ return 0;
+
+err_slave:
+ list_for_each_entry(dp, &priv->list, list)
+ ieee802154fake_del_priv(dp);
+err_grp:
+ kfree(priv);
+err_alloc:
+ return err;
+}
+
+static int __devexit ieee802154fake_remove(struct platform_device *pdev)
+{
+ struct fake_priv *priv = platform_get_drvdata(pdev);
+ struct fake_dev_priv *dp, *temp;
+
+ list_for_each_entry_safe(dp, temp, &priv->list, list)
+ ieee802154fake_del_priv(dp);
+ kfree(priv);
+ return 0;
+}
+
+static struct platform_device *ieee802154fake_dev;
+
+static struct platform_driver ieee802154fake_driver = {
+ .probe = ieee802154fake_probe,
+ .remove = __devexit_p(ieee802154fake_remove),
+ .driver = {
+ .name = "ieee802154fakelb",
+ .owner = THIS_MODULE,
+ },
+};
+
+static __init int fake_init(void)
+{
+ ieee802154fake_dev = platform_device_register_simple(
+ "ieee802154fakelb", -1, NULL, 0);
+ return platform_driver_register(&ieee802154fake_driver);
+}
+
+static __exit void fake_exit(void)
+{
+ platform_driver_unregister(&ieee802154fake_driver);
+ platform_device_unregister(ieee802154fake_dev);
+}
+
+module_init(fake_init);
+module_exit(fake_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dmitry Eremin-Solenikov, Sergey Lapin");
+
+
--
1.6.3.3
------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now. http://p.sf.net/sfu/bobj-july
^ permalink raw reply related
* Attention
From: julia091883 @ 2009-08-10 14:14 UTC (permalink / raw)
Attention
We write to notify you of your winning, please contact your claim agent Mr,PATRICK to file in for your claim valued,Euro750,000,tick,713Eur312,Serial,516Uy71, Tel:+34-672-878-045 Contact address, patrickfoster09@aol.es, Regards,Maria.B
^ permalink raw reply
* Re: [PATCH 1/2] mac802154: add a software MAC 802.15.4 implementation
From: Johannes Berg @ 2009-08-10 14:27 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov
Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA, Sergey Lapin
In-Reply-To: <1249913800-10176-2-git-send-email-dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 2260 bytes --]
On Mon, 2009-08-10 at 18:16 +0400, Dmitry Eremin-Solenikov wrote:
> Some of available devices are just dump radios implementing IEEE 802.15.4
dump -> dumb
> Note this implementaion is neither certified, nor feature complete!
implementation
> + * @tx: Handler that 802.15.4 module calls for each transmitted frame.
> + * skb cntains the buffer starting from the IEEE 802.15.4 header.
> + * The low-level driver should send the frame based on available
> + * configuration.
> + * This function should return zero or negative errno.
That return value is strange.
> +++ b/net/Makefile
> @@ -61,6 +61,7 @@ ifneq ($(CONFIG_DCB),)
> obj-y += dcb/
> endif
> obj-y += ieee802154/
> +obj-y += mac802154/
I think you should use obj-$(CONFIG_MAC802154) as there's no need to
recurse into the dir unless that's selected.
But does it make sense to actually have this little code as a separate
dir/module?
> + Note: this implementation is neither certified, nor feature
> + complete! We do not garantee that it is compatible w/ other
> + implementations, etc.
guarantee
But generally, you don't have to write that anyway due to the license.
> + If you plan to use HardMAC IEEE 802.15.4 devices, you can
> + say N here. Alternatievly you can say M to compile it as
> + module.
white space gone wrong
> + struct net_device *netdev; /* mwpanX device */
> + int open_count;
> + /* As in mac80211 slaves list is modified:
> + * 1) under the RTNL
> + * 2) protected by slaves_mtx;
> + * 3) in an RCU manner
> + *
> + * So atomic readers can use any of this protection methods
> + */
> + struct list_head slaves;
heh.
> +static int ieee802154_master_open(struct net_device *dev)
> +{
We've had no end to trouble with the master netdev, I suggest you look
into the current mac80211 code and see if you can get rid of it.
> +struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size,
> + struct ieee802154_ops *ops)
> +{
> + if (!try_module_get(ops->owner)) {
That isn't necessary since the module is just calling your function. In
fact, doing this removes the ability to rmmod any module using this
which is bogus.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2] ieee802154: add virtual loopback driver
From: Johannes Berg @ 2009-08-10 14:29 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov
Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
linux-zigbee-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
linux-wireless-u79uwXL29TY76Z2rM5mHXA, Sergey Lapin
In-Reply-To: <1249913800-10176-3-git-send-email-dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 522 bytes --]
On Mon, 2009-08-10 at 18:16 +0400, Dmitry Eremin-Solenikov wrote:
> +if IEEE802154_DRIVERS && MAC802154
> +config IEEE802154_FAKELB
depends on
> + tristate "Fake LR-WPAN driver with several interconnected devices"
> + ---help---
> + Say Y here to enable the fake driver that can emulate a net
> + of several interconnected radio devices.
> +
> + This driver can also be built as a module. To do so say M here.
> + The module will be called 'fakelb'.
whitespace damage
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* Re: 2.6.31-rc5-git5: Reported regressions from 2.6.30
From: James Bottomley @ 2009-08-10 14:30 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Linux Kernel Mailing List, Adrian Bunk, Andrew Morton,
Linus Torvalds, Natalie Protasevich, Kernel Testers List,
Network Development, Linux ACPI, Linux PM List, Linux SCSI List,
Linux Wireless List, DRI
In-Reply-To: <AuPdDySr74O.A.5LC.Pj1fKB@chimera>
On Sun, 2009-08-09 at 22:36 +0200, Rafael J. Wysocki wrote:
> Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=13716
> Subject : The AIC-7892P controller does not work any more
> Submitter : Andrej Podzimek <andrej@podzimek.org>
> Date : 2009-07-05 19:23 (36 days old)
This one sounds like an IRQ routing error, so probably ACPI, but we've
been totally unsuccessful at getting any further information out of the
submitter. Use your own time judgement on this, but if he hasn't been
responding to you either for 36 days, I'd suggest closing this as
unresponsive.
James
^ permalink raw reply
* Re: [PATCH 5/6] [NET] ixp2000/enp2611: don't set non-existent member get_stats
From: David Miller @ 2009-08-10 15:00 UTC (permalink / raw)
To: u.kleine-koenig
Cc: tglx, linux-kernel, linux-rt-users, linux, dbaryshkov, kernel,
netdev
In-Reply-To: <20090810094027.GA22200@pengutronix.de>
From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Date: Mon, 10 Aug 2009 11:40:27 +0200
> Yes, I tried that, but stopped when I saw it's declared const. I don't
> know if this is required. If yes then the only way to fix it is to add
> a platform callback that is called if non-NULL.
The easiest way to handle this is to have another copy of the ops,
which has the other get_stats member value, and to assign that as
the netdev_ops when the relevant condition holds.
> I don't want to invest time here without a word by the maintainers
> though.
But we can't apply the patch you initially posted, that's for sure
:-)
--
To unsubscribe from this list: send the line "unsubscribe linux-rt-users" 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][RFC] net/bridge: add basic VEPA support
From: Arnd Bergmann @ 2009-08-10 15:06 UTC (permalink / raw)
To: Fischer, Anna
Cc: 'Stephen Hemminger', bridge@lists.linux-foundation.org,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
virtualization@lists.linux-foundation.org, evb@yahoogroups.com,
davem@davemloft.net, kaber@trash.net, adobriyan@gmail.com,
Paul Congdon (UC Davis), Eric Biederman
In-Reply-To: <0199E0D51A61344794750DC57738F58E6D6AE99743@GVW1118EXC.americas.hpqcorp.net>
On Monday 10 August 2009, Fischer, Anna wrote:
> > Subject: Re: [PATCH][RFC] net/bridge: add basic VEPA support
> >
> > On Friday 07 August 2009, Paul Congdon (UC Davis) wrote:
> > > As I understand the macvlan code, it currently doesn't allow two VMs
> > on the
> > > same machine to communicate with one another.
> >
> > There are patches to do that. I think if we add that, there should be
> > a way to choose the behavior between either bridging between the
> > guests or VEPA.
>
> If you implement this direct bridging capability between local VMs for
> macvlan, then would this not break existing applications that currently
> use it? It would be quite a significant change to how macvlan works
> today. I guess, ideally, you would want to have macvlan work in
> separate modes, e.g. traditional macvlan, bridging, and VEPA.
Right, that's what I meant with my sentence above. I'm not sure
if we need to differentiate traditional macvlan and VEPA though.
AFAICT, the only difference should be the handling of broadcast
and multicast frames returning from the hairpin turn. Since this
does not happen with a traditional macvlan, we can always send them
to all macvlan ports except the source port.
> > > I could imagine a hairpin mode on the adjacent bridge making this
> > > possible, but the macvlan code would need to be updated to filter
> > > reflected frames so a source did not receive his own packet.
> >
> > Right, I missed this point so far. I'll follow up with a patch
> > to do that.
>
> Can you maybe point me to the missing patches for macvlan that you
> have mentioned in other emails, and the one you mention above?
> E.g. enabling multicast distribution and allowing local bridging etc.
> I could not find any of those in the archives. Thanks.
The patch from Eric Biederman to allow macvlan to bridge between
its slave ports is at
http://kerneltrap.org/mailarchive/linux-netdev/2009/3/9/5125774
I could not find any patches for the other features (or bugs).
> > This is the interesting part of the discussion. The bridge and macvlan
> > drivers certainly have an overlap in functionality and you can argue
> > that you only need one. Then again, the bridge code is a little crufty
> > and we might not want to add much more to it for functionality that can
> > be implemented in a much simpler way elsewhere. My preferred way would
> > be to use bridge when you really need 802.1d MAC learning, netfilter-
> > bridge
> > and STP, while we put the optimizations for stuff like VMDq, zero-copy
> > and multiqueue guest adapters only into the macvlan code.
>
> I can see this being a possible solution.
>
> My concern with putting VEPA into macvlan instead of the bridging code
> is that there will be more work required to make it usable for other
> virtualization solution as macvtap will only work for KVM type setups.
Right, I understand.
> Basically, VEPA capabilities would rely on someone developing further
> drivers to connect macvlan to different backend interfaces, e.g. one for
> KVM (macvtap), one for Xen PV drivers, one for virtio, and whatever else
> is out there, or will be there in the future. The bridging code is
> already very generic in that respect, and all virtualization layers
> can deal with connecting interfaces to a bridge.
>
> Our extensions to the bridging code to enable VEPA for the Linux kernel
> are only very minimal code changes and would allow to make VEPA available
> to most virtualization solutions today.
I don't object to having VEPA supported in the bridge code at all.
I think your patch is simple enough so it won't hurt in the bridge
code. If Stephen prefers to do VEPA only in one component, we should
probably make it possible for that component to act as a bridge between
1+n existing interfaces as well. You can almost do that with the regular
macvlan and the bridge driver, like
/ macvlan0 - br0 - tap0
eth0 -- macvlan1 - br1 - tap1
\ macvlan2 - br2 - tap2
Here, you can have two guests attached to tap devices (or xen net ...)
and the macvlan driver doing the VEPA. Of course this is not how bridge
works -- you would have the same mac addresses on two sides of
the bridge.
So we could have another macvlan backend (let's call it macvbridge)
so you can do this:
/ macvlan0 - 'qemu -net raw'
eth0 -- macvtap0 - 'qemu -net tap,fd=3 3<>/dev/net/macvtap0'
\ macvbr0 -- tap0 - 'qemu -net tap'
The macvbr driver could this way be used to associate an existing
network device to a slave of a macvlan port. Not sure if this
has any significant advantage over your bridge patches, it does
have the obvious disadvantage that someone needs to implement
it first, while your patch is there ;-)
Arnd <><
^ permalink raw reply
* Re: [PATCH][RFC] net/bridge: add basic VEPA support
From: Arnd Bergmann @ 2009-08-10 15:07 UTC (permalink / raw)
To: Fischer, Anna
Cc: 'Stephen Hemminger', bridge@lists.linux-foundation.org,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
virtualization@lists.linux-foundation.org, evb@yahoogroups.com,
davem@davemloft.net, kaber@trash.net, adobriyan@gmail.com,
Paul Congdon (UC Davis), Eric Biederman
In-Reply-To: <0199E0D51A61344794750DC57738F58E6D6AE99743@GVW1118EXC.americas.hpqcorp.net>
On Monday 10 August 2009, Fischer, Anna wrote:
> > Subject: Re: [PATCH][RFC] net/bridge: add basic VEPA support
> >
> > On Friday 07 August 2009, Paul Congdon (UC Davis) wrote:
> > > As I understand the macvlan code, it currently doesn't allow two VMs
> > on the
> > > same machine to communicate with one another.
> >
> > There are patches to do that. I think if we add that, there should be
> > a way to choose the behavior between either bridging between the
> > guests or VEPA.
>
> If you implement this direct bridging capability between local VMs for
> macvlan, then would this not break existing applications that currently
> use it? It would be quite a significant change to how macvlan works
> today. I guess, ideally, you would want to have macvlan work in
> separate modes, e.g. traditional macvlan, bridging, and VEPA.
Right, that's what I meant with my sentence above. I'm not sure
if we need to differentiate traditional macvlan and VEPA though.
AFAICT, the only difference should be the handling of broadcast
and multicast frames returning from the hairpin turn. Since this
does not happen with a traditional macvlan, we can always send them
to all macvlan ports except the source port.
> > > I could imagine a hairpin mode on the adjacent bridge making this
> > > possible, but the macvlan code would need to be updated to filter
> > > reflected frames so a source did not receive his own packet.
> >
> > Right, I missed this point so far. I'll follow up with a patch
> > to do that.
>
> Can you maybe point me to the missing patches for macvlan that you
> have mentioned in other emails, and the one you mention above?
> E.g. enabling multicast distribution and allowing local bridging etc.
> I could not find any of those in the archives. Thanks.
The patch from Eric Biederman to allow macvlan to bridge between
its slave ports is at
http://kerneltrap.org/mailarchive/linux-netdev/2009/3/9/5125774
I could not find any patches for the other features (or bugs).
> > This is the interesting part of the discussion. The bridge and macvlan
> > drivers certainly have an overlap in functionality and you can argue
> > that you only need one. Then again, the bridge code is a little crufty
> > and we might not want to add much more to it for functionality that can
> > be implemented in a much simpler way elsewhere. My preferred way would
> > be to use bridge when you really need 802.1d MAC learning, netfilter-
> > bridge
> > and STP, while we put the optimizations for stuff like VMDq, zero-copy
> > and multiqueue guest adapters only into the macvlan code.
>
> I can see this being a possible solution.
>
> My concern with putting VEPA into macvlan instead of the bridging code
> is that there will be more work required to make it usable for other
> virtualization solution as macvtap will only work for KVM type setups.
Right, I understand.
> Basically, VEPA capabilities would rely on someone developing further
> drivers to connect macvlan to different backend interfaces, e.g. one for
> KVM (macvtap), one for Xen PV drivers, one for virtio, and whatever else
> is out there, or will be there in the future. The bridging code is
> already very generic in that respect, and all virtualization layers
> can deal with connecting interfaces to a bridge.
>
> Our extensions to the bridging code to enable VEPA for the Linux kernel
> are only very minimal code changes and would allow to make VEPA available
> to most virtualization solutions today.
I don't object to having VEPA supported in the bridge code at all.
I think your patch is simple enough so it won't hurt in the bridge
code. If Stephen prefers to do VEPA only in one component, we should
probably make it possible for that component to act as a bridge between
1+n existing interfaces as well. You can almost do that with the regular
macvlan and the bridge driver, like
/ macvlan0 - br0 - tap0
eth0 -- macvlan1 - br1 - tap1
\ macvlan2 - br2 - tap2
Here, you can have two guests attached to tap devices (or xen net ...)
and the macvlan driver doing the VEPA. Of course this is not how bridge
works -- you would have the same mac addresses on two sides of
the bridge.
So we could have another macvlan backend (let's call it macvbridge)
so you can do this:
/ macvlan0 - 'qemu -net raw'
eth0 -- macvtap0 - 'qemu -net tap,fd=3 3<>/dev/net/macvtap0'
\ macvbr0 -- tap0 - 'qemu -net tap'
The macvbr driver could this way be used to associate an existing
network device to a slave of a macvlan port. Not sure if this
has any significant advantage over your bridge patches, it does
have the obvious disadvantage that someone needs to implement
it first, while your patch is there ;-)
Arnd <><
^ permalink raw reply
* Re: [evb] RE: [PATCH][RFC] net/bridge: add basic VEPA support
From: Stephen Hemminger @ 2009-08-10 15:20 UTC (permalink / raw)
To: Or Gerlitz
Cc: Paul Congdon (UC Davis), arnd, anna.fischer, netdev, bridge,
davem, adobriyan, virtualization, evb@yahoogroups.com
In-Reply-To: <4A7EB0AC.2030104@Voltaire.com>
On Sun, 09 Aug 2009 14:19:08 +0300
Or Gerlitz <ogerlitz@voltaire.com> wrote:
> Stephen Hemminger wrote:
> > I have a patch that forwards all multicast packets, and another that does proper forwarding. It should have worked that way in original macvlan, the current behavior is really a bug.
> >
> Looking in macvlan_set_multicast_list() it acts in a similar manner to
> macvlan_set_mac_address() in the sense that it calls dev_mc_sync(). I
> assume what's left is to add macvlan_hash_xxx multicast logic to
> map/unmap multicast groups to what macvlan devices want to receive them
> and this way the flooding can be removed, correct?
The device can just flood all multicast packets, since the filtering
is done on the receive path anyway.
^ permalink raw reply
* Re: [evb] Re: [PATCH][RFC] net/bridge: add basic VEPA support
From: Arnd Bergmann @ 2009-08-10 15:23 UTC (permalink / raw)
To: evb
Cc: 'Stephen Hemminger', 'Fischer, Anna', bridge,
linux-kernel, netdev, virtualization, davem, kaber, adobriyan,
'Or Gerlitz'
In-Reply-To: <005101ca1797$70dcf5b0$5296e110$@edu>
On Friday 07 August 2009, Paul Congdon (UC Davis) wrote:
>
> I don't think your scheme works too well because broadcast packet coming
> from other interfaces on br0 would get replicated and sent across the wire
> to ethB multiple times.
Right, that won't work. So the bridge patch for the hairpin turn
is still the best solution. Btw, how will that interact with
the bride-netfilter (ebtables) setup? Can you apply any filters
that work on current bridges also between two VEPA ports while
doing the hairpin turn?
Arnd <><
^ permalink raw reply
* Re: [PATCH] sky2: Serialize access to PCI config space
From: Stephen Hemminger @ 2009-08-10 15:23 UTC (permalink / raw)
To: Mike McCormack; +Cc: netdev
In-Reply-To: <4A7EB4EF.3030106@ring3k.org>
On Sun, 09 Aug 2009 20:37:19 +0900
Mike McCormack <mikem@ring3k.org> wrote:
>
> Disable NAPI when powering up or down the phy, as sky2_err_intr
> may also access the PCI config space.
Maybe, but any sky2_err interrupt is a serious condition that
means hardware is being used incorrectly or dead.
^ permalink raw reply
* Re: [PATCH] sky2: Serialize access to PCI config space
From: Stephen Hemminger @ 2009-08-10 15:27 UTC (permalink / raw)
To: Mike McCormack; +Cc: netdev
In-Reply-To: <4A7EB4EF.3030106@ring3k.org>
On Sun, 09 Aug 2009 20:37:19 +0900
Mike McCormack <mikem@ring3k.org> wrote:
>
> Disable NAPI when powering up or down the phy, as sky2_err_intr
> may also access the PCI config space.
>
Also access to PCI config is done through shared memory, so each
access is atomic. The only issue is the stupid config write bit;
this bit really doesn't protect from anything useful.
Probably better to just always leave it TST_CFG_WRITE_ON.
^ permalink raw reply
* Re: [evb] RE: [PATCH][RFC] net/bridge: add basic VEPA support
From: Arnd Bergmann @ 2009-08-10 15:28 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Or Gerlitz, Paul Congdon (UC Davis), anna.fischer, netdev, bridge,
davem, adobriyan, virtualization, evb@yahoogroups.com
In-Reply-To: <20090810082037.17b352c2@nehalam>
On Monday 10 August 2009, Stephen Hemminger wrote:
> On Sun, 09 Aug 2009 14:19:08 +0300, Or Gerlitz <ogerlitz@voltaire.com> wrote:
> > Looking in macvlan_set_multicast_list() it acts in a similar manner to
> > macvlan_set_mac_address() in the sense that it calls dev_mc_sync(). I
> > assume what's left is to add macvlan_hash_xxx multicast logic to
> > map/unmap multicast groups to what macvlan devices want to receive them
> > and this way the flooding can be removed, correct?
>
> The device can just flood all multicast packets, since the filtering
> is done on the receive path anyway.
But we'd still have to copy the frames to user space (for both
macvtap and raw packet sockets) and exit from the guest to inject
it into its stack, right?
I guess for multicast heavy workloads, we could save a lot of cycles
by throwing the frames away as early as possible. How common are those
setups in virtual servers though?
Arnd <><
^ permalink raw reply
* Re: [PATCH] sky2: Create buffer alloc and free helpers
From: Stephen Hemminger @ 2009-08-10 15:29 UTC (permalink / raw)
To: Mike McCormack; +Cc: netdev
In-Reply-To: <4A7EB5A0.3040401@ring3k.org>
On Sun, 09 Aug 2009 20:40:16 +0900
Mike McCormack <mikem@ring3k.org> wrote:
> Refactor similar two sections of code that free buffers into one.
> As a side effect, only call tx_init() in sky2_up() if all buffer
> allocations succeed.
>
> Signed-off-by: Mike McCormack <mikem@ring3k.org>
> + if (0 > sky2_alloc_buffers(sky2))
> goto err_out;
Don't change code style. Especially in mid driver.
Just return 0 for success and errno or -1.
^ permalink raw reply
* [PATCH 3/5] Add common socket helpers to unify the security hooks
From: Dan Smith @ 2009-08-10 15:32 UTC (permalink / raw)
To: containers; +Cc: orenl, netdev
In-Reply-To: <1249918379-29414-1-git-send-email-danms@us.ibm.com>
This moves the meat out of the bind(), getsockname(), and getpeername() syscalls
into helper functions that performs security_socket_bind() and then the
sock->ops->call(). This allows a unification of this behavior between the
syscalls and the pending socket restart logic.
Acked-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Dan Smith <danms@us.ibm.com>
Cc: netdev@vger.kernel.org
---
include/net/sock.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
net/socket.c | 29 ++++++-----------------------
2 files changed, 54 insertions(+), 23 deletions(-)
diff --git a/include/net/sock.h b/include/net/sock.h
index 2c0da92..43b9599 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1572,6 +1572,54 @@ extern void sock_enable_timestamp(struct sock *sk, int flag);
extern int sock_get_timestamp(struct sock *, struct timeval __user *);
extern int sock_get_timestampns(struct sock *, struct timespec __user *);
+/* bind() helper shared between any callers needing to perform a bind on
+ * behalf of userspace (syscall and restart) with the security hooks.
+ */
+static inline int sock_bind(struct socket *sock,
+ struct sockaddr *addr,
+ int addr_len)
+{
+ int err;
+
+ err = security_socket_bind(sock, addr, addr_len);
+ if (err)
+ return err;
+ else
+ return sock->ops->bind(sock, addr, addr_len);
+}
+
+/* getname() helper shared between any callers needing to perform a getname on
+ * behalf of userspace (syscall and restart) with the security hooks.
+ */
+static inline int sock_getname(struct socket *sock,
+ struct sockaddr *addr,
+ int *addr_len)
+{
+ int err;
+
+ err = security_socket_getsockname(sock);
+ if (err)
+ return err;
+ else
+ return sock->ops->getname(sock, addr, addr_len, 0);
+}
+
+/* getpeer() helper shared between any callers needing to perform a getpeer on
+ * behalf of userspace (syscall and restart) with the security hooks.
+ */
+static inline int sock_getpeer(struct socket *sock,
+ struct sockaddr *addr,
+ int *addr_len)
+{
+ int err;
+
+ err = security_socket_getpeername(sock);
+ if (err)
+ return err;
+ else
+ return sock->ops->getname(sock, addr, addr_len, 1);
+}
+
/*
* Enable debug/info messages
*/
diff --git a/net/socket.c b/net/socket.c
index 791d71a..65e7698 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1414,15 +1414,10 @@ SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address);
- if (err >= 0) {
- err = security_socket_bind(sock,
- (struct sockaddr *)&address,
- addrlen);
- if (!err)
- err = sock->ops->bind(sock,
- (struct sockaddr *)
- &address, addrlen);
- }
+ if (err >= 0)
+ err = sock_bind(sock,
+ (struct sockaddr *)&address,
+ addrlen);
fput_light(sock->file, fput_needed);
}
return err;
@@ -1610,11 +1605,7 @@ SYSCALL_DEFINE3(getsockname, int, fd, struct sockaddr __user *, usockaddr,
if (!sock)
goto out;
- err = security_socket_getsockname(sock);
- if (err)
- goto out_put;
-
- err = sock->ops->getname(sock, (struct sockaddr *)&address, &len, 0);
+ err = sock_getname(sock, (struct sockaddr *)&address, &len);
if (err)
goto out_put;
err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr, usockaddr_len);
@@ -1639,15 +1630,7 @@ SYSCALL_DEFINE3(getpeername, int, fd, struct sockaddr __user *, usockaddr,
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock != NULL) {
- err = security_socket_getpeername(sock);
- if (err) {
- fput_light(sock->file, fput_needed);
- return err;
- }
-
- err =
- sock->ops->getname(sock, (struct sockaddr *)&address, &len,
- 1);
+ err = sock_getpeer(sock, (struct sockaddr *)&address, &len);
if (!err)
err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr,
usockaddr_len);
--
1.6.2.5
^ permalink raw reply related
* [PATCH 5/5] c/r: Add AF_UNIX support (v8)
From: Dan Smith @ 2009-08-10 15:32 UTC (permalink / raw)
To: containers; +Cc: orenl, Oren Laaden, Alexey Dobriyan, netdev
In-Reply-To: <1249918379-29414-1-git-send-email-danms@us.ibm.com>
This patch adds basic checkpoint/restart support for AF_UNIX sockets. It
has been tested with a single and multiple processes, and with data inflight
at the time of checkpoint. It supports socketpair()s, path-based, and
abstract sockets.
Changes in v8:
- Fix stale dev_alloc_skb() from before the conversion to skb_clone()
- Fix a couple of broken error paths
- Fix memory leak of kvec.iov_base on successful return from sendmsg()
- Fix condition for deciding when to run sock_cptrst_verify()
- Fix buffer queue copy algorithm to hold the lock during walk(s)
- Log the errno when either getname() or getpeer() fails
- Add comments about ancillary messages in the UNIX queue
- Add TODO comments for credential restore and flags via setsockopt()
- Add TODO comment about strangely-connected dgram sockets and the use
of sendmsg(peer)
Changes in v7:
- Fix failure to free iov_base in error path of sock_read_buffer()
- Change sock_read_buffer() to use _ckpt_read_obj_type() to get the
header length and then use ckpt_kread() directly to read the payload
- Change sock_read_buffers() to sock_unix_read_buffers() and break out
some common functionality to better accommodate the subsequent INET
patch
- Generalize sock_unix_getnames() into sock_getnames() so INET can use it
- Change skb_morph() to skb_clone() which uses the more common path and
still avoids the copy
- Add check to validate the socket type before creating socket
on restore
- Comment the CAP_NET_ADMIN override in sock_read_buffer_hdr
- Strengthen the comment about priming the buffer limits
- Change the objhash functions to deny direct checkpoint of sockets and
remove the reference counting function
- Change SOCKET_BUFFERS to SOCKET_QUEUE
- Change this,peer objrefs to signed integers
- Remove names from internal socket structures
- Fix handling of sock_copy_buffers() result
- Use ckpt_fill_fname() instead of d_path() for writing CWD
- Use sock_getname() and sock_getpeer() for proper security hookage
- Return -ENOSYS for unsupported socket families in checkpoint and restart
- Use sock_setsockopt() and sock_getsockopt() where possible to save and
restore socket option values
- Check for SOCK_DESTROY flag in the global verify function because none
of our supported socket types use it
- Check for SOCK_USE_WRITE_QUEUE in AF_UNIX restore function because
that flag should not be used on such a socket
- Check socket state in UNIX restart path to validate the subset of valid
values
Changes in v6:
- Moved the socket addresses to the per-type header
- Eliminated the HASCWD flag
- Remove use of ckpt_write_err() in restart paths
- Change the order in which buffers are read so that we can set the
socket's limit equal to the size of the image's buffers (if appropriate)
and then restore the original values afterwards.
- Use the ckpt_validate_errno() helper
- Add a check to make sure that we didn't restore a (UNIX) socket with
any skb's in the send buffer
- Fix up sock_unix_join() to not leave addr uninitialized for socketpair
- Remove inclusion of checkpoint_hdr.h in the socket files
- Make sock_unix_write_cwd() use ckpt_write_string() and use the new
ckpt_read_string() for reading the cwd
- Use the restored realcred credentials in sock_unix_join()
- Fix error path of the chdir_and_bind
- Change the algorithm for reloading the socket buffers to use sendmsg()
on the socket's peer for better accounting
- For DGRAM sockets, check the backlog value against the system max
to avoid letting a restart bypass the overloaded queue length
- Use sock_bind() instead of sock->ops->bind() to gain the security hook
- Change "restart" to "restore" in some of the function names
Changes in v5:
- Change laddr and raddr buffers in socket header to be long enough
for INET6 addresses
- Place socket.c and sock.h function definitions inside #ifdef
CONFIG_CHECKPOINT
- Add explicit check in sock_unix_makeaddr() to refuse if the
checkpoint image specifies an addr length of 0
- Split sock_unix_restart() into a few pieces to facilitate:
- Changed behavior of the unix restore code so that unlinked LISTEN
sockets don't do a bind()...unlink()
- Save the base path of a bound socket's path so that we can chdir()
to the base before bind() if it is a relative path
- Call bind() for any socket that is not established but has a
non-zero-length local address
- Enforce the current sysctl limit on socket buffer size during restart
unless the user holds CAP_NET_ADMIN
- Unlink a path-based socket before calling bind()
Changes in v4:
- Changed the signdness of rcvlowat, rcvtimeo, sndtimeo, and backlog
to match their struct sock definitions. This should avoid issues
with sign extension.
- Add a sock_cptrst_verify() function to be run at restore time to
validate several of the values in the checkpoint image against
limits, flag masks, etc.
- Write an error string with ctk_write_err() in the obscure cases
- Don't write socket buffers for listen sockets
- Sanity check address lengths before we agree to allocate memory
- Check the result of inserting the peer object in the objhash on
restart
- Check return value of sock_cptrst() on restart
- Change logic in remote getname() phase of checkpoint to not fail for
closed (et al) sockets
- Eliminate the memory copy while reading socket buffers on restart
Changes in v3:
- Move sock_file_checkpoint() above sock_file_restore()
- Change __sock_file_*() functions to do_sock_file_*()
- Adjust some of the struct cr_hdr_socket alignment
- Improve the sock_copy_buffers() algorithm to avoid locking the source
queue for the entire operation
- Fix alignment in the socket header struct(s)
- Move the per-protocol structure (ckpt_hdr_socket_un) out of the
common socket header and read/write it separately
- Fix missing call to sock_cptrst() in restore path
- Break out the socket joining into another function
- Fix failure to restore the socket address thus fixing getname()
- Check the state values on restart
- Fix case of state being TCP_CLOSE, which allows dgram sockets to be
properly connected (if appropriate) to their peer and maintain the
sockaddr for getname() operation
- Fix restoring a listening socket that has been unlink()'d
- Fix checkpointing sockets with an in-flight FD-passing SKB. Fail
with EBUSY.
- Fix checkpointing listening sockets with an unaccepted connection.
Fail with EBUSY.
- Changed 'un' to 'unix' in function and structure names
Changes in v2:
- Change GFP_KERNEL to GFP_ATOMIC in sock_copy_buffers() (this seems
to be rather common in other uses of skb_copy())
- Move the ckpt_hdr_socket structure definition to linux/socket.h
- Fix whitespace issue
- Move sock_file_checkpoint() to net/socket.c for symmetry
Cc: Oren Laaden <orenl@cs.columbia.edu>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: netdev@vger.kernel.org
Signed-off-by: Dan Smith <danms@us.ibm.com>
---
checkpoint/files.c | 7 +
checkpoint/objhash.c | 19 +
include/linux/checkpoint_hdr.h | 12 +
include/linux/socket.h | 65 +++
include/net/sock.h | 11 +
net/Makefile | 2 +
net/checkpoint.c | 951 ++++++++++++++++++++++++++++++++++++++++
net/socket.c | 85 ++++
8 files changed, 1152 insertions(+), 0 deletions(-)
create mode 100644 net/checkpoint.c
diff --git a/checkpoint/files.c b/checkpoint/files.c
index 7aecac9..8729e00 100644
--- a/checkpoint/files.c
+++ b/checkpoint/files.c
@@ -21,6 +21,7 @@
#include <linux/syscalls.h>
#include <linux/checkpoint.h>
#include <linux/checkpoint_hdr.h>
+#include <net/sock.h>
/**************************************************************************
@@ -565,6 +566,12 @@ static struct restore_file_ops restore_file_ops[] = {
.file_type = CKPT_FILE_FIFO,
.restore = fifo_file_restore,
},
+ /* socket */
+ {
+ .file_name = "SOCKET",
+ .file_type = CKPT_FILE_SOCKET,
+ .restore = sock_file_restore,
+ },
};
static struct file *do_restore_file(struct ckpt_ctx *ctx)
diff --git a/checkpoint/objhash.c b/checkpoint/objhash.c
index 3452e83..e40b03e 100644
--- a/checkpoint/objhash.c
+++ b/checkpoint/objhash.c
@@ -20,6 +20,7 @@
#include <linux/user_namespace.h>
#include <linux/checkpoint.h>
#include <linux/checkpoint_hdr.h>
+#include <net/sock.h>
struct ckpt_obj;
struct ckpt_obj_ops;
@@ -234,6 +235,17 @@ static void obj_groupinfo_drop(void *ptr)
put_group_info((struct group_info *) ptr);
}
+static int obj_sock_grab(void *ptr)
+{
+ sock_hold((struct sock *) ptr);
+ return 0;
+}
+
+static void obj_sock_drop(void *ptr)
+{
+ sock_put((struct sock *) ptr);
+}
+
static struct ckpt_obj_ops ckpt_obj_ops[] = {
/* ignored object */
{
@@ -356,6 +368,13 @@ static struct ckpt_obj_ops ckpt_obj_ops[] = {
.checkpoint = checkpoint_groupinfo,
.restore = restore_groupinfo,
},
+ /* sock object */
+ {
+ .obj_name = "SOCKET",
+ .obj_type = CKPT_OBJ_SOCK,
+ .ref_drop = obj_sock_drop,
+ .ref_grab = obj_sock_grab,
+ },
};
diff --git a/include/linux/checkpoint_hdr.h b/include/linux/checkpoint_hdr.h
index 4b85956..d8816ed 100644
--- a/include/linux/checkpoint_hdr.h
+++ b/include/linux/checkpoint_hdr.h
@@ -88,6 +88,12 @@ enum {
CKPT_HDR_SIGHAND = 601,
+ CKPT_HDR_FD_SOCKET = 701,
+ CKPT_HDR_SOCKET,
+ CKPT_HDR_SOCKET_QUEUE,
+ CKPT_HDR_SOCKET_BUFFER,
+ CKPT_HDR_SOCKET_UNIX,
+
CKPT_HDR_TAIL = 9001,
CKPT_HDR_ERROR = 9999,
@@ -122,6 +128,7 @@ enum obj_type {
CKPT_OBJ_CRED,
CKPT_OBJ_USER,
CKPT_OBJ_GROUPINFO,
+ CKPT_OBJ_SOCK,
CKPT_OBJ_MAX
};
@@ -327,6 +334,7 @@ enum file_type {
CKPT_FILE_GENERIC,
CKPT_FILE_PIPE,
CKPT_FILE_FIFO,
+ CKPT_FILE_SOCKET,
CKPT_FILE_MAX
};
@@ -350,6 +358,10 @@ struct ckpt_hdr_file_pipe {
__s32 pipe_objref;
} __attribute__((aligned(8)));
+struct ckpt_hdr_file_socket {
+ struct ckpt_hdr_file common;
+} __attribute__((aligned(8)));
+
struct ckpt_hdr_utsns {
struct ckpt_hdr h;
char sysname[__NEW_UTS_LEN + 1];
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 3b461df..7d5fd48 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -23,6 +23,7 @@ struct __kernel_sockaddr_storage {
#include <linux/uio.h> /* iovec support */
#include <linux/types.h> /* pid_t */
#include <linux/compiler.h> /* __user */
+#include <linux/checkpoint.h> /* struct ckpt_hdr */
#ifdef __KERNEL__
# ifdef CONFIG_PROC_FS
@@ -328,5 +329,69 @@ extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *ka
extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
#endif
+
+#ifdef CONFIG_CHECKPOINT
+#include <linux/un.h> /* sockaddr_un */
+
+#define CKPT_UNIX_LINKED 1
+struct ckpt_hdr_socket_unix {
+ struct ckpt_hdr h;
+ __s32 this;
+ __s32 peer;
+ __u32 flags;
+ __u32 laddr_len;
+ __u32 raddr_len;
+ struct sockaddr_un laddr;
+ struct sockaddr_un raddr;
+} __attribute__ ((aligned(8)));
+
+struct ckpt_hdr_socket {
+ struct ckpt_hdr h;
+
+ struct { /* struct socket */
+ __u64 flags;
+ __u8 state;
+ } socket __attribute__ ((aligned(8)));
+
+ struct { /* struct sock_common */
+ __u32 bound_dev_if;
+ __u32 reuse;
+ __u16 family;
+ __u8 state;
+ } sock_common __attribute__ ((aligned(8)));
+
+ struct { /* struct sock */
+ __s64 rcvlowat;
+ __u64 flags;
+
+ __u32 err;
+ __u32 err_soft;
+ __u32 priority;
+ __s32 rcvbuf;
+ __s32 sndbuf;
+ __u16 type;
+ __s16 backlog;
+
+ __u8 protocol;
+ __u8 state;
+ __u8 shutdown;
+ __u8 userlocks;
+ __u8 no_check;
+
+ struct linger linger;
+ struct timeval rcvtimeo;
+ struct timeval sndtimeo;
+
+ } sock __attribute__ ((aligned(8)));
+
+} __attribute__ ((aligned(8)));
+
+struct ckpt_hdr_socket_queue {
+ struct ckpt_hdr h;
+ __u32 skb_count;
+ __u32 total_bytes;
+} __attribute__ ((aligned(8)));
+#endif /* CONFIG_CHECKPOINT */
+
#endif /* not kernel and not glibc */
#endif /* _LINUX_SOCKET_H */
diff --git a/include/net/sock.h b/include/net/sock.h
index 43b9599..da75f2f 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1640,4 +1640,15 @@ extern int sysctl_optmem_max;
extern __u32 sysctl_wmem_default;
extern __u32 sysctl_rmem_default;
+#ifdef CONFIG_CHECKPOINT
+/* Checkpoint/Restart Functions */
+struct ckpt_ctx;
+struct ckpt_hdr_socket;
+extern int sock_file_checkpoint(struct ckpt_ctx *, void *);
+extern void *sock_file_restore(struct ckpt_ctx *);
+extern struct socket *do_sock_file_restore(struct ckpt_ctx *,
+ struct ckpt_hdr_socket *);
+extern int do_sock_file_checkpoint(struct ckpt_ctx *ctx, struct file *file);
+#endif
+
#endif /* _SOCK_H */
diff --git a/net/Makefile b/net/Makefile
index ba324ae..91d12fe 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -66,3 +66,5 @@ ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_SYSCTL) += sysctl_net.o
endif
obj-$(CONFIG_WIMAX) += wimax/
+
+obj-$(CONFIG_CHECKPOINT) += checkpoint.o
diff --git a/net/checkpoint.c b/net/checkpoint.c
new file mode 100644
index 0000000..078c2e6
--- /dev/null
+++ b/net/checkpoint.c
@@ -0,0 +1,951 @@
+/*
+ * Copyright 2009 IBM Corporation
+ *
+ * Author: Dan Smith <danms@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#include <linux/socket.h>
+#include <linux/mount.h>
+#include <linux/file.h>
+#include <linux/namei.h>
+#include <linux/syscalls.h>
+#include <linux/sched.h>
+#include <linux/fs_struct.h>
+
+#include <net/af_unix.h>
+#include <net/tcp_states.h>
+
+#include <linux/checkpoint.h>
+#include <linux/checkpoint_hdr.h>
+
+#define UNIX_ADDR_EMPTY(a) (a <= sizeof(short))
+
+static inline int sock_unix_need_cwd(struct sockaddr_un *addr,
+ unsigned long len)
+{
+ return (!UNIX_ADDR_EMPTY(len)) &&
+ addr->sun_path[0] &&
+ (addr->sun_path[0] != '/');
+}
+
+static int sock_copy_buffers(struct sk_buff_head *from,
+ struct sk_buff_head *to,
+ uint32_t *total_bytes)
+{
+ int count1 = 0;
+ int count2 = 0;
+ int i;
+ struct sk_buff *skb;
+ struct sk_buff **skbs;
+
+ *total_bytes = 0;
+
+ spin_lock(&from->lock);
+ skb_queue_walk(from, skb)
+ count1++;
+ spin_unlock(&from->lock);
+
+ skbs = kmalloc(sizeof(*skbs) * count1, GFP_KERNEL);
+ if (!skbs)
+ return -ENOMEM;
+
+ for (i = 0; i < count1; i++) {
+ skbs[i] = dev_alloc_skb(0);
+ if (!skbs[i])
+ goto err;
+ }
+
+ i = 0;
+ spin_lock(&from->lock);
+ skb_queue_walk(from, skb) {
+ if (++count2 > count1)
+ break; /* The queue changed as we read it */
+
+ skb_morph(skbs[i], skb);
+ skb_queue_tail(to, skbs[i]);
+
+ *total_bytes += skb->len;
+ i++;
+ }
+ spin_unlock(&from->lock);
+
+ if (count1 != count2)
+ goto err;
+
+ kfree(skbs);
+
+ return count1;
+ err:
+ for (i = 0; i < count1; i++)
+ kfree_skb(skbs[i]);
+ kfree(skbs);
+
+ return -EAGAIN;
+}
+
+static int __sock_write_buffers(struct ckpt_ctx *ctx,
+ struct sk_buff_head *queue)
+{
+ struct sk_buff *skb;
+ int ret = 0;
+
+ skb_queue_walk(queue, skb) {
+ /* FIXME: This could be a false positive for non-unix
+ * buffers, so add a type check here in the
+ * future
+ */
+ if (UNIXCB(skb).fp) {
+ ckpt_write_err(ctx, "fd-passing is not supported");
+ return -EBUSY;
+ }
+
+ /* The other ancillary messages are always present
+ * unlike descriptors. Even though we can't detect
+ * them and fail the checkpoint, we're not at risk
+ * because we don't save out (or restore) the control
+ * information contained in the skb.
+ */
+
+ ret = ckpt_write_obj_type(ctx, skb->data, skb->len,
+ CKPT_HDR_SOCKET_BUFFER);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sock_write_buffers(struct ckpt_ctx *ctx, struct sk_buff_head *queue)
+{
+ struct ckpt_hdr_socket_queue *h;
+ struct sk_buff_head tmpq;
+ int ret = -ENOMEM;
+
+ h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_SOCKET_QUEUE);
+ if (!h)
+ return -ENOMEM;
+
+ skb_queue_head_init(&tmpq);
+
+ ret = sock_copy_buffers(queue, &tmpq, &h->total_bytes);
+ if (ret < 0)
+ goto out;
+
+ h->skb_count = ret;
+ ret = ckpt_write_obj(ctx, (struct ckpt_hdr *) h);
+ if (!ret)
+ ret = __sock_write_buffers(ctx, &tmpq);
+
+ out:
+ ckpt_hdr_put(ctx, h);
+ __skb_queue_purge(&tmpq);
+
+ return ret;
+}
+
+static int sock_unix_write_cwd(struct ckpt_ctx *ctx,
+ struct sock *sock,
+ const char *sockpath)
+{
+ struct path path;
+ char *buf;
+ char *fqpath;
+ int offset;
+ int len = PATH_MAX;
+ int ret = -ENOENT;
+
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ path.dentry = unix_sk(sock)->dentry;
+ path.mnt = unix_sk(sock)->mnt;
+
+ fqpath = ckpt_fill_fname(&path, &ctx->fs_mnt, buf, &len);
+ if (IS_ERR(fqpath)) {
+ ret = PTR_ERR(fqpath);
+ goto out;
+ }
+
+ offset = strlen(fqpath) - strlen(sockpath);
+ if (offset <= 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fqpath[offset] = '\0';
+
+ ckpt_debug("writing socket directory: %s\n", fqpath);
+ ret = ckpt_write_string(ctx, fqpath, strlen(fqpath));
+ out:
+ kfree(buf);
+ return ret;
+}
+
+static int sock_getnames(struct ckpt_ctx *ctx,
+ struct socket *socket,
+ struct sockaddr *loc, unsigned *loc_len,
+ struct sockaddr *rem, unsigned *rem_len)
+{
+ int ret;
+
+ ret = sock_getname(socket, loc, loc_len);
+ if (ret) {
+ ckpt_write_err(ctx, "Unable to getname of local: %i", ret);
+ return -EINVAL;
+ }
+
+ ret = sock_getpeer(socket, rem, rem_len);
+ if (ret) {
+ if ((socket->sk->sk_type != SOCK_DGRAM) &&
+ (socket->sk->sk_state == TCP_ESTABLISHED)) {
+ ckpt_write_err(ctx, "Unable to getname of remote: %i",
+ ret);
+ return -EINVAL;
+ }
+ *rem_len = 0;
+ }
+
+ return 0;
+}
+
+static int sock_unix_checkpoint(struct ckpt_ctx *ctx,
+ struct socket *socket,
+ struct ckpt_hdr_socket *h)
+{
+ struct unix_sock *sk = unix_sk(socket->sk);
+ struct unix_sock *pr = unix_sk(sk->peer);
+ struct ckpt_hdr_socket_unix *un;
+ int new;
+ int ret = -ENOMEM;
+
+ if ((socket->sk->sk_state == TCP_LISTEN) &&
+ !skb_queue_empty(&socket->sk->sk_receive_queue)) {
+ ckpt_write_err(ctx, "listening socket has unaccepted peers");
+ return -EBUSY;
+ }
+
+ un = ckpt_hdr_get_type(ctx, sizeof(*un), CKPT_HDR_SOCKET_UNIX);
+ if (!un)
+ return -EINVAL;
+
+ ret = sock_getnames(ctx, socket,
+ (struct sockaddr *)&un->laddr, &un->laddr_len,
+ (struct sockaddr *)&un->raddr, &un->raddr_len);
+ if (ret)
+ goto out;
+
+ if (sk->dentry && (sk->dentry->d_inode->i_nlink > 0))
+ un->flags |= CKPT_UNIX_LINKED;
+
+ un->this = ckpt_obj_lookup_add(ctx, sk, CKPT_OBJ_SOCK, &new);
+ if (un->this < 0)
+ goto out;
+
+ if (sk->peer)
+ un->peer = ckpt_obj_lookup_add(ctx, pr, CKPT_OBJ_SOCK, &new);
+ else
+ un->peer = 0;
+
+ if (un->peer < 0) {
+ ret = un->peer;
+ goto out;
+ }
+
+ ret = ckpt_write_obj(ctx, (struct ckpt_hdr *) h);
+ if (ret < 0)
+ goto out;
+
+ ret = ckpt_write_obj(ctx, (struct ckpt_hdr *) un);
+ if (ret < 0)
+ goto out;
+
+ if (sock_unix_need_cwd(&un->laddr, un->laddr_len))
+ ret = sock_unix_write_cwd(ctx, socket->sk, un->laddr.sun_path);
+ out:
+ ckpt_hdr_put(ctx, un);
+
+ return ret;
+}
+
+static int sock_cptrst_verify(struct ckpt_hdr_socket *h)
+{
+ uint8_t userlocks_mask = SOCK_SNDBUF_LOCK | SOCK_RCVBUF_LOCK |
+ SOCK_BINDADDR_LOCK | SOCK_BINDPORT_LOCK;
+
+ if (h->sock.shutdown & ~SHUTDOWN_MASK)
+ return -EINVAL;
+ if (h->sock.userlocks & ~userlocks_mask)
+ return -EINVAL;
+ if (!ckpt_validate_errno(h->sock.err))
+ return -EINVAL;
+
+ /* None of our supported types use this flag */
+ if (h->sock.flags & SOCK_DESTROY)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sock_cptrst_opt(int op, struct socket *socket,
+ int optname, char *opt, int len)
+{
+ mm_segment_t fs;
+ int ret;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ if (op == CKPT_CPT)
+ ret = sock_getsockopt(socket, SOL_SOCKET, optname, opt, &len);
+ else
+ ret = sock_setsockopt(socket, SOL_SOCKET, optname, opt, len);
+
+ set_fs(fs);
+
+ return ret;
+}
+
+#define CKPT_COPY_SOPT(op, sock, name, opt) \
+ sock_cptrst_opt(op, sock->sk_socket, name, (char *)opt, sizeof(*opt))
+
+static int sock_cptrst_bufopts(int op, struct sock *sock,
+ struct ckpt_hdr_socket *h)
+
+{
+ if (CKPT_COPY_SOPT(op, sock, SO_RCVBUF, &h->sock.rcvbuf))
+ if ((op == CKPT_RST) &&
+ CKPT_COPY_SOPT(op, sock, SO_RCVBUFFORCE, &h->sock.rcvbuf)) {
+ ckpt_debug("Failed to set SO_RCVBUF");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_SNDBUF, &h->sock.sndbuf))
+ if ((op == CKPT_RST) &&
+ CKPT_COPY_SOPT(op, sock, SO_SNDBUFFORCE, &h->sock.sndbuf)) {
+ ckpt_debug("Failed to set SO_SNDBUF");
+ return -EINVAL;
+ }
+
+ /* It's silly that we have to fight ourselves here, but
+ * sock_setsockopt() doubles the initial value, so divide here
+ * to store the user's value and avoid doubling on restart
+ */
+ if ((op == CKPT_CPT) && (h->sock.rcvbuf != SOCK_MIN_RCVBUF))
+ h->sock.rcvbuf >>= 1;
+
+ if ((op == CKPT_CPT) && (h->sock.sndbuf != SOCK_MIN_SNDBUF))
+ h->sock.sndbuf >>= 1;
+
+ return 0;
+}
+
+static int sock_cptrst(struct ckpt_ctx *ctx,
+ struct sock *sock,
+ struct ckpt_hdr_socket *h,
+ int op)
+{
+ if (sock->sk_socket) {
+ CKPT_COPY(op, h->socket.flags, sock->sk_socket->flags);
+ CKPT_COPY(op, h->socket.state, sock->sk_socket->state);
+ }
+
+ CKPT_COPY(op, h->sock_common.bound_dev_if, sock->sk_bound_dev_if);
+ CKPT_COPY(op, h->sock_common.family, sock->sk_family);
+
+ CKPT_COPY(op, h->sock.shutdown, sock->sk_shutdown);
+ CKPT_COPY(op, h->sock.userlocks, sock->sk_userlocks);
+ CKPT_COPY(op, h->sock.no_check, sock->sk_no_check);
+ CKPT_COPY(op, h->sock.protocol, sock->sk_protocol);
+ CKPT_COPY(op, h->sock.err, sock->sk_err);
+ CKPT_COPY(op, h->sock.err_soft, sock->sk_err_soft);
+ CKPT_COPY(op, h->sock.type, sock->sk_type);
+ CKPT_COPY(op, h->sock.state, sock->sk_state);
+ CKPT_COPY(op, h->sock.backlog, sock->sk_max_ack_backlog);
+
+ /* TODO:
+ * Break out setting each of the flags to use setsockopt() or
+ * perform proper security check
+ */
+ CKPT_COPY(op, h->sock.flags, sock->sk_flags);
+
+ if (sock_cptrst_bufopts(op, sock, h))
+ return -EINVAL;
+
+ if (CKPT_COPY_SOPT(op, sock, SO_REUSEADDR, &h->sock_common.reuse)) {
+ ckpt_debug("Failed to set SO_REUSEADDR");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_PRIORITY, &h->sock.priority)) {
+ ckpt_debug("Failed to set SO_PRIORITY");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_RCVLOWAT, &h->sock.rcvlowat)) {
+ ckpt_debug("Failed to set SO_RCVLOWAT");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_LINGER, &h->sock.linger)) {
+ ckpt_debug("Failed to set SO_LINGER");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_SNDTIMEO, &h->sock.sndtimeo)) {
+ ckpt_debug("Failed to set SO_SNDTIMEO");
+ return -EINVAL;
+ }
+
+ if (CKPT_COPY_SOPT(op, sock, SO_RCVTIMEO, &h->sock.rcvtimeo)) {
+ ckpt_debug("Failed to set SO_RCVTIMEO");
+ return -EINVAL;
+ }
+
+ if ((h->socket.state == SS_CONNECTED) &&
+ (h->sock.state != TCP_ESTABLISHED)) {
+ ckpt_debug("socket/sock in inconsistent state: %i/%i",
+ h->socket.state, h->sock.state);
+ return -EINVAL;
+ } else if ((h->sock.state < TCP_ESTABLISHED) ||
+ (h->sock.state >= TCP_MAX_STATES)) {
+ ckpt_debug("sock in invalid state: %i", h->sock.state);
+ return -EINVAL;
+ } else if ((h->socket.state < SS_FREE) ||
+ (h->socket.state > SS_DISCONNECTING)) {
+ ckpt_debug("socket in invalid state: %i",
+ h->socket.state);
+ return -EINVAL;
+ }
+
+ if (op == CKPT_RST)
+ return sock_cptrst_verify(h);
+ else
+ return 0;
+}
+
+int do_sock_file_checkpoint(struct ckpt_ctx *ctx, struct file *file)
+{
+ struct socket *socket = file->private_data;
+ struct sock *sock = socket->sk;
+ struct ckpt_hdr_socket *h;
+ int ret = 0;
+
+ h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_SOCKET);
+ if (!h)
+ return -ENOMEM;
+
+ ret = sock_cptrst(ctx, sock, h, CKPT_CPT);
+ if (ret)
+ goto out;
+
+ if (sock->sk_family == AF_UNIX) {
+ ret = sock_unix_checkpoint(ctx, socket, h);
+ if (ret)
+ goto out;
+ } else {
+ ckpt_write_err(ctx, "unsupported socket family %i",
+ sock->sk_family);
+ ret = -ENOSYS;
+ goto out;
+ }
+
+ if (sock->sk_state != TCP_LISTEN) {
+ ret = sock_write_buffers(ctx, &sock->sk_receive_queue);
+ if (ret)
+ goto out;
+
+ ret = sock_write_buffers(ctx, &sock->sk_write_queue);
+ if (ret)
+ goto out;
+ }
+ out:
+ ckpt_hdr_put(ctx, h);
+
+ return ret;
+}
+
+static int sock_read_buffer_sendmsg(struct ckpt_ctx *ctx, struct sock *sock)
+{
+ struct msghdr msg;
+ struct kvec kvec;
+ int ret = 0;
+ int len;
+
+ memset(&msg, 0, sizeof(msg));
+
+ len = _ckpt_read_obj_type(ctx, NULL, 0, CKPT_HDR_SOCKET_BUFFER);
+ if (len < 0)
+ return len;
+
+ if (len > SKB_MAX_ALLOC) {
+ ckpt_debug("Socket buffer too big (%i > %lu)",
+ len, SKB_MAX_ALLOC);
+ return -ENOSPC;
+ }
+
+ kvec.iov_len = len;
+ kvec.iov_base = kmalloc(len, GFP_KERNEL);
+ if (!kvec.iov_base)
+ return -ENOMEM;
+
+ ret = ckpt_kread(ctx, kvec.iov_base, len);
+ if (ret < 0)
+ goto out;
+
+ ret = kernel_sendmsg(sock->sk_socket, &msg, &kvec, 1, len);
+ ckpt_debug("kernel_sendmsg(%i): %i\n", len, ret);
+ if ((ret > 0) && (ret != len))
+ ret = -ENOMEM;
+ out:
+ kfree(kvec.iov_base);
+
+ return ret;
+}
+
+static struct ckpt_hdr_socket_queue *sock_read_buffer_hdr(struct ckpt_ctx *ctx,
+ uint32_t *bufsize)
+{
+ struct ckpt_hdr_socket_queue *h;
+ int err = 0;
+
+ h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SOCKET_QUEUE);
+ if (IS_ERR(h))
+ return h;
+
+ if (!bufsize) {
+ if (h->total_bytes != 0) {
+ ckpt_debug("Expected empty buffer, got %u\n",
+ h->total_bytes);
+ err = -EINVAL;
+ }
+ } else if (h->total_bytes > *bufsize) {
+ /* NB: We let CAP_NET_ADMIN override the system buffer limit
+ * as setsockopt() does
+ */
+ if (capable(CAP_NET_ADMIN))
+ *bufsize = h->total_bytes;
+ else {
+ ckpt_debug("Buffer total %u exceeds limit %u\n",
+ h->total_bytes, *bufsize);
+ err = -EINVAL;
+ }
+ }
+
+ if (err) {
+ ckpt_hdr_put(ctx, h);
+ return ERR_PTR(err);
+ } else
+ return h;
+}
+
+static int sock_unix_read_buffers(struct ckpt_ctx *ctx,
+ struct sock *sock,
+ uint32_t *bufsize)
+{
+ uint8_t sock_shutdown;
+ struct ckpt_hdr_socket_queue *h;
+ int ret = 0;
+ int i;
+
+ h = sock_read_buffer_hdr(ctx, bufsize);
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ /* If peer is shutdown, unshutdown it for this process */
+ sock_shutdown = sock->sk_shutdown;
+ sock->sk_shutdown &= ~SHUTDOWN_MASK;
+
+ for (i = 0; i < h->skb_count; i++) {
+ ret = sock_read_buffer_sendmsg(ctx, sock);
+ ckpt_debug("read_buffer_sendmsg(%i): %i\n", i, ret);
+ if (ret < 0)
+ break;
+
+ if (ret > h->total_bytes) {
+ ckpt_debug("Buffers exceeded claim");
+ ret = -EINVAL;
+ break;
+ }
+
+ h->total_bytes -= ret;
+ ret = 0;
+ }
+
+ sock->sk_shutdown = sock_shutdown;
+ ckpt_hdr_put(ctx, h);
+
+ return ret;
+}
+
+static struct unix_address *sock_unix_makeaddr(struct sockaddr_un *sun_addr,
+ unsigned len)
+{
+ struct unix_address *addr;
+
+ if (len > sizeof(struct sockaddr_un))
+ return ERR_PTR(-EINVAL);
+
+ addr = kmalloc(sizeof(*addr) + len, GFP_KERNEL);
+ if (!addr)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(addr->name, sun_addr, len);
+ addr->len = len;
+ atomic_set(&addr->refcnt, 1);
+
+ return addr;
+}
+
+static int sock_unix_join(struct ckpt_ctx *ctx,
+ struct sock *a,
+ struct sock *b,
+ struct ckpt_hdr_socket_unix *un)
+{
+ struct unix_address *addr = NULL;
+
+ /* FIXME: Do we need to call some security hooks here? */
+
+ sock_hold(a);
+ sock_hold(b);
+
+ unix_sk(a)->peer = b;
+ unix_sk(b)->peer = a;
+
+ /* TODO:
+ * Checkpoint the credentials, restore them here if the values match
+ * the restored creds or we may_setuid()
+ */
+
+ a->sk_peercred.pid = task_tgid_vnr(current);
+ a->sk_peercred.uid = ctx->realcred->uid;
+ a->sk_peercred.gid = ctx->realcred->gid;
+
+ b->sk_peercred.pid = a->sk_peercred.pid;
+ b->sk_peercred.uid = a->sk_peercred.uid;
+ b->sk_peercred.gid = a->sk_peercred.gid;
+
+ if (!UNIX_ADDR_EMPTY(un->raddr_len))
+ addr = sock_unix_makeaddr(&un->raddr, un->raddr_len);
+ else if (!UNIX_ADDR_EMPTY(un->laddr_len))
+ addr = sock_unix_makeaddr(&un->laddr, un->laddr_len);
+
+ if (IS_ERR(addr))
+ return PTR_ERR(addr);
+ else if (addr) {
+ atomic_inc(&addr->refcnt); /* Held by both ends */
+ unix_sk(a)->addr = unix_sk(b)->addr = addr;
+ }
+
+ return 0;
+}
+
+static int sock_unix_restore_connected(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_socket *h,
+ struct ckpt_hdr_socket_unix *un,
+ struct socket *socket)
+{
+ struct sock *this = ckpt_obj_fetch(ctx, un->this, CKPT_OBJ_SOCK);
+ struct sock *peer = ckpt_obj_fetch(ctx, un->peer, CKPT_OBJ_SOCK);
+ struct socket *tmp = NULL;
+ int ret;
+
+ if (!IS_ERR(this) && !IS_ERR(peer)) {
+ /* We're last */
+ struct socket *old = this->sk_socket;
+
+ old->sk = NULL;
+ sock_release(old);
+ sock_graft(this, socket);
+
+ } else if ((PTR_ERR(this) == -EINVAL) && (PTR_ERR(peer) == -EINVAL)) {
+ /* We're first */
+ int family = socket->sk->sk_family;
+ int type = socket->sk->sk_type;
+
+ ret = sock_create(family, type, 0, &tmp);
+ ckpt_debug("sock_create: %i\n", ret);
+ if (ret)
+ goto out;
+
+ this = socket->sk;
+ peer = tmp->sk;
+
+ ret = ckpt_obj_insert(ctx, this, un->this, CKPT_OBJ_SOCK);
+ if (ret < 0)
+ goto out;
+
+ ret = ckpt_obj_insert(ctx, peer, un->peer, CKPT_OBJ_SOCK);
+ if (ret < 0)
+ goto out;
+
+ ret = sock_unix_join(ctx, this, peer, un);
+ ckpt_debug("sock_unix_join: %i\n", ret);
+ if (ret)
+ goto out;
+
+ } else {
+ ckpt_debug("Order Error\n");
+ ret = PTR_ERR(this);
+ goto out;
+ }
+
+ /* Prime the socket's buffer limit with the maximum. These will be
+ * overwritten with the values in the checkpoint stream in a later
+ * phase.
+ */
+ peer->sk_userlocks |= SOCK_SNDBUF_LOCK;
+ peer->sk_sndbuf = sysctl_wmem_max;
+
+ /* Read my buffers and sendmsg() them back to me via my peer */
+
+ /* TODO: handle the unconnected case, as well, as the case
+ * where sendto() has been used on some of the buffers
+ */
+
+ ret = sock_unix_read_buffers(ctx, peer, &peer->sk_sndbuf);
+ ckpt_debug("sock_unix_read_buffers: %i\n", ret);
+ if (ret)
+ goto out;
+
+ /* Read peer's buffers and expect 0 */
+ ret = sock_unix_read_buffers(ctx, peer, NULL);
+ out:
+ if (tmp && ret)
+ sock_release(tmp);
+
+ return ret;
+}
+
+static int sock_unix_unlink(const char *name)
+{
+ struct path spath;
+ struct path ppath;
+ int ret;
+
+ ret = kern_path(name, 0, &spath);
+ if (ret)
+ return ret;
+
+ ret = kern_path(name, LOOKUP_PARENT, &ppath);
+ if (ret)
+ goto out_s;
+
+ if (!spath.dentry) {
+ ckpt_debug("No dentry found for %s\n", name);
+ ret = -ENOENT;
+ goto out_p;
+ }
+
+ if (!ppath.dentry || !ppath.dentry->d_inode) {
+ ckpt_debug("No inode for parent of %s\n", name);
+ ret = -ENOENT;
+ goto out_p;
+ }
+
+ ret = vfs_unlink(ppath.dentry->d_inode, spath.dentry);
+ out_p:
+ path_put(&ppath);
+ out_s:
+ path_put(&spath);
+
+ return ret;
+}
+
+/* Call bind() for socket, optionally changing (temporarily) to @path first
+ * if non-NULL
+ */
+static int sock_unix_chdir_and_bind(struct socket *socket,
+ const char *path,
+ struct sockaddr *addr,
+ unsigned long addrlen)
+{
+ struct sockaddr_un *un = (struct sockaddr_un *)addr;
+ int ret;
+ struct path cur;
+ struct path dir;
+
+ if (path) {
+ ckpt_debug("switching to cwd %s for unix bind", path);
+
+ ret = kern_path(path, 0, &dir);
+ if (ret)
+ return ret;
+
+ ret = inode_permission(dir.dentry->d_inode,
+ MAY_EXEC | MAY_ACCESS);
+ if (ret)
+ goto out;
+
+ write_lock(¤t->fs->lock);
+ cur = current->fs->pwd;
+ current->fs->pwd = dir;
+ write_unlock(¤t->fs->lock);
+ }
+
+ ret = sock_unix_unlink(un->sun_path);
+ ckpt_debug("unlink(%s): %i\n", un->sun_path, ret);
+ if ((ret == 0) || (ret == -ENOENT))
+ ret = sock_bind(socket, addr, addrlen);
+
+ if (path) {
+ write_lock(¤t->fs->lock);
+ current->fs->pwd = cur;
+ write_unlock(¤t->fs->lock);
+ }
+ out:
+ if (path)
+ path_put(&dir);
+
+ return ret;
+}
+
+static int sock_unix_fakebind(struct socket *socket,
+ struct sockaddr_un *addr,
+ unsigned long len)
+{
+ struct unix_address *uaddr;
+
+ uaddr = sock_unix_makeaddr(addr, len);
+ if (IS_ERR(uaddr))
+ return PTR_ERR(uaddr);
+
+ unix_sk(socket->sk)->addr = uaddr;
+
+ return 0;
+}
+
+static int sock_unix_bind(struct ckpt_hdr_socket *h,
+ struct ckpt_hdr_socket_unix *un,
+ struct socket *socket,
+ const char *path)
+{
+ struct sockaddr *addr = (struct sockaddr *)&un->laddr;
+ unsigned long len = un->laddr_len;
+
+ if (!un->laddr.sun_path[0])
+ return sock_bind(socket, addr, len);
+ else if (!(un->flags & CKPT_UNIX_LINKED))
+ return sock_unix_fakebind(socket, &un->laddr, len);
+ else
+ return sock_unix_chdir_and_bind(socket, path, addr, len);
+}
+
+/* Some easy pre-flight checks before we get underway */
+static int sock_unix_precheck(struct socket *socket,
+ struct ckpt_hdr_socket *h)
+{
+ struct net *net = sock_net(socket->sk);
+
+ if ((h->socket.state == SS_CONNECTING) ||
+ (h->socket.state == SS_DISCONNECTING) ||
+ (h->socket.state == SS_FREE)) {
+ ckpt_debug("AF_UNIX socket can't be SS_(DIS)CONNECTING");
+ return -EINVAL;
+ }
+
+ /* AF_UNIX overloads the backlog setting to define the maximum
+ * queue length for DGRAM sockets. Make sure we don't let the
+ * caller exceed that value on restart.
+ */
+ if ((h->sock.type == SOCK_DGRAM) &&
+ (h->sock.backlog > net->unx.sysctl_max_dgram_qlen)) {
+ ckpt_debug("DGRAM backlog of %i exceeds system max of %i\n",
+ h->sock.backlog, net->unx.sysctl_max_dgram_qlen);
+ return -EINVAL;
+ }
+
+ if (h->sock.flags & SOCK_USE_WRITE_QUEUE) {
+ ckpt_debug("AF_UNIX socket has SOCK_USE_WRITE_QUEUE set");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sock_unix_restore(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_socket *h,
+ struct socket *socket)
+{
+ struct ckpt_hdr_socket_unix *un;
+ int ret = -EINVAL;
+ char *cwd = NULL;
+
+ ret = sock_unix_precheck(socket, h);
+ if (ret)
+ return ret;
+
+ un = ckpt_read_obj_type(ctx, sizeof(*un), CKPT_HDR_SOCKET_UNIX);
+ if (IS_ERR(un))
+ return PTR_ERR(un);
+
+ if (un->peer < 0)
+ goto out;
+
+ if (sock_unix_need_cwd(&un->laddr, un->laddr_len)) {
+ ret = ckpt_read_string(ctx, &cwd, PATH_MAX);
+ ckpt_debug("read cwd(%i): %s\n", ret, cwd);
+ if (ret)
+ goto out;
+ }
+
+ if ((h->sock.state != TCP_ESTABLISHED) &&
+ !UNIX_ADDR_EMPTY(un->laddr_len)) {
+ ret = sock_unix_bind(h, un, socket, cwd);
+ if (ret)
+ goto out;
+ }
+
+ if ((h->sock.state == TCP_ESTABLISHED) || (h->sock.state == TCP_CLOSE))
+ ret = sock_unix_restore_connected(ctx, h, un, socket);
+ else if (h->sock.state == TCP_LISTEN)
+ ret = socket->ops->listen(socket, h->sock.backlog);
+ else
+ ckpt_debug("unsupported UNIX socket state %i\n", h->sock.state);
+ out:
+ ckpt_hdr_put(ctx, un);
+ kfree(cwd);
+ return ret;
+}
+
+struct socket *do_sock_file_restore(struct ckpt_ctx *ctx,
+ struct ckpt_hdr_socket *h)
+{
+ struct socket *socket;
+ int ret;
+
+ if ((h->sock.type != SOCK_DGRAM) && (h->sock.type != SOCK_STREAM)) {
+ ckpt_debug("Socket type %i not supported", h->sock.type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = sock_create(h->sock_common.family, h->sock.type, 0, &socket);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ if (h->sock_common.family == AF_UNIX) {
+ ret = sock_unix_restore(ctx, h, socket);
+ ckpt_debug("sock_unix_restore: %i\n", ret);
+ } else {
+ ckpt_debug("unsupported family %i\n", h->sock_common.family);
+ ret = -ENOSYS;
+ }
+
+ if (ret)
+ goto out;
+
+ ret = sock_cptrst(ctx, socket->sk, h, CKPT_RST);
+ out:
+ if (ret) {
+ sock_release(socket);
+ socket = ERR_PTR(ret);
+ }
+
+ return socket;
+}
+
diff --git a/net/socket.c b/net/socket.c
index 65e7698..8732fe2 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -96,6 +96,8 @@
#include <net/sock.h>
#include <linux/netfilter.h>
+#include <linux/checkpoint.h>
+
static int sock_no_open(struct inode *irrelevant, struct file *dontcare);
static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
@@ -140,6 +142,9 @@ static const struct file_operations socket_file_ops = {
.sendpage = sock_sendpage,
.splice_write = generic_splice_sendpage,
.splice_read = sock_splice_read,
+#ifdef CONFIG_CHECKPOINT
+ .checkpoint = sock_file_checkpoint,
+#endif
};
/*
@@ -415,6 +420,86 @@ int sock_map_fd(struct socket *sock, int flags)
return fd;
}
+#ifdef CONFIG_CHECKPOINT
+int sock_file_checkpoint(struct ckpt_ctx *ctx, void *ptr)
+{
+ struct ckpt_hdr_file_socket *h;
+ int ret;
+ struct file *file = ptr;
+
+ h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_FILE);
+ if (!h)
+ return -ENOMEM;
+
+ h->common.f_type = CKPT_FILE_SOCKET;
+
+ ret = checkpoint_file_common(ctx, file, &h->common);
+ if (ret < 0)
+ goto out;
+ ret = ckpt_write_obj(ctx, (struct ckpt_hdr *) h);
+ if (ret < 0)
+ goto out;
+
+ ret = do_sock_file_checkpoint(ctx, file);
+ out:
+ ckpt_hdr_put(ctx, h);
+ return ret;
+}
+
+static struct file *sock_alloc_attach_fd(struct socket *socket)
+{
+ struct file *file;
+ int err;
+
+ file = get_empty_filp();
+ if (!file)
+ return ERR_PTR(ENOMEM);
+
+ err = sock_attach_fd(socket, file, 0);
+ if (err < 0) {
+ put_filp(file);
+ file = ERR_PTR(err);
+ }
+
+ return file;
+}
+
+void *sock_file_restore(struct ckpt_ctx *ctx)
+{
+ struct ckpt_hdr_socket *h = NULL;
+ struct socket *socket = NULL;
+ struct file *file = NULL;
+ int err;
+
+ h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SOCKET);
+ if (IS_ERR(h))
+ return h;
+
+ socket = do_sock_file_restore(ctx, h);
+ if (IS_ERR(socket)) {
+ err = PTR_ERR(socket);
+ goto err_put;
+ }
+
+ file = sock_alloc_attach_fd(socket);
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ goto err_release;
+ }
+
+ ckpt_hdr_put(ctx, h);
+
+ return file;
+
+ err_release:
+ sock_release(socket);
+ err_put:
+ ckpt_hdr_put(ctx, h);
+
+ return ERR_PTR(err);
+}
+#endif /* CONFIG_CHECKPOINT */
+
static struct socket *sock_from_file(struct file *file, int *err)
{
if (file->f_op == &socket_file_ops)
--
1.6.2.5
^ permalink raw reply related
* Re: [RFC PATCH v2 19/19] virtio: add a vbus transport
From: Gregory Haskins @ 2009-08-10 15:40 UTC (permalink / raw)
To: Anthony Liguori
Cc: linux-kernel, agraf, pmullaney, pmorreale, rusty, netdev, kvm,
avi, bhutchings, andi, gregkh, herber, chrisw, shemminger,
Michael S. Tsirkin, alacrityvm-devel, Arnd Bergmann
In-Reply-To: <4A7EFBE8.7050409@codemonkey.ws>
[-- Attachment #1: Type: text/plain, Size: 7197 bytes --]
Anthony Liguori wrote:
> Gregory Haskins wrote:
>> We add a new virtio transport for accessing backends located on vbus.
>> This
>> complements the existing transports for virtio-pci, virtio-s390, and
>> virtio-lguest that already exist.
>>
>> Signed-off-by: Gregory Haskins <ghaskins@novell.com>
>
> Very interesting...
>
> I'm somewhat confused by what you're advocating vbus as. I'm trying to
> figure out how we converge vbus and virtio and become one big happy
> family :-)
>
> What parts of it do you think are better than virtio?
I see them as distinctly different, and complimentary: virtio is
primarily a device-interface. vbus is primarily a bus-model.
While its true that vbus has a rudimentary "device interface" (ala
dev->call(), dev->shm(), etc), it will typically be layered with a more
robust device model (such as virtqueue based virtio-net, or IOQ based
venet).
This is akin to raw PCI. PCI has the basic mechanics (MMIO, PIO,
interrupts, DMA), but generally devices overlay higher layer constructs
(such as tx/rx rings for ethernet). vbus is similar in that regard.
virtio, on the other hand, is a fully fleshed device model, but it needs
to be paired up with some other bus-model (such as qemu+pci via
virtio-pci, or linux+vbus via virtio-vbus) to be complete.
> Should we forget
> about venet and just focus on virtio-net on top of virtio-vbus assuming
> that we can prove venet-tap/virtio-vbus/virtio-net is just as good as
> venet-tap/vbus/venet?
I think going forward, I would love to see some community weight behind
virtio-vbus and the standardization of the virtio-X drivers (net, blk,
console, etc) as the default core IO device model for vbus. If that
were to happen, I would not be inclined to create any new native vbus
drivers for where there is overlap with virtio (net, disk, etc).
I *will*, however, likely use the native vbus interfaces for some of the
device models in the pipeline (such as the real-time controller, and
OFED) because there isn't a virtio equivalent for that, and the vbus
device model was written with them in mind.
And regardless of the final outcome of virtio-vbus as a viable virtio
transport model, I will probably continue to support the native vbus
"venet" protocol for AlacrityVM indefinitely only because I have people
that are using it today (AlacrityVM being one example, but others are in
the works), it is simple, and it works extremely well.
>
> If we can prove that an in-kernel virtio-net
> backend/virtio-pci/virtio-net does just as well as
> venet-tap/virtio-vbus/virtio-net does that mean that vbus is no longer
> needed?
vbus is primarily about the bus model and resource-containers. It is
therefore so much more than just 802.x networking and/or KVM. So no,
obtaining equal network performance with virtio-net sans vbus does not
invalidate vbus as a concept. It would perhaps purely be disincentive
for anyone to chose venet over virtio-net within a KVM guest who doesn't
have a vested interest in the venet ABI.
>
> If you concede that the transport mechanisms can be identical, are you
> really advocating the discovering and configuration mechanisms in vbus?
The problem is that they are not truly identical, and they cannot easily
be made identical.
One of the design goals is to create a re-useable, in-kernel device
model with a thin-shim transport between the guest and the bus. The
transport (called a "vbus-connector") in question must convey a very
small verb namespace (devadd/drop, devopen/close, shmsignal, etc) to use
this bus. So you have a driver for this transport (e.g. vbus-pcibridge)
but the bus-model itself, device-models, and guest drivers are all reusable.
Another design goal is to create a high-performance, low-latency,
software-to-software optimized IO subsystem. PCI was designed for
software-to-hardware, so it has assumptions about that environment that
simply do not apply in software-to-software. Most of these limitations
can be worked-around with KVM surgery and/or creative guest development,
but they are (at best) awkward to express in PCI.
For example, creating a high-performance, synchronous, in-kernel "call"
in PCI is fairly complicated, awkward, and requires a bit of per-device
heap to pull off. Yet, I need this functionality for things like RT
support (scheduler state changes, etc).
As another example, the PCI model is somewhat resource rigid and
therefore awkward for creating dynamic software objects in response to
guest actions on demand, such as IPC sockets, due to the way MSI works.
As a third example, the PCI device model makes assumptions about
signal-path delivery (MSI to APIC) which may or may not be the optimal
path (e.g. see my priority+aggregation thread with Arnd).
I am open to suggestions on ways to write the "connector" code
differently. I am sure that some things could be addressed at a
different layer (such as the conversation with Arnd regarding the PV
interrupt controller).
I think, however, that the motivations to PCI'ize the x86-KVM world are
driven more by inertia (its already in qemu and many guests know it)
than by intentional selection (its model is the best to express the
needs of virt). Therefore, I am trying to put a stake in the ground to
redirect that inertia towards a path that I believe is more amenable to
virt in the long term. Consider it a long-term investment in the platform.
My approach to date is more akin to how something like USB was
introduced. The USB designers were not trying to artificially constrain
their design so that it looked like PCI just to avoid needing an OS
driver. They (presumably) designed a bus that best achieved their
goals, and then people wrote adaption layers (ala PCI-to-USB bridges) to
support them on the OS's/arches that cared. Eventually most did.
I admit that, as with any new bus (e.g. USB), there will undoubtedly be
some lumps during the initial roll-out phase since it is not yet
universally supported. But the long term vision is that (assuming it
succeeds as a project) that eventually vbus would be ubiquitous (ala
USB) and it makes for a better bus-model choice for things like
linux-based hypervisors (pci-based kvm (x86/ppc), s390-kvm, lguest, UML,
openvz, and even something like Xen-Dom0, etc), as well as applications
and even some specialized physical systems (Blade systems, clusters, etc).
> Is that what we should be focusing on? Do you care only about the host
> mechanisms or do you also require the guest infrastructure to be present?
I see it as a tightly integrated portion of the stack that sits right
below the virtio layer.
>
> I think two paravirtual I/O frameworks for KVM is a bad thing.
So hopefully after reading my reply above you can see that I don't think
this is indeed what is being proposed.
> It duplicates a ton of code and will very likely lead to user unhappiness.
I totally agree and I hope we can work together to find a solution that
makes the most sense and eliminates as much duplicate effort as possible.
Kind Regards,
-Greg
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 267 bytes --]
^ permalink raw reply
* Re: [PATCH 0/8] sky2: driver update
From: Stephen Hemminger @ 2009-08-10 15:48 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20090809.210943.256633746.davem@davemloft.net>
On Sun, 09 Aug 2009 21:09:43 -0700 (PDT)
David Miller <davem@davemloft.net> wrote:
> From: Stephen Hemminger <shemminger@vyatta.com>
> Date: Thu, 06 Aug 2009 09:12:13 -0700
>
> > This undoes the prematurely committed sky2 transmit logic changes
> > and applies Mike's later changes.
> > Here is a corrected patch stream please apply this to 2.6.31-rc5
>
> I have to pass on this set, too many problems:
>
> 1) You didn't provide explicit "From: " lines for patches where
> you were not the author Stephen. I could guess from the
> Signoff lines but I'd rather not.
What is the mechanics for this, does patchwork accept From:
lines in message body?
^ permalink raw reply
* RE: [evb] Re: [PATCH][RFC] net/bridge: add basic VEPA support
From: Fischer, Anna @ 2009-08-10 15:59 UTC (permalink / raw)
To: Arnd Bergmann
Cc: evb@yahoogroups.com, 'Stephen Hemminger', Fischer, Anna,
bridge@lists.linux-foundation.org, linux-kernel@vger.kernel.org,
netdev@vger.kernel.org, virtualization@lists.linux-foundation.org,
davem@davemloft.net, kaber@trash.net, adobriyan@gmail.com,
'Or Gerlitz', Paul Congdon (UC Davis)
In-Reply-To: <200908101723.10832.arnd@arndb.de>
> Subject: Re: [PATCH][RFC] net/bridge: add basic VEPA support
>
> On Friday 07 August 2009, Paul Congdon (UC Davis) wrote:
> >
> > I don't think your scheme works too well because broadcast packet
> coming
> > from other interfaces on br0 would get replicated and sent across the
> wire
> > to ethB multiple times.
>
> Right, that won't work. So the bridge patch for the hairpin turn
> is still the best solution.
Yes, I think that we should separate the discussions between hairpin
mode on the adjacent bridge and the VEPA filtering service residing
within the end-station. The hairpin feature really has to be
implemented in the bridging code.
> Btw, how will that interact with
> the bride-netfilter (ebtables) setup? Can you apply any filters
> that work on current bridges also between two VEPA ports while
> doing the hairpin turn?
The hairpin mode is implemented on the adjacent bridge. The only
difference for a hairpin mode port vs. a normal bridge port is that
it can pass frames back out to the same port it came from. All the
netfilter hooks are still in place.
On the VEPA filtering service side, the only change we have implemented
in the bridging code is that in VEPA mode all frames are passed to the
uplink on TX. However, frames are still passed through the netfilter
hooks before they go out on the wire. On the inbound path, there are
no changes to the way frames are processed (except the filtering for
the original source port), so netfilter hooks work in the same way
as for a normal bridge.
If a frame is reflected back because of a hairpin turn, then of course
the incoming port is the VEPA uplink port and not the port that
originally sent the frame. So if you are trying to enforce some
packet filtering on that inbound path, then you would have to do that
based on MAC addresses and not on bridge ports. But I would assume that
you would enforce the filtering already before you send out the frame
to the adjacent bridge. Apart from that, if you enable your bridge to
behave in VEPA mode, then you would typically do packet filtering etc
on the adjacent bridge and not use the netfilter hook. You can still use
both though, if you like.
Anna
^ permalink raw reply
* Re: [evb] Re: [PATCH][RFC] net/bridge: add basic VEPA support
From: Arnd Bergmann @ 2009-08-10 16:16 UTC (permalink / raw)
To: Fischer, Anna
Cc: evb@yahoogroups.com, 'Stephen Hemminger',
bridge@lists.linux-foundation.org, linux-kernel@vger.kernel.org,
netdev@vger.kernel.org, virtualization@lists.linux-foundation.org,
davem@davemloft.net, kaber@trash.net, adobriyan@gmail.com,
'Or Gerlitz', Paul Congdon (UC Davis)
In-Reply-To: <0199E0D51A61344794750DC57738F58E6D6AE99867@GVW1118EXC.americas.hpqcorp.net>
On Monday 10 August 2009, Fischer, Anna wrote:
> On the VEPA filtering service side, the only change we have implemented
> in the bridging code is that in VEPA mode all frames are passed to the
> uplink on TX. However, frames are still passed through the netfilter
> hooks before they go out on the wire. On the inbound path, there are
> no changes to the way frames are processed (except the filtering for
> the original source port), so netfilter hooks work in the same way
> as for a normal bridge.
Ah, interesting. I did not realize that the hooks were still active,
although that obviously makes sense. So that would be another
important difference between our implementations.
> If a frame is reflected back because of a hairpin turn, then of course
> the incoming port is the VEPA uplink port and not the port that
> originally sent the frame. So if you are trying to enforce some
> packet filtering on that inbound path, then you would have to do that
> based on MAC addresses and not on bridge ports. But I would assume that
> you would enforce the filtering already before you send out the frame
> to the adjacent bridge. Apart from that, if you enable your bridge to
> behave in VEPA mode, then you would typically do packet filtering etc
> on the adjacent bridge and not use the netfilter hook. You can still use
> both though, if you like.
Right, that was my point. They bridge in VEPA mode would likely be
configured to be completely ignorant of the data going through it
and not do any filter, and you do all filterring on the adjacent
bridge.
I just wasn't sure that this is possible with ebtables if the
adjacent bridge is a Linux system with the bridge in hairpin turn
mode.
Arnd <><
^ permalink raw reply
* RE: [evb] RE: [PATCH][RFC] net/bridge: add basic VEPA support
From: Fischer, Anna @ 2009-08-10 16:32 UTC (permalink / raw)
To: Stephen Hemminger, Arnd Bergmann
Cc: Or Gerlitz, Paul Congdon (UC Davis), netdev@vger.kernel.org,
bridge@lists.linux-foundation.org, davem@davemloft.net,
adobriyan@gmail.com, virtualization@lists.linux-foundation.org,
evb@yahoogroups.com
In-Reply-To: <200908101728.15649.arnd@arndb.de>
> Subject: Re: [evb] RE: [PATCH][RFC] net/bridge: add basic VEPA support
>
> On Monday 10 August 2009, Stephen Hemminger wrote:
> > On Sun, 09 Aug 2009 14:19:08 +0300, Or Gerlitz
> <ogerlitz@voltaire.com> wrote:
> > > Looking in macvlan_set_multicast_list() it acts in a similar manner
> to
> > > macvlan_set_mac_address() in the sense that it calls dev_mc_sync().
> I
> > > assume what's left is to add macvlan_hash_xxx multicast logic to
> > > map/unmap multicast groups to what macvlan devices want to receive
> them
> > > and this way the flooding can be removed, correct?
> >
> > The device can just flood all multicast packets, since the filtering
> > is done on the receive path anyway.
Is this handled by one of the additional patches? In the current kernel tree
macvlan code it looks as if multicast filtering is only handled by the
physical device driver, but not on particular macvlan devices.
> But we'd still have to copy the frames to user space (for both
> macvtap and raw packet sockets) and exit from the guest to inject
> it into its stack, right?
I think it would be nice if you can implement what Or describes for
macvlan and avoid flooding, and it doesn't sound too hard to do.
I guess one advantage for macvlan (over the bridge) is that you can
program in all information you have for the ports attached to it, e.g.
MAC addresses and multicast addresses. So you could take advantage of
that whereas the bridge always floods multicast frames to all ports.
How would this work though, if the OS inside the guest wants to register
to a particular multicast address? Is this propagated through the backend
drivers to the macvlan/macvtap interface?
Anna
^ permalink raw reply
* Re: [evb] RE: [PATCH][RFC] net/bridge: add basic VEPA support
From: Stephen Hemminger @ 2009-08-10 16:51 UTC (permalink / raw)
To: Fischer, Anna
Cc: Arnd Bergmann, Or Gerlitz, Paul Congdon (UC Davis),
netdev@vger.kernel.org, bridge@lists.linux-foundation.org,
davem@davemloft.net, adobriyan@gmail.com,
virtualization@lists.linux-foundation.org, evb@yahoogroups.com
In-Reply-To: <0199E0D51A61344794750DC57738F58E6D6AE99884@GVW1118EXC.americas.hpqcorp.net>
On Mon, 10 Aug 2009 16:32:01 +0000
"Fischer, Anna" <anna.fischer@hp.com> wrote:
> > Subject: Re: [evb] RE: [PATCH][RFC] net/bridge: add basic VEPA support
> >
> > On Monday 10 August 2009, Stephen Hemminger wrote:
> > > On Sun, 09 Aug 2009 14:19:08 +0300, Or Gerlitz
> > <ogerlitz@voltaire.com> wrote:
> > > > Looking in macvlan_set_multicast_list() it acts in a similar manner
> > to
> > > > macvlan_set_mac_address() in the sense that it calls dev_mc_sync().
> > I
> > > > assume what's left is to add macvlan_hash_xxx multicast logic to
> > > > map/unmap multicast groups to what macvlan devices want to receive
> > them
> > > > and this way the flooding can be removed, correct?
> > >
> > > The device can just flood all multicast packets, since the filtering
> > > is done on the receive path anyway.
>
> Is this handled by one of the additional patches? In the current kernel tree
> macvlan code it looks as if multicast filtering is only handled by the
> physical device driver, but not on particular macvlan devices.
>
>
> > But we'd still have to copy the frames to user space (for both
> > macvtap and raw packet sockets) and exit from the guest to inject
> > it into its stack, right?
>
> I think it would be nice if you can implement what Or describes for
> macvlan and avoid flooding, and it doesn't sound too hard to do.
>
> I guess one advantage for macvlan (over the bridge) is that you can
> program in all information you have for the ports attached to it, e.g.
> MAC addresses and multicast addresses. So you could take advantage of
> that whereas the bridge always floods multicast frames to all ports.
>
> How would this work though, if the OS inside the guest wants to register
> to a particular multicast address? Is this propagated through the backend
> drivers to the macvlan/macvtap interface?
Sure filtering is better, but multicast performance with large number
of guests is really a corner case, not the real performance issue.
^ permalink raw reply
* [PATCH] AF_PACKET: Fix PACKET_MMAP on 64-bit kernel/32-bit user systems
From: cndougla @ 2009-08-10 17:03 UTC (permalink / raw)
To: netdev
PACKET_MMAP sets up a ring buffer at a memory location so that kernel
and user space can read from it without extra overhead. However, the
data is written with a header, struct tpacket_hdr, which begins with
an unsigned long field, tp_status. This means in a split 64-bit/32-bit
environment the kernel- and user-space reads from the mmap'd region
will be different, since in kernel-space the field is 64-bit while in
user-space the field is 32-bit.
The following patch makes the size of the structure constant by
changing the tp_status field to 32-bits. It does not appear to need
64-bits, since only 4-bits are actually used throughout the AF_PACKET
code.
Signed-off-by: Chase Douglas <cndougla@linux.vnet.ibm.com>
--- a/include/linux/if_packet.h 2009-08-10 16:53:11.000000000 +0000
+++ b/include/linux/if_packet.h 2009-08-10 16:53:28.000000000 +0000
@@ -65,7 +65,7 @@ struct tpacket_auxdata
struct tpacket_hdr
{
- unsigned long tp_status;
+ unsigned int tp_status;
#define TP_STATUS_KERNEL 0
#define TP_STATUS_USER 1
#define TP_STATUS_COPY 2
^ permalink raw reply
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