[{"id":1760222,"web_url":"http://patchwork.ozlabs.org/comment/1760222/","msgid":"<1504103947.21231.5.camel@redhat.com>","list_archive_url":null,"date":"2017-08-30T14:39:07","subject":"Re: [PATCH net-next 3/3 v11] drivers: net: ethernet: qualcomm:\n\trmnet: Initial implementation","submitter":{"id":665,"url":"http://patchwork.ozlabs.org/api/people/665/","name":"Dan Williams","email":"dcbw@redhat.com"},"content":"On Tue, 2017-08-29 at 22:44 -0600, Subash Abhinov Kasiviswanathan\nwrote:\n> RmNet driver provides a transport agnostic MAP (multiplexing and\n> aggregation protocol) support in embedded module. Module provides\n> virtual network devices which can be attached to any IP-mode\n> physical device. This will be used to provide all MAP functionality\n> on future hardware in a single consistent location.\n\nGeneral comment; other drivers that do similar things (macvlan, ipvlan)\nuse the term \"port\" to refer to what I think you're calling a\n\"rmnet_real_dev_info\".  Maybe that's a shorter or less confusing term. \nCould be renamed later too, if you wanted to do so.\n\n> Signed-off-by: Subash Abhinov Kasiviswanathan\n> <subashab@codeaurora.org>\n> ---\n>  Documentation/networking/rmnet.txt                 |  82 ++++\n>  drivers/net/ethernet/qualcomm/Kconfig              |   2 +\n>  drivers/net/ethernet/qualcomm/Makefile             |   2 +\n>  drivers/net/ethernet/qualcomm/rmnet/Kconfig        |  12 +\n>  drivers/net/ethernet/qualcomm/rmnet/Makefile       |  10 +\n>  drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 419\n> +++++++++++++++++++++\n>  drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h |  56 +++\n>  .../net/ethernet/qualcomm/rmnet/rmnet_handlers.c   | 271\n> +++++++++++++\n>  .../net/ethernet/qualcomm/rmnet/rmnet_handlers.h   |  26 ++\n>  drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h    |  88 +++++\n>  .../ethernet/qualcomm/rmnet/rmnet_map_command.c    | 107 ++++++\n>  .../net/ethernet/qualcomm/rmnet/rmnet_map_data.c   | 105 ++++++\n>  .../net/ethernet/qualcomm/rmnet/rmnet_private.h    |  45 +++\n>  drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c    | 170 +++++++++\n>  drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h    |  29 ++\n>  15 files changed, 1424 insertions(+)\n>  create mode 100644 Documentation/networking/rmnet.txt\n>  create mode 100644 drivers/net/ethernet/qualcomm/rmnet/Kconfig\n>  create mode 100644 drivers/net/ethernet/qualcomm/rmnet/Makefile\n>  create mode 100644\n> drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c\n>  create mode 100644\n> drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h\n>  create mode 100644\n> drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c\n>  create mode 100644\n> drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h\n>  create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h\n>  create mode 100644\n> drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c\n>  create mode 100644\n> drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c\n>  create mode 100644\n> drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h\n>  create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c\n>  create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h\n> \n> diff --git a/Documentation/networking/rmnet.txt\n> b/Documentation/networking/rmnet.txt\n> new file mode 100644\n> index 0000000..6b341ea\n> --- /dev/null\n> +++ b/Documentation/networking/rmnet.txt\n> @@ -0,0 +1,82 @@\n> +1. Introduction\n> +\n> +rmnet driver is used for supporting the Multiplexing and aggregation\n> +Protocol (MAP). This protocol is used by all recent chipsets using\n> Qualcomm\n> +Technologies, Inc. modems.\n> +\n> +This driver can be used to register onto any physical network device\n> in\n> +IP mode. Physical transports include USB, HSIC, PCIe and IP\n> accelerator.\n> +\n> +Multiplexing allows for creation of logical netdevices (rmnet\n> devices) to\n> +handle multiple private data networks (PDN) like a default internet,\n> tethering,\n> +multimedia messaging service (MMS) or IP media subsystem (IMS).\n> Hardware sends\n> +packets with MAP headers to rmnet. Based on the multiplexer id,\n> rmnet\n> +routes to the appropriate PDN after removing the MAP header.\n> +\n> +Aggregation is required to achieve high data rates. This involves\n> hardware\n> +sending aggregated bunch of MAP frames. rmnet driver will de-\n> aggregate\n> +these MAP frames and send them to appropriate PDN's.\n> +\n> +2. Packet format\n> +\n> +a. MAP packet (data / control)\n> +\n> +MAP header has the same endianness of the IP packet.\n> +\n> +Packet format -\n> +\n> +Bit             0             1           2-7      8 -\n> 15           16 - 31\n> +Function   Command / Data   Reserved     Pad   Multiplexer\n> ID    Payload length\n> +Bit            32 - x\n> +Function     Raw  Bytes\n> +\n> +Command (1)/ Data (0) bit value is to indicate if the packet is a\n> MAP command\n> +or data packet. Control packet is used for transport level flow\n> control. Data\n> +packets are standard IP packets.\n> +\n> +Reserved bits are usually zeroed out and to be ignored by receiver.\n> +\n> +Padding is number of bytes to be added for 4 byte alignment if\n> required by\n> +hardware.\n> +\n> +Multiplexer ID is to indicate the PDN on which data has to be sent.\n> +\n> +Payload length includes the padding length but does not include MAP\n> header\n> +length.\n> +\n> +b. MAP packet (command specific)\n> +\n> +Bit             0             1           2-7      8 -\n> 15           16 - 31\n> +Function   Command         Reserved     Pad   Multiplexer\n> ID    Payload length\n> +Bit          32 - 39        40 - 45    46 - 47       48 - 63\n> +Function   Command name    Reserved   Command Type   Reserved\n> +Bit          64 - 95\n> +Function   Transaction ID\n> +Bit          96 - 127\n> +Function   Command data\n> +\n> +Command 1 indicates disabling flow while 2 is enabling flow\n> +\n> +Command types -\n> +0 for MAP command request\n> +1 is to acknowledge the receipt of a command\n> +2 is for unsupported commands\n> +3 is for error during processing of commands\n> +\n> +c. Aggregation\n> +\n> +Aggregation is multiple MAP packets (can be data or command)\n> delivered to\n> +rmnet in a single linear skb. rmnet will process the individual\n> +packets and either ACK the MAP command or deliver the IP packet to\n> the\n> +network stack as needed\n> +\n> +MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional\n> padding....\n> +MAP header|IP Packet|Optional padding|MAP header|Command\n> Packet|Optional pad...\n> +\n> +3. Userspace configuration\n> +\n> +rmnet userspace configuration is done through netlink library\n> librmnetctl\n> +and command line utility rmnetcli. Utility is hosted in codeaurora\n> forum git.\n> +The driver uses rtnl_link_ops for communication.\n> +\n> +https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensourc\n> e/dataservices/tree/rmnetctl\n> diff --git a/drivers/net/ethernet/qualcomm/Kconfig\n> b/drivers/net/ethernet/qualcomm/Kconfig\n> index 877675a..f520071 100644\n> --- a/drivers/net/ethernet/qualcomm/Kconfig\n> +++ b/drivers/net/ethernet/qualcomm/Kconfig\n> @@ -59,4 +59,6 @@ config QCOM_EMAC\n>  \t  low power, Receive-Side Scaling (RSS), and IEEE 1588-2008\n>  \t  Precision Clock Synchronization Protocol.\n>  \n> +source \"drivers/net/ethernet/qualcomm/rmnet/Kconfig\"\n> +\n>  endif # NET_VENDOR_QUALCOMM\n> diff --git a/drivers/net/ethernet/qualcomm/Makefile\n> b/drivers/net/ethernet/qualcomm/Makefile\n> index 92fa7c4..1847350 100644\n> --- a/drivers/net/ethernet/qualcomm/Makefile\n> +++ b/drivers/net/ethernet/qualcomm/Makefile\n> @@ -9,3 +9,5 @@ obj-$(CONFIG_QCA7000_UART) += qcauart.o\n>  qcauart-objs := qca_uart.o\n>  \n>  obj-y += emac/\n> +\n> +obj-$(CONFIG_RMNET) += rmnet/\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/Kconfig\n> b/drivers/net/ethernet/qualcomm/rmnet/Kconfig\n> new file mode 100644\n> index 0000000..6e2587a\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/Kconfig\n> @@ -0,0 +1,12 @@\n> +#\n> +# RMNET MAP driver\n> +#\n> +\n> +menuconfig RMNET\n> +\ttristate \"RmNet MAP driver\"\n> +\tdefault n\n> +\t---help---\n> +\t  If you select this, you will enable the RMNET module which\n> is used\n> +\t  for handling data in the multiplexing and aggregation\n> protocol (MAP)\n> +\t  format in the embedded data path. RMNET devices can be\n> attached to\n> +\t  any IP mode physical device.\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/Makefile\n> b/drivers/net/ethernet/qualcomm/rmnet/Makefile\n> new file mode 100644\n> index 0000000..01bddf2\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/Makefile\n> @@ -0,0 +1,10 @@\n> +#\n> +# Makefile for the RMNET module\n> +#\n> +\n> +rmnet-y\t\t := rmnet_config.o\n> +rmnet-y\t\t += rmnet_vnd.o\n> +rmnet-y\t\t += rmnet_handlers.o\n> +rmnet-y\t\t += rmnet_map_data.o\n> +rmnet-y\t\t += rmnet_map_command.o\n> +obj-$(CONFIG_RMNET) += rmnet.o\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c\n> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c\n> new file mode 100644\n> index 0000000..e836d26\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c\n> @@ -0,0 +1,419 @@\n> +/* Copyright (c) 2013-2017, The Linux Foundation. All rights\n> reserved.\n> + *\n> + * This program is free software; you can redistribute it and/or\n> modify\n> + * it under the terms of the GNU General Public License version 2\n> and\n> + * only version 2 as published by the Free Software Foundation.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * RMNET configuration engine\n> + *\n> + */\n> +\n> +#include <net/sock.h>\n> +#include <linux/module.h>\n> +#include <linux/netlink.h>\n> +#include <linux/netdevice.h>\n> +#include \"rmnet_config.h\"\n> +#include \"rmnet_handlers.h\"\n> +#include \"rmnet_vnd.h\"\n> +#include \"rmnet_private.h\"\n> +\n> +/* Locking scheme -\n> + * The shared resource which needs to be protected is realdev-\n> >rx_handler_data.\n> + * For the writer path, this is using rtnl_lock(). The writer paths\n> are\n> + * rmnet_newlink(), rmnet_dellink() and\n> rmnet_force_unassociate_device(). These\n> + * paths are already called with rtnl_lock() acquired in. There is\n> also an\n> + * ASSERT_RTNL() to ensure that we are calling with rtnl acquired.\n> For\n> + * dereference here, we will need to use rtnl_dereference(). Dev\n> list writing\n> + * needs to happen with rtnl_lock() acquired for\n> netdev_master_upper_dev_link().\n> + * For the reader path, the real_dev->rx_handler_data is called in\n> the TX / RX\n> + * path. We only need rcu_read_lock() for these scenarios. In these\n> cases,\n> + * the rcu_read_lock() is held in __dev_queue_xmit() and\n> + * netif_receive_skb_internal(), so readers need to use\n> rcu_dereference_rtnl()\n> + * to get the relevant information. For dev list reading, we again\n> acquire\n> + * rcu_read_lock() in rmnet_dellink() for\n> netdev_master_upper_dev_get_rcu().\n> + * We also use unregister_netdevice_many() to free all rmnet devices\n> in\n> + * rmnet_force_unassociate_device() so we dont lose the rtnl_lock()\n> and free in\n> + * same context.\n> + */\n> +\n> +/* Local Definitions and Declarations */\n> +#define RMNET_LOCAL_LOGICAL_ENDPOINT -1\n> +\n> +struct rmnet_walk_data {\n> +\tstruct net_device *real_dev;\n> +\tstruct list_head *head;\n> +\tstruct rmnet_real_dev_info *real_dev_info;\n> +};\n> +\n> +static int rmnet_is_real_dev_registered(const struct net_device\n> *real_dev)\n> +{\n> +\trx_handler_func_t *rx_handler;\n> +\n> +\trx_handler = rcu_dereference(real_dev->rx_handler);\n> +\treturn (rx_handler == rmnet_rx_handler);\n> +}\n> +\n> +/* Needs either rcu_read_lock() or rtnl lock */\n> +static struct rmnet_real_dev_info*\n> +__rmnet_get_real_dev_info(const struct net_device *real_dev)\n> +{\n> +\tif (rmnet_is_real_dev_registered(real_dev))\n> +\t\treturn rcu_dereference_rtnl(real_dev-\n> >rx_handler_data);\n> +\telse\n> +\t\treturn NULL;\n> +}\n> +\n> +/* Needs rtnl lock */\n> +static struct rmnet_real_dev_info*\n> +rmnet_get_real_dev_info_rtnl(const struct net_device *real_dev)\n> +{\n> +\treturn rtnl_dereference(real_dev->rx_handler_data);\n> +}\n> +\n> +static struct rmnet_endpoint*\n> +rmnet_get_endpoint(struct net_device *dev, int config_id)\n> +{\n> +\tstruct rmnet_real_dev_info *r;\n> +\tstruct rmnet_endpoint *ep;\n> +\n> +\tif (!rmnet_is_real_dev_registered(dev)) {\n> +\t\tep = rmnet_vnd_get_endpoint(dev);\n> +\t} else {\n> +\t\tr = __rmnet_get_real_dev_info(dev);\n> +\n> +\t\tif (!r)\n> +\t\t\treturn NULL;\n> +\n> +\t\tif (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT)\n> +\t\t\tep = &r->local_ep;\n> +\t\telse\n> +\t\t\tep = &r->muxed_ep[config_id];\n> +\t}\n> +\n> +\treturn ep;\n> +}\n> +\n> +static int rmnet_unregister_real_device(struct net_device *real_dev,\n> +\t\t\t\t\tstruct rmnet_real_dev_info\n> *r)\n> +{\n> +\tif (r->nr_rmnet_devs)\n> +\t\treturn -EINVAL;\n> +\n> +\tkfree(r);\n> +\n> +\tnetdev_rx_handler_unregister(real_dev);\n> +\n> +\t/* release reference on real_dev */\n> +\tdev_put(real_dev);\n> +\n> +\tnetdev_dbg(real_dev, \"Removed from rmnet\\n\");\n> +\treturn 0;\n> +}\n> +\n> +static int rmnet_register_real_device(struct net_device *real_dev)\n> +{\n> +\tstruct rmnet_real_dev_info *r;\n> +\tint rc;\n> +\n> +\tASSERT_RTNL();\n> +\n> +\tif (rmnet_is_real_dev_registered(real_dev))\n> +\t\treturn 0;\n> +\n> +\tr = kzalloc(sizeof(*r), GFP_ATOMIC);\n> +\tif (!r)\n> +\t\treturn -ENOMEM;\n> +\n> +\tr->dev = real_dev;\n> +\trc = netdev_rx_handler_register(real_dev, rmnet_rx_handler,\n> r);\n> +\tif (rc) {\n> +\t\tkfree(r);\n> +\t\treturn -EBUSY;\n> +\t}\n> +\n> +\t/* hold on to real dev for MAP data */\n> +\tdev_hold(real_dev);\n> +\n> +\tnetdev_dbg(real_dev, \"registered with rmnet\\n\");\n> +\treturn 0;\n> +}\n> +\n> +static int rmnet_set_ingress_data_format(struct net_device *dev, u32\n> idf)\n> +{\n> +\tstruct rmnet_real_dev_info *r;\n> +\n> +\tnetdev_dbg(dev, \"Ingress format 0x%08X\\n\", idf);\n> +\n> +\tr = __rmnet_get_real_dev_info(dev);\n> +\n> +\tr->ingress_data_format = idf;\n> +\n> +\treturn 0;\n> +}\n> +\n> +static int rmnet_set_egress_data_format(struct net_device *dev, u32\n> edf,\n> +\t\t\t\t\tu16 agg_size, u16 agg_count)\n> +{\n> +\tstruct rmnet_real_dev_info *r;\n> +\n> +\tnetdev_dbg(dev, \"Egress format 0x%08X agg size %d cnt %d\\n\",\n> +\t\t   edf, agg_size, agg_count);\n> +\n> +\tr = __rmnet_get_real_dev_info(dev);\n> +\n> +\tr->egress_data_format = edf;\n> +\n> +\treturn 0;\n> +}\n> +\n> +static int __rmnet_set_endpoint_config(struct net_device *dev, int\n> config_id,\n> +\t\t\t\t       struct rmnet_endpoint *ep)\n> +{\n> +\tstruct rmnet_endpoint *dev_ep;\n> +\n> +\tdev_ep = rmnet_get_endpoint(dev, config_id);\n> +\n> +\tif (!dev_ep)\n> +\t\treturn -EINVAL;\n> +\n> +\tmemcpy(dev_ep, ep, sizeof(struct rmnet_endpoint));\n> +\tif (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT)\n\nMaybe this got elided during the revisions, but now I can't find\nanywhere that sets RMNET_LOCAL_LOGICAL_ENDPOINT.  Looking at the\ncallchain, there are two places that LOCAL_LOGICAL_ENDPOINT matters:\n\nrmnet_get_endpoint(): only ever called by __rmnet_set_endpoint_config()\n\n__rmnet_set_endpoint_config(): only called from\nrmnet_set_endpoint_config(); which itself is only called from\nrmnet_newlink().\n\nSo the only place that 'config_id' is set, and thus that it could be\nLOCAL_LOGICAL_ENDPOINT, is rmnet_newlink() via 'mux_id'.  But\nIFLA_VLAN_ID is a u16, and so I don't see anywhere that\nconfig_id/mux_id will ever be < 0, and thus anywhere that it could be\nLOCAL_LOGICAL_ENDPOINT.\n\nI could well just not be seeing it though...\n\n> +\t\tdev_ep->mux_id = 0;\n> +\telse\n> +\t\tdev_ep->mux_id = config_id;\n> +\n> +\treturn 0;\n> +}\n\nThis function (__rmnet_set_endpoint_config) seems to only be called\nfrom rmnet_set_endpoint_config().  Perhaps just combine them?\n\nBut that brings up another point; can the rmnet \"mode\" or egress_dev\nchange at runtime, after the rmnet child has been created?  I forget if\nthat was possible with your original patchset that used ioctls.\n\n> +static int rmnet_set_endpoint_config(struct net_device *dev,\n> +\t\t\t\t     int config_id, u8 rmnet_mode,\n> +\t\t\t\t     struct net_device *egress_dev)\n> +{\n> +\tstruct rmnet_endpoint ep;\n> +\n> +\tnetdev_dbg(dev, \"id %d mode %d dev %s\\n\",\n> +\t\t   config_id, rmnet_mode, egress_dev->name);\n> +\n> +\tif (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT ||\n> +\t    config_id >= RMNET_MAX_LOGICAL_EP)\n> +\t\treturn -EINVAL;\n> +\n> +\t/* This config is cleared on every set, so its ok to not\n> +\t * clear it on a device delete.\n> +\t */\n> +\tmemset(&ep, 0, sizeof(struct rmnet_endpoint));\n> +\tep.rmnet_mode = rmnet_mode;\n> +\tep.egress_dev = egress_dev;\n> +\n> +\treturn __rmnet_set_endpoint_config(dev, config_id, &ep);\n> +}\n> +\n> +static int rmnet_newlink(struct net *src_net, struct net_device\n> *dev,\n> +\t\t\t struct nlattr *tb[], struct nlattr *data[],\n> +\t\t\t struct netlink_ext_ack *extack)\n> +{\n> +\tint ingress_format = RMNET_INGRESS_FORMAT_DEMUXING |\n> +\t\t\t     RMNET_INGRESS_FORMAT_DEAGGREGATION |\n> +\t\t\t     RMNET_INGRESS_FORMAT_MAP;\n> +\tint egress_format = RMNET_EGRESS_FORMAT_MUXING |\n> +\t\t\t    RMNET_EGRESS_FORMAT_MAP;\n> +\tstruct rmnet_real_dev_info *r;\n> +\tstruct net_device *real_dev;\n> +\tint mode = RMNET_EPMODE_VND;\n> +\tint err = 0;\n> +\tu16 mux_id;\n> +\n> +\treal_dev = __dev_get_by_index(src_net,\n> nla_get_u32(tb[IFLA_LINK]));\n> +\tif (!real_dev || !dev)\n> +\t\treturn -ENODEV;\n> +\n> +\tif (!data[IFLA_VLAN_ID])\n> +\t\treturn -EINVAL;\n> +\n> +\tmux_id = nla_get_u16(data[IFLA_VLAN_ID]);\n> +\n> +\terr = rmnet_register_real_device(real_dev);\n> +\tif (err)\n> +\t\tgoto err0;\n> +\n> +\tr = rmnet_get_real_dev_info_rtnl(real_dev);\n> +\terr = rmnet_vnd_newlink(mux_id, dev, r);\n> +\tif (err)\n> +\t\tgoto err1;\n> +\n> +\terr = netdev_master_upper_dev_link(dev, real_dev, NULL,\n> NULL);\n> +\tif (err)\n> +\t\tgoto err2;\n> +\n> +\trmnet_vnd_set_mux(dev, mux_id);\n\nWhy not set the mux_id in rmnet_vnd_newlink()?\n\nAlso, bigger problem.  r->rmnet_devices[] is only 32 items in size. \nBut mux_id (which is used as an index into rmnet_devices in a few\nplaces) can be up to 255 (RMNET_MAX_LOGICAL_EP).\n\nSo if you try to create an rmnet for mux ID 32, you panic the kernel. \nSee below my comments about rmnet_real_dev_info...\n\n> +\trmnet_set_egress_data_format(real_dev, egress_format, 0, 0);\n> +\trmnet_set_ingress_data_format(real_dev, ingress_format);\n\nI can't see anywhere that the egress/ingress data get set except for\nthis function, so perhaps you could just skip these functions and\n(since you already have 'r' from above) set r-\n>[egress|ingress]_data_format directly?\n\n> +\trmnet_set_endpoint_config(real_dev, mux_id, mode, dev);\n> +\trmnet_set_endpoint_config(dev, mux_id, mode, real_dev);\n> +\treturn 0;\n> +\n> +err2:\n> +\trmnet_vnd_dellink(mux_id, r);\n> +err1:\n> +\trmnet_unregister_real_device(real_dev, r);\n> +err0:\n> +\treturn err;\n> +}\n> +\n> +static void rmnet_dellink(struct net_device *dev, struct list_head\n> *head)\n> +{\n> +\tstruct rmnet_real_dev_info *r;\n> +\tstruct net_device *real_dev;\n> +\tu8 mux_id;\n> +\n> +\trcu_read_lock();\n> +\treal_dev = netdev_master_upper_dev_get_rcu(dev);\n> +\trcu_read_unlock();\n> +\n> +\tif (!real_dev || !rmnet_is_real_dev_registered(real_dev))\n> +\t\treturn;\n> +\n> +\tr = rmnet_get_real_dev_info_rtnl(real_dev);\n> +\n> +\tmux_id = rmnet_vnd_get_mux(dev);\n> +\trmnet_vnd_dellink(mux_id, r);\n> +\tnetdev_upper_dev_unlink(dev, real_dev);\n> +\trmnet_unregister_real_device(real_dev, r);\n> +\n> +\tunregister_netdevice_queue(dev, head);\n> +}\n> +\n> +static int rmnet_dev_walk_unreg(struct net_device *rmnet_dev, void\n> *data)\n> +{\n> +\tstruct rmnet_walk_data *d = data;\n> +\tu8 mux_id;\n> +\n> +\tmux_id = rmnet_vnd_get_mux(rmnet_dev);\n> +\n> +\trmnet_vnd_dellink(mux_id, d->real_dev_info);\n> +\tnetdev_upper_dev_unlink(rmnet_dev, d->real_dev);\n> +\tunregister_netdevice_queue(rmnet_dev, d->head);\n> +\n> +\treturn 0;\n> +}\n> +\n> +static void rmnet_force_unassociate_device(struct net_device *dev)\n> +{\n> +\tstruct net_device *real_dev = dev;\n> +\tstruct rmnet_real_dev_info *r;\n> +\tstruct rmnet_walk_data d;\n> +\tLIST_HEAD(list);\n> +\n> +\tif (!rmnet_is_real_dev_registered(real_dev))\n> +\t\treturn;\n> +\n> +\tASSERT_RTNL();\n> +\n> +\td.real_dev = real_dev;\n> +\td.head = &list;\n> +\n> +\tr = rmnet_get_real_dev_info_rtnl(dev);\n> +\td.real_dev_info = r;\n> +\n> +\trcu_read_lock();\n> +\tnetdev_walk_all_lower_dev_rcu(real_dev,\n> rmnet_dev_walk_unreg, &d);\n> +\trcu_read_unlock();\n> +\tunregister_netdevice_many(&list);\n> +\n> +\trmnet_unregister_real_device(real_dev, r);\n> +}\n> +\n> +static int rmnet_config_notify_cb(struct notifier_block *nb,\n> +\t\t\t\t  unsigned long event, void *data)\n> +{\n> +\tstruct net_device *dev = netdev_notifier_info_to_dev(data);\n> +\n> +\tif (!dev)\n> +\t\treturn NOTIFY_DONE;\n> +\n> +\tswitch (event) {\n> +\tcase NETDEV_UNREGISTER:\n> +\t\tnetdev_dbg(dev, \"Kernel unregister\\n\");\n> +\t\trmnet_force_unassociate_device(dev);\n> +\t\tbreak;\n> +\n> +\tdefault:\n> +\t\tbreak;\n> +\t}\n> +\n> +\treturn NOTIFY_DONE;\n> +}\n> +\n> +static struct notifier_block rmnet_dev_notifier __read_mostly = {\n> +\t.notifier_call = rmnet_config_notify_cb,\n> +};\n> +\n> +static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr\n> *data[],\n> +\t\t\t       struct netlink_ext_ack *extack)\n> +{\n> +\tu16 mux_id;\n> +\n> +\tif (!data || !data[IFLA_VLAN_ID])\n> +\t\treturn -EINVAL;\n> +\n> +\tmux_id = nla_get_u16(data[IFLA_VLAN_ID]);\n> +\tif (mux_id > (RMNET_MAX_LOGICAL_EP - 1))\n> +\t\treturn -ERANGE;\n> +\n> +\treturn 0;\n> +}\n> +\n> +static size_t rmnet_get_size(const struct net_device *dev)\n> +{\n> +\treturn nla_total_size(2); /* IFLA_VLAN_ID */\n> +}\n> +\n> +struct rtnl_link_ops rmnet_link_ops __read_mostly = {\n> +\t.kind\t\t= \"rmnet\",\n> +\t.maxtype\t= __IFLA_VLAN_MAX,\n> +\t.priv_size\t= sizeof(struct rmnet_priv),\n> +\t.setup\t\t= rmnet_vnd_setup,\n> +\t.validate\t= rmnet_rtnl_validate,\n> +\t.newlink\t= rmnet_newlink,\n> +\t.dellink\t= rmnet_dellink,\n> +\t.get_size\t= rmnet_get_size,\n> +};\n> +\n> +struct rmnet_real_dev_info*\n> +rmnet_get_real_dev_info(struct net_device *real_dev)\n> +{\n> +\treturn __rmnet_get_real_dev_info(real_dev);\n> +}\n> +\n> +/* Startup/Shutdown */\n> +\n> +static int __init rmnet_init(void)\n> +{\n> +\tint rc;\n> +\n> +\trc = register_netdevice_notifier(&rmnet_dev_notifier);\n> +\tif (rc != 0)\n> +\t\treturn rc;\n> +\n> +\trc = rtnl_link_register(&rmnet_link_ops);\n> +\tif (rc != 0) {\n> +\t\tunregister_netdevice_notifier(&rmnet_dev_notifier);\n> +\t\treturn rc;\n> +\t}\n> +\treturn rc;\n> +}\n> +\n> +static void __exit rmnet_exit(void)\n> +{\n> +\tunregister_netdevice_notifier(&rmnet_dev_notifier);\n> +\trtnl_link_unregister(&rmnet_link_ops);\n> +}\n> +\n> +module_init(rmnet_init)\n> +module_exit(rmnet_exit)\n> +MODULE_LICENSE(\"GPL v2\");\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h\n> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h\n> new file mode 100644\n> index 0000000..985d372\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h\n> @@ -0,0 +1,56 @@\n> +/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All\n> rights reserved.\n> + *\n> + * This program is free software; you can redistribute it and/or\n> modify\n> + * it under the terms of the GNU General Public License version 2\n> and\n> + * only version 2 as published by the Free Software Foundation.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * RMNET Data configuration engine\n> + *\n> + */\n> +\n> +#include <linux/skbuff.h>\n> +\n> +#ifndef _RMNET_CONFIG_H_\n> +#define _RMNET_CONFIG_H_\n> +\n> +#define RMNET_MAX_LOGICAL_EP 255\n> +#define RMNET_MAX_VND        32\n> +\n> +/* Information about the next device to deliver the packet to.\n> + * Exact usage of this parameter depends on the rmnet_mode.\n> + */\n> +struct rmnet_endpoint {\n> +\tu8 rmnet_mode;\n> +\tu8 mux_id;\n> +\tstruct net_device *egress_dev;\n> +};\n> +\n> +/* One instance of this structure is instantiated for each real_dev\n> associated\n> + * with rmnet.\n> + */\n> +struct rmnet_real_dev_info {\n> +\tstruct net_device *dev;\n> +\tstruct rmnet_endpoint local_ep;\n> +\tstruct rmnet_endpoint muxed_ep[RMNET_MAX_LOGICAL_EP];\n\nThis means that the first time you add an rmnet dev to a netdev, it'll\ncreate a structure that's quite large (at least 255 * 6, but more due\nto padding), when in most cases few of these items will be used.  Most\nof the time you'd have only a couple PDNs active, but this will\nallocate memory for MAX_LOGICAL_EP of them, no?\n\nipvlan uses a list to track the child devices attached to a physical\ndevice so that it doesn't have to allocate them all at once and waste\nmemory; that technique could replace the 'rmnet_devices' member below.\n\nIt also uses a hash to find the actual ipvlan upperdev from the\nrx_handler of the lowerdev, which is probably what would replace\nmuxed_ep[] here.\n\nIs the relationship between rmnet \"child\"/upper devs and mux_ids 1:1? \nOr can you have multiple rmnet devs for the same mux_id?\n\nDan\n\n> +\tu32 ingress_data_format;\n> +\tu32 egress_data_format;\n> +\tstruct net_device *rmnet_devices[RMNET_MAX_VND];\n> +\tu8 nr_rmnet_devs;\n> +};\n> +\n> +extern struct rtnl_link_ops rmnet_link_ops;\n> +\n> +struct rmnet_priv {\n> +\tstruct rmnet_endpoint local_ep;\n> +\tu8 mux_id;\n> +};\n> +\n> +struct rmnet_real_dev_info*\n> +rmnet_get_real_dev_info(struct net_device *real_dev);\n> +\n> +#endif /* _RMNET_CONFIG_H_ */\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c\n> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c\n> new file mode 100644\n> index 0000000..7dab3bb\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c\n> @@ -0,0 +1,271 @@\n> +/* Copyright (c) 2013-2017, The Linux Foundation. All rights\n> reserved.\n> + *\n> + * This program is free software; you can redistribute it and/or\n> modify\n> + * it under the terms of the GNU General Public License version 2\n> and\n> + * only version 2 as published by the Free Software Foundation.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * RMNET Data ingress/egress handler\n> + *\n> + */\n> +\n> +#include <linux/netdevice.h>\n> +#include <linux/netdev_features.h>\n> +#include \"rmnet_private.h\"\n> +#include \"rmnet_config.h\"\n> +#include \"rmnet_vnd.h\"\n> +#include \"rmnet_map.h\"\n> +#include \"rmnet_handlers.h\"\n> +\n> +#define RMNET_IP_VERSION_4 0x40\n> +#define RMNET_IP_VERSION_6 0x60\n> +\n> +/* Helper Functions */\n> +\n> +static void rmnet_set_skb_proto(struct sk_buff *skb)\n> +{\n> +\tswitch (skb->data[0] & 0xF0) {\n> +\tcase RMNET_IP_VERSION_4:\n> +\t\tskb->protocol = htons(ETH_P_IP);\n> +\t\tbreak;\n> +\tcase RMNET_IP_VERSION_6:\n> +\t\tskb->protocol = htons(ETH_P_IPV6);\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tskb->protocol = htons(ETH_P_MAP);\n> +\t\tbreak;\n> +\t}\n> +}\n> +\n> +/* Generic handler */\n> +\n> +static rx_handler_result_t\n> +rmnet_bridge_handler(struct sk_buff *skb, struct rmnet_endpoint *ep)\n> +{\n> +\tif (!ep->egress_dev)\n> +\t\tkfree_skb(skb);\n> +\telse\n> +\t\trmnet_egress_handler(skb, ep);\n> +\n> +\treturn RX_HANDLER_CONSUMED;\n> +}\n> +\n> +static rx_handler_result_t\n> +rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_endpoint *ep)\n> +{\n> +\tswitch (ep->rmnet_mode) {\n> +\tcase RMNET_EPMODE_NONE:\n> +\t\treturn RX_HANDLER_PASS;\n> +\n> +\tcase RMNET_EPMODE_BRIDGE:\n> +\t\treturn rmnet_bridge_handler(skb, ep);\n> +\n> +\tcase RMNET_EPMODE_VND:\n> +\t\tskb_reset_transport_header(skb);\n> +\t\tskb_reset_network_header(skb);\n> +\t\trmnet_vnd_rx_fixup(skb, skb->dev);\n> +\n> +\t\tskb->pkt_type = PACKET_HOST;\n> +\t\tskb_set_mac_header(skb, 0);\n> +\t\tnetif_receive_skb(skb);\n> +\t\treturn RX_HANDLER_CONSUMED;\n> +\n> +\tdefault:\n> +\t\tkfree_skb(skb);\n> +\t\treturn RX_HANDLER_CONSUMED;\n> +\t}\n> +}\n> +\n> +static rx_handler_result_t\n> +rmnet_ingress_deliver_packet(struct sk_buff *skb,\n> +\t\t\t     struct rmnet_real_dev_info *r)\n> +{\n> +\tif (!r) {\n> +\t\tkfree_skb(skb);\n> +\t\treturn RX_HANDLER_CONSUMED;\n> +\t}\n> +\n> +\tskb->dev = r->local_ep.egress_dev;\n> +\n> +\treturn rmnet_deliver_skb(skb, &r->local_ep);\n> +}\n> +\n> +/* MAP handler */\n> +\n> +static rx_handler_result_t\n> +__rmnet_map_ingress_handler(struct sk_buff *skb,\n> +\t\t\t    struct rmnet_real_dev_info *r)\n> +{\n> +\tstruct rmnet_endpoint *ep;\n> +\tu8 mux_id;\n> +\tu16 len;\n> +\n> +\tif (RMNET_MAP_GET_CD_BIT(skb)) {\n> +\t\tif (r->ingress_data_format\n> +\t\t    & RMNET_INGRESS_FORMAT_MAP_COMMANDS)\n> +\t\t\treturn rmnet_map_command(skb, r);\n> +\n> +\t\tkfree_skb(skb);\n> +\t\treturn RX_HANDLER_CONSUMED;\n> +\t}\n> +\n> +\tmux_id = RMNET_MAP_GET_MUX_ID(skb);\n> +\tlen = RMNET_MAP_GET_LENGTH(skb) - RMNET_MAP_GET_PAD(skb);\n> +\n> +\tif (mux_id >= RMNET_MAX_LOGICAL_EP) {\n> +\t\tkfree_skb(skb);\n> +\t\treturn RX_HANDLER_CONSUMED;\n> +\t}\n> +\n> +\tep = &r->muxed_ep[mux_id];\n> +\n> +\tif (r->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING)\n> +\t\tskb->dev = ep->egress_dev;\n> +\n> +\t/* Subtract MAP header */\n> +\tskb_pull(skb, sizeof(struct rmnet_map_header));\n> +\tskb_trim(skb, len);\n> +\trmnet_set_skb_proto(skb);\n> +\treturn rmnet_deliver_skb(skb, ep);\n> +}\n> +\n> +static rx_handler_result_t\n> +rmnet_map_ingress_handler(struct sk_buff *skb,\n> +\t\t\t  struct rmnet_real_dev_info *r)\n> +{\n> +\tstruct sk_buff *skbn;\n> +\tint rc;\n> +\n> +\tif (r->ingress_data_format &\n> RMNET_INGRESS_FORMAT_DEAGGREGATION) {\n> +\t\twhile ((skbn = rmnet_map_deaggregate(skb, r)) !=\n> NULL)\n> +\t\t\t__rmnet_map_ingress_handler(skbn, r);\n> +\n> +\t\tconsume_skb(skb);\n> +\t\trc = RX_HANDLER_CONSUMED;\n> +\t} else {\n> +\t\trc = __rmnet_map_ingress_handler(skb, r);\n> +\t}\n> +\n> +\treturn rc;\n> +}\n> +\n> +static int rmnet_map_egress_handler(struct sk_buff *skb,\n> +\t\t\t\t    struct rmnet_real_dev_info *r,\n> +\t\t\t\t    struct rmnet_endpoint *ep,\n> +\t\t\t\t    struct net_device *orig_dev)\n> +{\n> +\tint required_headroom, additional_header_len;\n> +\tstruct rmnet_map_header *map_header;\n> +\n> +\tadditional_header_len = 0;\n> +\trequired_headroom = sizeof(struct rmnet_map_header);\n> +\n> +\tif (skb_headroom(skb) < required_headroom) {\n> +\t\tif (pskb_expand_head(skb, required_headroom, 0,\n> GFP_KERNEL))\n> +\t\t\treturn RMNET_MAP_CONSUMED;\n> +\t}\n> +\n> +\tmap_header = rmnet_map_add_map_header(skb,\n> additional_header_len, 0);\n> +\tif (!map_header)\n> +\t\treturn RMNET_MAP_CONSUMED;\n> +\n> +\tif (r->egress_data_format & RMNET_EGRESS_FORMAT_MUXING) {\n> +\t\tif (ep->mux_id == 0xff)\n> +\t\t\tmap_header->mux_id = 0;\n> +\t\telse\n> +\t\t\tmap_header->mux_id = ep->mux_id;\n> +\t}\n> +\n> +\tskb->protocol = htons(ETH_P_MAP);\n> +\n> +\treturn RMNET_MAP_SUCCESS;\n> +}\n> +\n> +/* Ingress / Egress Entry Points */\n> +\n> +/* Processes packet as per ingress data format for receiving device.\n> Logical\n> + * endpoint is determined from packet inspection. Packet is then\n> sent to the\n> + * egress device listed in the logical endpoint configuration.\n> + */\n> +rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb)\n> +{\n> +\tstruct rmnet_real_dev_info *r;\n> +\tstruct sk_buff *skb = *pskb;\n> +\tstruct net_device *dev;\n> +\tint rc;\n> +\n> +\tif (!skb)\n> +\t\treturn RX_HANDLER_CONSUMED;\n> +\n> +\tdev = skb->dev;\n> +\tr = rmnet_get_real_dev_info(dev);\n> +\n> +\tif (r->ingress_data_format & RMNET_INGRESS_FORMAT_MAP) {\n> +\t\trc = rmnet_map_ingress_handler(skb, r);\n> +\t} else {\n> +\t\tswitch (ntohs(skb->protocol)) {\n> +\t\tcase ETH_P_MAP:\n> +\t\t\tif (r->local_ep.rmnet_mode ==\n> +\t\t\t\tRMNET_EPMODE_BRIDGE) {\n> +\t\t\t\trc =\n> rmnet_ingress_deliver_packet(skb, r);\n> +\t\t\t} else {\n> +\t\t\t\tkfree_skb(skb);\n> +\t\t\t\trc = RX_HANDLER_CONSUMED;\n> +\t\t\t}\n> +\t\t\tbreak;\n> +\n> +\t\tcase ETH_P_IP:\n> +\t\tcase ETH_P_IPV6:\n> +\t\t\trc = rmnet_ingress_deliver_packet(skb, r);\n> +\t\t\tbreak;\n> +\n> +\t\tdefault:\n> +\t\t\trc = RX_HANDLER_PASS;\n> +\t\t}\n> +\t}\n> +\n> +\treturn rc;\n> +}\n> +\n> +/* Modifies packet as per logical endpoint configuration and egress\n> data format\n> + * for egress device configured in logical endpoint. Packet is then\n> transmitted\n> + * on the egress device.\n> + */\n> +void rmnet_egress_handler(struct sk_buff *skb,\n> +\t\t\t  struct rmnet_endpoint *ep)\n> +{\n> +\tstruct rmnet_real_dev_info *r;\n> +\tstruct net_device *orig_dev;\n> +\n> +\torig_dev = skb->dev;\n> +\tskb->dev = ep->egress_dev;\n> +\n> +\tr = rmnet_get_real_dev_info(skb->dev);\n> +\tif (!r) {\n> +\t\tkfree_skb(skb);\n> +\t\treturn;\n> +\t}\n> +\n> +\tif (r->egress_data_format & RMNET_EGRESS_FORMAT_MAP) {\n> +\t\tswitch (rmnet_map_egress_handler(skb, r, ep,\n> orig_dev)) {\n> +\t\tcase RMNET_MAP_CONSUMED:\n> +\t\t\treturn;\n> +\n> +\t\tcase RMNET_MAP_SUCCESS:\n> +\t\t\tbreak;\n> +\n> +\t\tdefault:\n> +\t\t\tkfree_skb(skb);\n> +\t\t\treturn;\n> +\t\t}\n> +\t}\n> +\n> +\tif (ep->rmnet_mode == RMNET_EPMODE_VND)\n> +\t\trmnet_vnd_tx_fixup(skb, orig_dev);\n> +\n> +\tdev_queue_xmit(skb);\n> +}\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h\n> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h\n> new file mode 100644\n> index 0000000..f2638cf\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h\n> @@ -0,0 +1,26 @@\n> +/* Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights\n> reserved.\n> + *\n> + * This program is free software; you can redistribute it and/or\n> modify\n> + * it under the terms of the GNU General Public License version 2\n> and\n> + * only version 2 as published by the Free Software Foundation.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * RMNET Data ingress/egress handler\n> + *\n> + */\n> +\n> +#ifndef _RMNET_HANDLERS_H_\n> +#define _RMNET_HANDLERS_H_\n> +\n> +#include \"rmnet_config.h\"\n> +\n> +void rmnet_egress_handler(struct sk_buff *skb,\n> +\t\t\t  struct rmnet_endpoint *ep);\n> +\n> +rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb);\n> +\n> +#endif /* _RMNET_HANDLERS_H_ */\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h\n> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h\n> new file mode 100644\n> index 0000000..2aabad2\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h\n> @@ -0,0 +1,88 @@\n> +/* Copyright (c) 2013-2017, The Linux Foundation. All rights\n> reserved.\n> + *\n> + * This program is free software; you can redistribute it and/or\n> modify\n> + * it under the terms of the GNU General Public License version 2\n> and\n> + * only version 2 as published by the Free Software Foundation.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + */\n> +\n> +#ifndef _RMNET_MAP_H_\n> +#define _RMNET_MAP_H_\n> +\n> +struct rmnet_map_control_command {\n> +\tu8  command_name;\n> +\tu8  cmd_type:2;\n> +\tu8  reserved:6;\n> +\tu16 reserved2;\n> +\tu32 transaction_id;\n> +\tunion {\n> +\t\tstruct {\n> +\t\t\tu16 ip_family:2;\n> +\t\t\tu16 reserved:14;\n> +\t\t\tu16 flow_control_seq_num;\n> +\t\t\tu32 qos_id;\n> +\t\t} flow_control;\n> +\t\tu8 data[0];\n> +\t};\n> +}  __aligned(1);\n> +\n> +enum rmnet_map_results {\n> +\tRMNET_MAP_SUCCESS,\n> +\tRMNET_MAP_CONSUMED,\n> +\tRMNET_MAP_GENERAL_FAILURE,\n> +\tRMNET_MAP_NOT_ENABLED,\n> +\tRMNET_MAP_FAILED_AGGREGATION,\n> +\tRMNET_MAP_FAILED_MUX\n> +};\n> +\n> +enum rmnet_map_commands {\n> +\tRMNET_MAP_COMMAND_NONE,\n> +\tRMNET_MAP_COMMAND_FLOW_DISABLE,\n> +\tRMNET_MAP_COMMAND_FLOW_ENABLE,\n> +\t/* These should always be the last 2 elements */\n> +\tRMNET_MAP_COMMAND_UNKNOWN,\n> +\tRMNET_MAP_COMMAND_ENUM_LENGTH\n> +};\n> +\n> +struct rmnet_map_header {\n> +\tu8  pad_len:6;\n> +\tu8  reserved_bit:1;\n> +\tu8  cd_bit:1;\n> +\tu8  mux_id;\n> +\tu16 pkt_len;\n> +}  __aligned(1);\n> +\n> +#define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \\\n> +\t\t\t\t (Y)->data)->mux_id)\n> +#define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \\\n> +\t\t\t\t(Y)->data)->cd_bit)\n> +#define RMNET_MAP_GET_PAD(Y) (((struct rmnet_map_header *) \\\n> +\t\t\t\t(Y)->data)->pad_len)\n> +#define RMNET_MAP_GET_CMD_START(Y) ((struct\n> rmnet_map_control_command *) \\\n> +\t\t\t\t    ((Y)->data + \\\n> +\t\t\t\t      sizeof(struct\n> rmnet_map_header)))\n> +#define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header *)\n> \\\n> +\t\t\t\t\t(Y)->data)->pkt_len))\n> +\n> +#define RMNET_MAP_COMMAND_REQUEST     0\n> +#define RMNET_MAP_COMMAND_ACK         1\n> +#define RMNET_MAP_COMMAND_UNSUPPORTED 2\n> +#define RMNET_MAP_COMMAND_INVALID     3\n> +\n> +#define RMNET_MAP_NO_PAD_BYTES        0\n> +#define RMNET_MAP_ADD_PAD_BYTES       1\n> +\n> +u8 rmnet_map_demultiplex(struct sk_buff *skb);\n> +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,\n> +\t\t\t\t      struct rmnet_real_dev_info\n> *rdinfo);\n> +\n> +struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff\n> *skb,\n> +\t\t\t\t\t\t  int hdrlen, int\n> pad);\n> +rx_handler_result_t rmnet_map_command(struct sk_buff *skb,\n> +\t\t\t\t      struct rmnet_real_dev_info\n> *rdinfo);\n> +\n> +#endif /* _RMNET_MAP_H_ */\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c\n> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c\n> new file mode 100644\n> index 0000000..ccded40\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c\n> @@ -0,0 +1,107 @@\n> +/* Copyright (c) 2013-2017, The Linux Foundation. All rights\n> reserved.\n> + *\n> + * This program is free software; you can redistribute it and/or\n> modify\n> + * it under the terms of the GNU General Public License version 2\n> and\n> + * only version 2 as published by the Free Software Foundation.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + */\n> +\n> +#include <linux/netdevice.h>\n> +#include \"rmnet_config.h\"\n> +#include \"rmnet_map.h\"\n> +#include \"rmnet_private.h\"\n> +#include \"rmnet_vnd.h\"\n> +\n> +static u8 rmnet_map_do_flow_control(struct sk_buff *skb,\n> +\t\t\t\t    struct rmnet_real_dev_info\n> *rdinfo,\n> +\t\t\t\t    int enable)\n> +{\n> +\tstruct rmnet_map_control_command *cmd;\n> +\tstruct rmnet_endpoint *ep;\n> +\tstruct net_device *vnd;\n> +\tu16 ip_family;\n> +\tu16 fc_seq;\n> +\tu32 qos_id;\n> +\tu8 mux_id;\n> +\tint r;\n> +\n> +\tmux_id = RMNET_MAP_GET_MUX_ID(skb);\n> +\tcmd = RMNET_MAP_GET_CMD_START(skb);\n> +\n> +\tif (mux_id >= RMNET_MAX_LOGICAL_EP) {\n> +\t\tkfree_skb(skb);\n> +\t\treturn RX_HANDLER_CONSUMED;\n> +\t}\n> +\n> +\tep = &rdinfo->muxed_ep[mux_id];\n> +\tvnd = ep->egress_dev;\n> +\n> +\tip_family = cmd->flow_control.ip_family;\n> +\tfc_seq = ntohs(cmd->flow_control.flow_control_seq_num);\n> +\tqos_id = ntohl(cmd->flow_control.qos_id);\n> +\n> +\t/* Ignore the ip family and pass the sequence number for\n> both v4 and v6\n> +\t * sequence. User space does not support creating dedicated\n> flows for\n> +\t * the 2 protocols\n> +\t */\n> +\tr = rmnet_vnd_do_flow_control(vnd, enable);\n> +\tif (r) {\n> +\t\tkfree_skb(skb);\n> +\t\treturn RMNET_MAP_COMMAND_UNSUPPORTED;\n> +\t} else {\n> +\t\treturn RMNET_MAP_COMMAND_ACK;\n> +\t}\n> +}\n> +\n> +static void rmnet_map_send_ack(struct sk_buff *skb,\n> +\t\t\t       unsigned char type,\n> +\t\t\t       struct rmnet_real_dev_info *rdinfo)\n> +{\n> +\tstruct rmnet_map_control_command *cmd;\n> +\tint xmit_status;\n> +\n> +\tskb->protocol = htons(ETH_P_MAP);\n> +\n> +\tcmd = RMNET_MAP_GET_CMD_START(skb);\n> +\tcmd->cmd_type = type & 0x03;\n> +\n> +\tnetif_tx_lock(skb->dev);\n> +\txmit_status = skb->dev->netdev_ops->ndo_start_xmit(skb, skb-\n> >dev);\n> +\tnetif_tx_unlock(skb->dev);\n> +}\n> +\n> +/* Process MAP command frame and send N/ACK message as appropriate.\n> Message cmd\n> + * name is decoded here and appropriate handler is called.\n> + */\n> +rx_handler_result_t rmnet_map_command(struct sk_buff *skb,\n> +\t\t\t\t      struct rmnet_real_dev_info\n> *rdinfo)\n> +{\n> +\tstruct rmnet_map_control_command *cmd;\n> +\tunsigned char command_name;\n> +\tunsigned char rc = 0;\n> +\n> +\tcmd = RMNET_MAP_GET_CMD_START(skb);\n> +\tcommand_name = cmd->command_name;\n> +\n> +\tswitch (command_name) {\n> +\tcase RMNET_MAP_COMMAND_FLOW_ENABLE:\n> +\t\trc = rmnet_map_do_flow_control(skb, rdinfo, 1);\n> +\t\tbreak;\n> +\n> +\tcase RMNET_MAP_COMMAND_FLOW_DISABLE:\n> +\t\trc = rmnet_map_do_flow_control(skb, rdinfo, 0);\n> +\t\tbreak;\n> +\n> +\tdefault:\n> +\t\trc = RMNET_MAP_COMMAND_UNSUPPORTED;\n> +\t\tkfree_skb(skb);\n> +\t\tbreak;\n> +\t}\n> +\tif (rc == RMNET_MAP_COMMAND_ACK)\n> +\t\trmnet_map_send_ack(skb, rc, rdinfo);\n> +\treturn RX_HANDLER_CONSUMED;\n> +}\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c\n> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c\n> new file mode 100644\n> index 0000000..a29c476\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c\n> @@ -0,0 +1,105 @@\n> +/* Copyright (c) 2013-2017, The Linux Foundation. All rights\n> reserved.\n> + *\n> + * This program is free software; you can redistribute it and/or\n> modify\n> + * it under the terms of the GNU General Public License version 2\n> and\n> + * only version 2 as published by the Free Software Foundation.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * RMNET Data MAP protocol\n> + *\n> + */\n> +\n> +#include <linux/netdevice.h>\n> +#include \"rmnet_config.h\"\n> +#include \"rmnet_map.h\"\n> +#include \"rmnet_private.h\"\n> +\n> +#define RMNET_MAP_DEAGGR_SPACING  64\n> +#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2)\n> +\n> +/* Adds MAP header to front of skb->data\n> + * Padding is calculated and set appropriately in MAP header. Mux ID\n> is\n> + * initialized to 0.\n> + */\n> +struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff\n> *skb,\n> +\t\t\t\t\t\t  int hdrlen, int\n> pad)\n> +{\n> +\tstruct rmnet_map_header *map_header;\n> +\tu32 padding, map_datalen;\n> +\tu8 *padbytes;\n> +\n> +\tif (skb_headroom(skb) < sizeof(struct rmnet_map_header))\n> +\t\treturn NULL;\n> +\n> +\tmap_datalen = skb->len - hdrlen;\n> +\tmap_header = (struct rmnet_map_header *)\n> +\t\t\tskb_push(skb, sizeof(struct\n> rmnet_map_header));\n> +\tmemset(map_header, 0, sizeof(struct rmnet_map_header));\n> +\n> +\tif (pad == RMNET_MAP_NO_PAD_BYTES) {\n> +\t\tmap_header->pkt_len = htons(map_datalen);\n> +\t\treturn map_header;\n> +\t}\n> +\n> +\tpadding = ALIGN(map_datalen, 4) - map_datalen;\n> +\n> +\tif (padding == 0)\n> +\t\tgoto done;\n> +\n> +\tif (skb_tailroom(skb) < padding)\n> +\t\treturn NULL;\n> +\n> +\tpadbytes = (u8 *)skb_put(skb, padding);\n> +\tmemset(padbytes, 0, padding);\n> +\n> +done:\n> +\tmap_header->pkt_len = htons(map_datalen + padding);\n> +\tmap_header->pad_len = padding & 0x3F;\n> +\n> +\treturn map_header;\n> +}\n> +\n> +/* Deaggregates a single packet\n> + * A whole new buffer is allocated for each portion of an aggregated\n> frame.\n> + * Caller should keep calling deaggregate() on the source skb until\n> 0 is\n> + * returned, indicating that there are no more packets to\n> deaggregate. Caller\n> + * is responsible for freeing the original skb.\n> + */\n> +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,\n> +\t\t\t\t      struct rmnet_real_dev_info\n> *rdinfo)\n> +{\n> +\tstruct rmnet_map_header *maph;\n> +\tstruct sk_buff *skbn;\n> +\tu32 packet_len;\n> +\n> +\tif (skb->len == 0)\n> +\t\treturn NULL;\n> +\n> +\tmaph = (struct rmnet_map_header *)skb->data;\n> +\tpacket_len = ntohs(maph->pkt_len) + sizeof(struct\n> rmnet_map_header);\n> +\n> +\tif (((int)skb->len - (int)packet_len) < 0)\n> +\t\treturn NULL;\n> +\n> +\tskbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING,\n> GFP_ATOMIC);\n> +\tif (!skbn)\n> +\t\treturn NULL;\n> +\n> +\tskbn->dev = skb->dev;\n> +\tskb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM);\n> +\tskb_put(skbn, packet_len);\n> +\tmemcpy(skbn->data, skb->data, packet_len);\n> +\tskb_pull(skb, packet_len);\n> +\n> +\t/* Some hardware can send us empty frames. Catch them */\n> +\tif (ntohs(maph->pkt_len) == 0) {\n> +\t\tkfree_skb(skb);\n> +\t\treturn NULL;\n> +\t}\n> +\n> +\treturn skbn;\n> +}\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h\n> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h\n> new file mode 100644\n> index 0000000..ed820b5\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h\n> @@ -0,0 +1,45 @@\n> +/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All\n> rights reserved.\n> + *\n> + * This program is free software; you can redistribute it and/or\n> modify\n> + * it under the terms of the GNU General Public License version 2\n> and\n> + * only version 2 as published by the Free Software Foundation.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + */\n> +\n> +#ifndef _RMNET_PRIVATE_H_\n> +#define _RMNET_PRIVATE_H_\n> +\n> +#define RMNET_MAX_VND              32\n> +#define RMNET_MAX_PACKET_SIZE      16384\n> +#define RMNET_DFLT_PACKET_SIZE     1500\n> +#define RMNET_NEEDED_HEADROOM      16\n> +#define RMNET_TX_QUEUE_LEN         1000\n> +\n> +/* Constants */\n> +#define RMNET_EGRESS_FORMAT__RESERVED__         BIT(0)\n> +#define RMNET_EGRESS_FORMAT_MAP                 BIT(1)\n> +#define RMNET_EGRESS_FORMAT_AGGREGATION         BIT(2)\n> +#define RMNET_EGRESS_FORMAT_MUXING              BIT(3)\n> +#define RMNET_EGRESS_FORMAT_MAP_CKSUMV3         BIT(4)\n> +#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4         BIT(5)\n> +\n> +#define RMNET_INGRESS_FIX_ETHERNET              BIT(0)\n> +#define RMNET_INGRESS_FORMAT_MAP                BIT(1)\n> +#define RMNET_INGRESS_FORMAT_DEAGGREGATION      BIT(2)\n> +#define RMNET_INGRESS_FORMAT_DEMUXING           BIT(3)\n> +#define RMNET_INGRESS_FORMAT_MAP_COMMANDS       BIT(4)\n> +#define RMNET_INGRESS_FORMAT_MAP_CKSUMV3        BIT(5)\n> +#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4        BIT(6)\n> +\n> +/* Pass the frame up the stack with no modifications to skb->dev */\n> +#define RMNET_EPMODE_NONE (0)\n> +/* Replace skb->dev to a virtual rmnet device and pass up the stack\n> */\n> +#define RMNET_EPMODE_VND (1)\n> +/* Pass the frame directly to another device with dev_queue_xmit()\n> */\n> +#define RMNET_EPMODE_BRIDGE (2)\n> +\n> +#endif /* _RMNET_PRIVATE_H_ */\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c\n> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c\n> new file mode 100644\n> index 0000000..c8b573d\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c\n> @@ -0,0 +1,170 @@\n> +/* Copyright (c) 2013-2017, The Linux Foundation. All rights\n> reserved.\n> + *\n> + * This program is free software; you can redistribute it and/or\n> modify\n> + * it under the terms of the GNU General Public License version 2\n> and\n> + * only version 2 as published by the Free Software Foundation.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + *\n> + * RMNET Data virtual network driver\n> + *\n> + */\n> +\n> +#include <linux/etherdevice.h>\n> +#include <linux/if_arp.h>\n> +#include <net/pkt_sched.h>\n> +#include \"rmnet_config.h\"\n> +#include \"rmnet_handlers.h\"\n> +#include \"rmnet_private.h\"\n> +#include \"rmnet_map.h\"\n> +#include \"rmnet_vnd.h\"\n> +\n> +/* RX/TX Fixup */\n> +\n> +void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev)\n> +{\n> +\tdev->stats.rx_packets++;\n> +\tdev->stats.rx_bytes += skb->len;\n> +}\n> +\n> +void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev)\n> +{\n> +\tdev->stats.tx_packets++;\n> +\tdev->stats.tx_bytes += skb->len;\n> +}\n> +\n> +/* Network Device Operations */\n> +\n> +static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,\n> +\t\t\t\t\tstruct net_device *dev)\n> +{\n> +\tstruct rmnet_priv *priv;\n> +\n> +\tpriv = netdev_priv(dev);\n> +\tif (priv->local_ep.egress_dev) {\n> +\t\trmnet_egress_handler(skb, &priv->local_ep);\n> +\t} else {\n> +\t\tdev->stats.tx_dropped++;\n> +\t\tkfree_skb(skb);\n> +\t}\n> +\treturn NETDEV_TX_OK;\n> +}\n> +\n> +static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int\n> new_mtu)\n> +{\n> +\tif (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE)\n> +\t\treturn -EINVAL;\n> +\n> +\trmnet_dev->mtu = new_mtu;\n> +\treturn 0;\n> +}\n> +\n> +static const struct net_device_ops rmnet_vnd_ops = {\n> +\t.ndo_start_xmit = rmnet_vnd_start_xmit,\n> +\t.ndo_change_mtu = rmnet_vnd_change_mtu,\n> +};\n\nPlease implement ndo_get_iflink as well, so that it's easy to find out\nwhat the \"parent\"/lowerdev for a given rmnet interface is.\n\nThat might mean adding a \"phy_dev\" member to rmnet_priv, but that might\nhelp you clean up a lot of other stuff too\n\n> +/* Called by kernel whenever a new rmnet<n> device is created. Sets\n> MTU,\n> + * flags, ARP type, needed headroom, etc...\n> + */\n> +void rmnet_vnd_setup(struct net_device *rmnet_dev)\n> +{\n> +\tstruct rmnet_priv *priv;\n> +\n> +\tpriv = netdev_priv(rmnet_dev);\n> +\tnetdev_dbg(rmnet_dev, \"Setting up device %s\\n\", rmnet_dev-\n> >name);\n> +\n> +\trmnet_dev->netdev_ops = &rmnet_vnd_ops;\n> +\trmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE;\n> +\trmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM;\n> +\trandom_ether_addr(rmnet_dev->dev_addr);\n> +\trmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN;\n> +\n> +\t/* Raw IP mode */\n> +\trmnet_dev->header_ops = NULL;  /* No header */\n> +\trmnet_dev->type = ARPHRD_RAWIP;\n> +\trmnet_dev->hard_header_len = 0;\n> +\trmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);\n> +\n> +\trmnet_dev->needs_free_netdev = true;\n> +}\n> +\n> +/* Exposed API */\n> +\n> +int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,\n> +\t\t      struct rmnet_real_dev_info *r)\n> +{\n> +\tint rc;\n> +\n> +\tif (r->rmnet_devices[id])\n> +\t\treturn -EINVAL;\n> +\n> +\trc = register_netdevice(rmnet_dev);\n> +\tif (!rc) {\n> +\t\tr->rmnet_devices[id] = rmnet_dev;\n> +\t\tr->nr_rmnet_devs++;\n> +\t\trmnet_dev->rtnl_link_ops = &rmnet_link_ops;\n> +\t}\n> +\n> +\treturn rc;\n> +}\n> +\n> +int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r)\n> +{\n> +\tif (id >= RMNET_MAX_VND || !r->rmnet_devices[id])\n> +\t\treturn -EINVAL;\n> +\n> +\tr->rmnet_devices[id] = NULL;\n> +\tr->nr_rmnet_devs--;\n> +\treturn 0;\n> +}\n> +\n> +u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev)\n> +{\n> +\tstruct rmnet_priv *priv;\n> +\n> +\tpriv = netdev_priv(rmnet_dev);\n> +\treturn priv->mux_id;\n> +}\n> +\n> +void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id)\n> +{\n> +\tstruct rmnet_priv *priv;\n> +\n> +\tpriv = netdev_priv(rmnet_dev);\n> +\tpriv->mux_id = mux_id;\n> +}\n> +\n> +/* Gets the logical endpoint configuration for a RmNet virtual\n> network device\n> + * node. Caller should confirm that devices is a RmNet VND before\n> calling.\n> + */\n> +struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device\n> *rmnet_dev)\n> +{\n> +\tstruct rmnet_priv *priv;\n> +\n> +\tif (!rmnet_dev)\n> +\t\treturn NULL;\n> +\n> +\tpriv = netdev_priv(rmnet_dev);\n> +\n> +\treturn &priv->local_ep;\n> +}\n> +\n> +int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int\n> enable)\n> +{\n> +\tnetdev_dbg(rmnet_dev, \"Setting VND TX queue state to %d\\n\",\n> enable);\n> +\t/* Although we expect similar number of enable/disable\n> +\t * commands, optimize for the disable. That is more\n> +\t * latency sensitive than enable\n> +\t */\n> +\tif (unlikely(enable))\n> +\t\tnetif_wake_queue(rmnet_dev);\n> +\telse\n> +\t\tnetif_stop_queue(rmnet_dev);\n> +\n> +\treturn 0;\n> +}\n> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h\n> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h\n> new file mode 100644\n> index 0000000..b102b42\n> --- /dev/null\n> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h\n> @@ -0,0 +1,29 @@\n> +/* Copyright (c) 2013-2017, The Linux Foundation. All rights\n> reserved.\n> + *\n> + * This program is free software; you can redistribute it and/or\n> modify\n> + * it under the terms of the GNU General Public License version 2\n> and\n> + * only version 2 as published by the Free Software Foundation.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * RMNET Data Virtual Network Device APIs\n> + *\n> + */\n> +\n> +#ifndef _RMNET_VND_H_\n> +#define _RMNET_VND_H_\n> +\n> +int rmnet_vnd_do_flow_control(struct net_device *dev, int enable);\n> +struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device\n> *dev);\n> +int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,\n> +\t\t      struct rmnet_real_dev_info *r);\n> +int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r);\n> +void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device\n> *dev);\n> +void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device\n> *dev);\n> +u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev);\n> +void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id);\n> +void rmnet_vnd_setup(struct net_device *dev);\n> +#endif /* _RMNET_VND_H_ */","headers":{"Return-Path":"<netdev-owner@vger.kernel.org>","X-Original-To":"patchwork-incoming@ozlabs.org","Delivered-To":"patchwork-incoming@ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","ext-mx02.extmail.prod.ext.phx2.redhat.com;\n\tdmarc=none (p=none dis=none) header.from=redhat.com","ext-mx02.extmail.prod.ext.phx2.redhat.com;\n\tspf=fail smtp.mailfrom=dcbw@redhat.com"],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xj7Vb5qp7z9s8P\n\tfor <patchwork-incoming@ozlabs.org>;\n\tThu, 31 Aug 2017 00:41:27 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1751376AbdH3OlY (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tWed, 30 Aug 2017 10:41:24 -0400","from mx1.redhat.com ([209.132.183.28]:41070 \"EHLO mx1.redhat.com\"\n\trhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP\n\tid S1751323AbdH3OlX (ORCPT <rfc822;netdev@vger.kernel.org>);\n\tWed, 30 Aug 2017 10:41:23 -0400","from smtp.corp.redhat.com\n\t(int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14])\n\t(using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits))\n\t(No client certificate requested)\n\tby mx1.redhat.com (Postfix) with ESMTPS id 58230806B8;\n\tWed, 30 Aug 2017 14:41:23 +0000 (UTC)","from ovpn-112-33.rdu2.redhat.com (ovpn-112-33.rdu2.redhat.com\n\t[10.10.112.33])\n\tby smtp.corp.redhat.com (Postfix) with ESMTP id DF3CB9173B;\n\tWed, 30 Aug 2017 14:41:19 +0000 (UTC)"],"DMARC-Filter":"OpenDMARC Filter v1.3.2 mx1.redhat.com 58230806B8","Message-ID":"<1504103947.21231.5.camel@redhat.com>","Subject":"Re: [PATCH net-next 3/3 v11] drivers: net: ethernet: qualcomm:\n\trmnet: Initial implementation","From":"Dan Williams <dcbw@redhat.com>","To":"Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>,\n\tnetdev@vger.kernel.org, davem@davemloft.net,\n\tfengguang.wu@intel.com, jiri@resnulli.us,\n\tstephen@networkplumber.org, David.Laight@ACULAB.COM,\n\tmarcel@holtmann.org, andrew@lunn.ch","Date":"Wed, 30 Aug 2017 09:39:07 -0500","In-Reply-To":"<1504068258-16982-4-git-send-email-subashab@codeaurora.org>","References":"<1504068258-16982-1-git-send-email-subashab@codeaurora.org>\n\t<1504068258-16982-4-git-send-email-subashab@codeaurora.org>","Content-Type":"text/plain; charset=\"UTF-8\"","Mime-Version":"1.0","Content-Transfer-Encoding":"8bit","X-Scanned-By":"MIMEDefang 2.79 on 10.5.11.14","X-Greylist":"Sender IP whitelisted, not delayed by milter-greylist-4.5.16\n\t(mx1.redhat.com [10.5.110.26]);\n\tWed, 30 Aug 2017 14:41:23 +0000 (UTC)","Sender":"netdev-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netdev.vger.kernel.org>","X-Mailing-List":"netdev@vger.kernel.org"}},{"id":1760495,"web_url":"http://patchwork.ozlabs.org/comment/1760495/","msgid":"<c64415aa8d1184303f5916bdd2fd6ce3@codeaurora.org>","list_archive_url":null,"date":"2017-08-30T21:19:19","subject":"Re: [PATCH net-next 3/3 v11] drivers: net: ethernet: qualcomm:\n\trmnet: Initial implementation","submitter":{"id":65547,"url":"http://patchwork.ozlabs.org/api/people/65547/","name":"Subash Abhinov Kasiviswanathan","email":"subashab@codeaurora.org"},"content":"> General comment; other drivers that do similar things (macvlan, ipvlan)\n> use the term \"port\" to refer to what I think you're calling a\n> \"rmnet_real_dev_info\".  Maybe that's a shorter or less confusing term.\n> Could be renamed later too, if you wanted to do so.\n> \n\nHi Dan\n\nI'll rename it to rmnet_port.\n\n> Maybe this got elided during the revisions, but now I can't find\n> anywhere that sets RMNET_LOCAL_LOGICAL_ENDPOINT.  Looking at the\n> callchain, there are two places that LOCAL_LOGICAL_ENDPOINT matters:\n> \n> rmnet_get_endpoint(): only ever called by __rmnet_set_endpoint_config()\n> \n> __rmnet_set_endpoint_config(): only called from\n> rmnet_set_endpoint_config(); which itself is only called from\n> rmnet_newlink().\n> \n> So the only place that 'config_id' is set, and thus that it could be\n> LOCAL_LOGICAL_ENDPOINT, is rmnet_newlink() via 'mux_id'.  But\n> IFLA_VLAN_ID is a u16, and so I don't see anywhere that\n> config_id/mux_id will ever be < 0, and thus anywhere that it could be\n> LOCAL_LOGICAL_ENDPOINT.\n> \n> I could well just not be seeing it though...\n> \n> This function (__rmnet_set_endpoint_config) seems to only be called\n> from rmnet_set_endpoint_config().  Perhaps just combine them?\n> \n> But that brings up another point; can the rmnet \"mode\" or egress_dev\n> change at runtime, after the rmnet child has been created?  I forget if\n> that was possible with your original patchset that used ioctls.\n> \n\nThe original series with IOCTL was able to change it.\nWith the current netlink based configuration, we are using a fixed \nconfig\nof muxing and the egress dev is fixed for its lifetime. Practically, \nthese\nshould never change for a set of rmnet devices attached to a real dev.\nI will remove LOCAL_LOGICAL_ENDPOINT since it is unused.\n\n> Why not set the mux_id in rmnet_vnd_newlink()?\n> \n> Also, bigger problem.  r->rmnet_devices[] is only 32 items in size.\n> But mux_id (which is used as an index into rmnet_devices in a few\n> places) can be up to 255 (RMNET_MAX_LOGICAL_EP).\n> \n> So if you try to create an rmnet for mux ID 32, you panic the kernel.\n> See below my comments about rmnet_real_dev_info...\n> \n\nI'll fix this.\n\n> I can't see anywhere that the egress/ingress data get set except for\n> this function, so perhaps you could just skip these functions and\n> (since you already have 'r' from above) set r-\n>> [egress|ingress]_data_format directly?\n> \n\nYes, till this is made configurable, this need not be set separately.\n\n> This means that the first time you add an rmnet dev to a netdev, it'll\n> create a structure that's quite large (at least 255 * 6, but more due\n> to padding), when in most cases few of these items will be used.  Most\n> of the time you'd have only a couple PDNs active, but this will\n> allocate memory for MAX_LOGICAL_EP of them, no?\n> \n> ipvlan uses a list to track the child devices attached to a physical\n> device so that it doesn't have to allocate them all at once and waste\n> memory; that technique could replace the 'rmnet_devices' member below.\n> \n> It also uses a hash to find the actual ipvlan upperdev from the\n> rx_handler of the lowerdev, which is probably what would replace\n> muxed_ep[] here.\n> \n> Is the relationship between rmnet \"child\"/upper devs and mux_ids 1:1?\n> Or can you have multiple rmnet devs for the same mux_id?\n> \n> Dan\n\nWe can have multiple rmnet devices having the same mux_id. They will\nneed to be attached to different real_dev though. I'll look into the\ncreation of hash for the lookup. Once I have the hash up, I should\nbe able to get rid of some of the structures.\n\nThe other main functionality which I am unsure is the\nbridge handling - passing on MAP data from one real_dev to another.\nIs there some to achieve this using any existing netlink attributes?\nAny suggestions would be appreciated.\n\n> Please implement ndo_get_iflink as well, so that it's easy to find out\n> what the \"parent\"/lowerdev for a given rmnet interface is.\n> \n> That might mean adding a \"phy_dev\" member to rmnet_priv, but that might\n> help you clean up a lot of other stuff too\n> \n\nSure, I'll implement this. Let me know if you have more comments.\n\n--\nQualcomm Innovation Center, Inc.\nThe Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a \nLinux Foundation Collaborative Project","headers":{"Return-Path":"<netdev-owner@vger.kernel.org>","X-Original-To":"patchwork-incoming@ozlabs.org","Delivered-To":"patchwork-incoming@ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","ozlabs.org; dkim=pass (1024-bit key;\n\tunprotected) header.d=codeaurora.org header.i=@codeaurora.org\n\theader.b=\"OghS/Ll0\"; \n\tdkim=pass (1024-bit key) header.d=codeaurora.org\n\theader.i=@codeaurora.org header.b=\"OghS/Ll0\"; \n\tdkim-atps=neutral"],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xjJKq2yXrz9sNw\n\tfor <patchwork-incoming@ozlabs.org>;\n\tThu, 31 Aug 2017 07:19:27 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1750919AbdH3VTV (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tWed, 30 Aug 2017 17:19:21 -0400","from smtp.codeaurora.org ([198.145.29.96]:60012 \"EHLO\n\tsmtp.codeaurora.org\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1750824AbdH3VTU (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Wed, 30 Aug 2017 17:19:20 -0400","by smtp.codeaurora.org (Postfix, from userid 1000)\n\tid DF5FF61245; Wed, 30 Aug 2017 21:19:19 +0000 (UTC)","from mail.codeaurora.org (localhost.localdomain [127.0.0.1])\n\tby smtp.codeaurora.org (Postfix) with ESMTP id 0D7B361201;\n\tWed, 30 Aug 2017 21:19:19 +0000 (UTC)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org;\n\ts=default; t=1504127959;\n\tbh=GHSmlByPKnJVnccjzlADT2amyQMkvrCs52NIKvPAd+4=;\n\th=Date:From:To:Cc:Subject:In-Reply-To:References:From;\n\tb=OghS/Ll0Khht3vKLgDhTiUX8VZtlpwfxynFwVzRswskPDFIHHuwMaho+tjbR6VvhC\n\t1Hflhb7if0CarZU/BTehL+0B/rfTkIN0AMsbeagDU5qw7odnZhD091XEzLr5ZxefoN\n\tZie6J8GlEDmAXm/RVaFFEfTLJeVDevyxDs8ycW8I=","v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org;\n\ts=default; t=1504127959;\n\tbh=GHSmlByPKnJVnccjzlADT2amyQMkvrCs52NIKvPAd+4=;\n\th=Date:From:To:Cc:Subject:In-Reply-To:References:From;\n\tb=OghS/Ll0Khht3vKLgDhTiUX8VZtlpwfxynFwVzRswskPDFIHHuwMaho+tjbR6VvhC\n\t1Hflhb7if0CarZU/BTehL+0B/rfTkIN0AMsbeagDU5qw7odnZhD091XEzLr5ZxefoN\n\tZie6J8GlEDmAXm/RVaFFEfTLJeVDevyxDs8ycW8I="],"X-Spam-Checker-Version":"SpamAssassin 3.4.0 (2014-02-07) on\n\tpdx-caf-mail.web.codeaurora.org","X-Spam-Level":"","X-Spam-Status":"No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00,\n\tDKIM_SIGNED,\n\tT_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0","MIME-Version":"1.0","Content-Type":"text/plain; charset=US-ASCII;\n format=flowed","Content-Transfer-Encoding":"7bit","Date":"Wed, 30 Aug 2017 15:19:19 -0600","From":"Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>","To":"Dan Williams <dcbw@redhat.com>","Cc":"netdev@vger.kernel.org, davem@davemloft.net,\n\tfengguang.wu@intel.com, jiri@resnulli.us,\n\tstephen@networkplumber.org, David.Laight@aculab.com,\n\tmarcel@holtmann.org, andrew@lunn.ch","Subject":"Re: [PATCH net-next 3/3 v11] drivers: net: ethernet: qualcomm:\n\trmnet: Initial implementation","In-Reply-To":"<1504103947.21231.5.camel@redhat.com>","References":"<1504068258-16982-1-git-send-email-subashab@codeaurora.org>\n\t<1504068258-16982-4-git-send-email-subashab@codeaurora.org>\n\t<1504103947.21231.5.camel@redhat.com>","Message-ID":"<c64415aa8d1184303f5916bdd2fd6ce3@codeaurora.org>","X-Sender":"subashab@codeaurora.org","User-Agent":"Roundcube Webmail/1.2.5","Sender":"netdev-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netdev.vger.kernel.org>","X-Mailing-List":"netdev@vger.kernel.org"}},{"id":1760505,"web_url":"http://patchwork.ozlabs.org/comment/1760505/","msgid":"<20170830.142744.324884406082731585.davem@davemloft.net>","list_archive_url":null,"date":"2017-08-30T21:27:44","subject":"Re: [PATCH net-next 3/3 v11] drivers: net: ethernet: qualcomm:\n\trmnet: Initial implementation","submitter":{"id":15,"url":"http://patchwork.ozlabs.org/api/people/15/","name":"David Miller","email":"davem@davemloft.net"},"content":"From: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>\nDate: Wed, 30 Aug 2017 15:19:19 -0600\n\n> Sure, I'll implement this. Let me know if you have more comments.\n\nSubash, keep in mind that since I applied your v11 patches already you\nwill need to send me relative fixes and changes at this point, rather\nthan resubmit the series.\n\nThank you.","headers":{"Return-Path":"<netdev-owner@vger.kernel.org>","X-Original-To":"patchwork-incoming@ozlabs.org","Delivered-To":"patchwork-incoming@ozlabs.org","Authentication-Results":"ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xjJWW4Z0Xz9s8w\n\tfor <patchwork-incoming@ozlabs.org>;\n\tThu, 31 Aug 2017 07:27:51 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1751283AbdH3V1t (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tWed, 30 Aug 2017 17:27:49 -0400","from shards.monkeyblade.net ([184.105.139.130]:34088 \"EHLO\n\tshards.monkeyblade.net\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1750828AbdH3V1s (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Wed, 30 Aug 2017 17:27:48 -0400","from localhost (74-93-104-98-Washington.hfc.comcastbusiness.net\n\t[74.93.104.98]) (using TLSv1 with cipher AES256-SHA (256/256 bits))\n\t(Client did not present a certificate)\n\t(Authenticated sender: davem-davemloft)\n\tby shards.monkeyblade.net (Postfix) with ESMTPSA id B7FF5133FDE6A;\n\tWed, 30 Aug 2017 14:27:47 -0700 (PDT)"],"Date":"Wed, 30 Aug 2017 14:27:44 -0700 (PDT)","Message-Id":"<20170830.142744.324884406082731585.davem@davemloft.net>","To":"subashab@codeaurora.org","Cc":"dcbw@redhat.com, netdev@vger.kernel.org, fengguang.wu@intel.com,\n\tjiri@resnulli.us, stephen@networkplumber.org,\n\tDavid.Laight@aculab.com, marcel@holtmann.org, andrew@lunn.ch","Subject":"Re: [PATCH net-next 3/3 v11] drivers: net: ethernet: qualcomm:\n\trmnet: Initial implementation","From":"David Miller <davem@davemloft.net>","In-Reply-To":"<c64415aa8d1184303f5916bdd2fd6ce3@codeaurora.org>","References":"<1504068258-16982-4-git-send-email-subashab@codeaurora.org>\n\t<1504103947.21231.5.camel@redhat.com>\n\t<c64415aa8d1184303f5916bdd2fd6ce3@codeaurora.org>","X-Mailer":"Mew version 6.7 on Emacs 25.2 / Mule 6.0 (HANACHIRUSATO)","Mime-Version":"1.0","Content-Type":"Text/Plain; charset=us-ascii","Content-Transfer-Encoding":"7bit","X-Greylist":"Sender succeeded SMTP AUTH, not delayed by\n\tmilter-greylist-4.5.12 (shards.monkeyblade.net\n\t[149.20.54.216]); Wed, 30 Aug 2017 14:27:48 -0700 (PDT)","Sender":"netdev-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netdev.vger.kernel.org>","X-Mailing-List":"netdev@vger.kernel.org"}},{"id":1760514,"web_url":"http://patchwork.ozlabs.org/comment/1760514/","msgid":"<a88f19fc1dead2c7fe560ed090b79488@codeaurora.org>","list_archive_url":null,"date":"2017-08-30T21:40:01","subject":"Re: [PATCH net-next 3/3 v11] drivers: net: ethernet: qualcomm:\n\trmnet: Initial implementation","submitter":{"id":65547,"url":"http://patchwork.ozlabs.org/api/people/65547/","name":"Subash Abhinov Kasiviswanathan","email":"subashab@codeaurora.org"},"content":"> Subash, keep in mind that since I applied your v11 patches already you\n> will need to send me relative fixes and changes at this point, rather\n> than resubmit the series.\n> \n> Thank you.\n\nThanks for the heads up David. Will do.","headers":{"Return-Path":"<netdev-owner@vger.kernel.org>","X-Original-To":"patchwork-incoming@ozlabs.org","Delivered-To":"patchwork-incoming@ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","ozlabs.org; dkim=pass (1024-bit key;\n\tunprotected) header.d=codeaurora.org header.i=@codeaurora.org\n\theader.b=\"QoDQ1fUj\"; \n\tdkim=pass (1024-bit key) header.d=codeaurora.org\n\theader.i=@codeaurora.org header.b=\"Hq2msSDw\"; \n\tdkim-atps=neutral"],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xjJng02X0z9s8P\n\tfor <patchwork-incoming@ozlabs.org>;\n\tThu, 31 Aug 2017 07:40:06 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1751516AbdH3VkE (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tWed, 30 Aug 2017 17:40:04 -0400","from smtp.codeaurora.org ([198.145.29.96]:39932 \"EHLO\n\tsmtp.codeaurora.org\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1751205AbdH3VkC (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Wed, 30 Aug 2017 17:40:02 -0400","by smtp.codeaurora.org (Postfix, from userid 1000)\n\tid 1B16460C6C; Wed, 30 Aug 2017 21:40:01 +0000 (UTC)","from mail.codeaurora.org (localhost.localdomain [127.0.0.1])\n\tby smtp.codeaurora.org (Postfix) with ESMTP id 4D27C60B69;\n\tWed, 30 Aug 2017 21:40:01 +0000 (UTC)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org;\n\ts=default; t=1504129202;\n\tbh=wEd8ZD+xs0obPPvwIUyEYLtAimg/aarMvE6/Lmcy7vw=;\n\th=Date:From:To:Cc:Subject:In-Reply-To:References:From;\n\tb=QoDQ1fUje0LneWLcUTmnlPLt9zDBliimfu2zfCfRrcmq9YUltPI9LamD7/iqkl0Ml\n\tPuXaSCVJrgsMpiMhICzt0zfB91pVIDTecn7aVcs2tapkaYpnlTCASVpMROtq09KUBu\n\tJmn5htAOUQypF7BAApPvii9Btmu5zBARuyg3p15s=","v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org;\n\ts=default; t=1504129201;\n\tbh=wEd8ZD+xs0obPPvwIUyEYLtAimg/aarMvE6/Lmcy7vw=;\n\th=Date:From:To:Cc:Subject:In-Reply-To:References:From;\n\tb=Hq2msSDwmpYTtTswXZ/SRiya78YuA/JjoWFqscrSngrzPjWWByY5OTD3ARsPISaJw\n\tm/yhokcOHHThJVGyPPoBR+jqD50DSv9snFFbHpGIQzkoEXbK/f6JFtxmW/fnojJqX1\n\tyiJFcqPGQcWeDjVWPq2i3M4btGzlhVJP4CpFLsEY="],"X-Spam-Checker-Version":"SpamAssassin 3.4.0 (2014-02-07) on\n\tpdx-caf-mail.web.codeaurora.org","X-Spam-Level":"","X-Spam-Status":"No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00,\n\tDKIM_SIGNED,\n\tT_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0","MIME-Version":"1.0","Content-Type":"text/plain; charset=US-ASCII;\n format=flowed","Content-Transfer-Encoding":"7bit","Date":"Wed, 30 Aug 2017 15:40:01 -0600","From":"Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>","To":"David Miller <davem@davemloft.net>","Cc":"dcbw@redhat.com, netdev@vger.kernel.org, fengguang.wu@intel.com,\n\tjiri@resnulli.us, stephen@networkplumber.org,\n\tDavid.Laight@aculab.com, marcel@holtmann.org, andrew@lunn.ch","Subject":"Re: [PATCH net-next 3/3 v11] drivers: net: ethernet: qualcomm:\n\trmnet: Initial implementation","In-Reply-To":"<20170830.142744.324884406082731585.davem@davemloft.net>","References":"<1504068258-16982-4-git-send-email-subashab@codeaurora.org>\n\t<1504103947.21231.5.camel@redhat.com>\n\t<c64415aa8d1184303f5916bdd2fd6ce3@codeaurora.org>\n\t<20170830.142744.324884406082731585.davem@davemloft.net>","Message-ID":"<a88f19fc1dead2c7fe560ed090b79488@codeaurora.org>","X-Sender":"subashab@codeaurora.org","User-Agent":"Roundcube Webmail/1.2.5","Sender":"netdev-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netdev.vger.kernel.org>","X-Mailing-List":"netdev@vger.kernel.org"}}]