On Thu, 13 Apr 2017 23:05:29 -0600 Subash Abhinov Kasiviswanathan <subas...@codeaurora.org> wrote:
> RmNet driver provides a transport agnostic MAP (multiplexing and > aggregation protocol) support in embedded module. Module provides > virtual network devices which can be attached to any IP-mode > physical device. This will be used to provide all MAP functionality > on future hardware in a single consistent location. > > Signed-off-by: Subash Abhinov Kasiviswanathan <subas...@codeaurora.org> > > diff --git a/Documentation/networking/rmnet.txt > b/Documentation/networking/rmnet.txt > new file mode 100644 > index 0000000..58d3ea2 > --- /dev/null > +++ b/Documentation/networking/rmnet.txt > ... > +3. Userspace configuration > + > +rmnet userspace configuration is done through netlink library librmnetctl > +and command line utility rmnetcli. Utility is hosted in codeaurora forum git. > +The driver uses rtnl_link_ops for communication. > + > +https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/\ > +dataservices/tree/rmnetctl Don't split URL better to have long line. > diff --git a/drivers/net/Makefile b/drivers/net/Makefile > index 98ed4d9..29b3945 100644 > --- a/drivers/net/Makefile > +++ b/drivers/net/Makefile > @@ -74,3 +74,4 @@ obj-$(CONFIG_HYPERV_NET) += hyperv/ > obj-$(CONFIG_NTB_NETDEV) += ntb_netdev.o > > obj-$(CONFIG_FUJITSU_ES) += fjes/ > +obj-$(CONFIG_RMNET) += rmnet/ > diff --git a/drivers/net/rmnet/Kconfig b/drivers/net/rmnet/Kconfig Since this is Qualcomm and Ethernet specific, maybe better to put in drivers/net/ethernet/qualcom/rmnet > new file mode 100644 > index 0000000..63cd477 > --- /dev/null > +++ b/drivers/net/rmnet/Kconfig > @@ -0,0 +1,23 @@ > +# > +# RMNET MAP driver > +# > + > +menuconfig RMNET > + depends on NETDEVICES > + bool "RmNet MAP driver" > + default n > + ---help--- > + If you say Y here, then the rmnet module will be statically > + compiled into the kernel. The rmnet module provides MAP > + functionality for embedded and bridged traffic. > +if RMNET > + > +config RMNET_DEBUG > + bool "RmNet Debug Logging" > + default n > + ---help--- > + Say Y here if you want RmNet to be able to log packets in main > + system log. This should not be enabled on production builds as it can > + impact system performance. Note that simply enabling it here will not > + enable the logging; it must be enabled at run-time as well. Please use network device standard debug mechanism. netif_msg_XXX > +endif # RMNET > diff --git a/drivers/net/rmnet/Makefile b/drivers/net/rmnet/Makefile > new file mode 100644 > index 0000000..2b6c9cf > --- /dev/null > +++ b/drivers/net/rmnet/Makefile > @@ -0,0 +1,14 @@ > +# > +# Makefile for the RMNET module > +# > + > +rmnet-y := rmnet_main.o > +rmnet-y += rmnet_config.o > +rmnet-y += rmnet_vnd.o > +rmnet-y += rmnet_handlers.o > +rmnet-y += rmnet_map_data.o > +rmnet-y += rmnet_map_command.o > +rmnet-y += rmnet_stats.o > +obj-$(CONFIG_RMNET) += rmnet.o > + > +CFLAGS_rmnet_main.o := -I$(src) > diff --git a/drivers/net/rmnet/rmnet_config.c > b/drivers/net/rmnet/rmnet_config.c > new file mode 100644 > index 0000000..a4bc76b > --- /dev/null > +++ b/drivers/net/rmnet/rmnet_config.c > @@ -0,0 +1,592 @@ > +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only 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. > + * > + * RMNET configuration engine > + * > + */ > + > +#include <net/sock.h> > +#include <linux/module.h> > +#include <linux/netlink.h> > +#include <linux/netdevice.h> > +#include <linux/skbuff.h> > +#include <linux/spinlock.h> > +#include <linux/rmnet.h> > +#include "rmnet_config.h" > +#include "rmnet_handlers.h" > +#include "rmnet_vnd.h" > +#include "rmnet_private.h" > + > +RMNET_LOG_MODULE(RMNET_LOGMASK_CONFIG); > + > +/* Local Definitions and Declarations */ > +#define RMNET_LOCAL_LOGICAL_ENDPOINT -1 > + > +/* _rmnet_is_physical_endpoint_associated() - Determines if device is > associated > + * @dev: Device to get check > + * > + * Compares device rx_handler callback pointer against known function > + */ > +static inline int _rmnet_is_physical_endpoint_associated(struct net_device > *dev) > +{ > + rx_handler_func_t *rx_handler; > + > + rx_handler = rcu_dereference(dev->rx_handler); > + > + if (rx_handler == rmnet_rx_handler) > + return 1; > + else > + return 0; > +} Could just be: static inline int _rmnet_is_physical_endpoint_associated(const struct net_device *dev) { rx_handler_func_t *rx_handler = rcu_dereference(dev->rx_handler); return rx_handler == rmet_rx_handler; } But standard practice is to use ndo_ops to identify self in network drivers. I.e return dev->netdev_ops == &rmnet_device_ops; > +/* _rmnet_get_phys_ep_config() - Get physical ep config for an associated > device > + * @dev: Device to get endpoint configuration from > + */ > +static inline struct rmnet_phys_ep_conf_s *_rmnet_get_phys_ep_config > + (struct net_device *dev) awkward line break. dev could be const > +{ > + if (_rmnet_is_physical_endpoint_associated(dev)) > + return (struct rmnet_phys_ep_conf_s *) > + rcu_dereference(dev->rx_handler_data); > + else > + return 0; > +} > + > +struct rmnet_free_vnd_work { > + struct work_struct work; > + int vnd_id[RMNET_MAX_VND]; > + int count; > +}; > + > +/* _rmnet_get_logical_ep() - Gets the logical end point configuration > + * structure for a network device > + * @dev: Device to get endpoint configuration from > + * @config_id: Logical endpoint id on device > + * Retrieves the logical_endpoint_config structure. > + */ > +static struct rmnet_logical_ep_conf_s *_rmnet_get_logical_ep > + (struct net_device *dev, int config_id) > +{ > + struct rmnet_phys_ep_conf_s *config; > + struct rmnet_logical_ep_conf_s *epconfig_l; > + > + if (rmnet_vnd_is_vnd(dev)) { > + epconfig_l = rmnet_vnd_get_le_config(dev); > + } else { > + config = _rmnet_get_phys_ep_config(dev); > + > + if (!config) > + return NULL; > + > + if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT) > + epconfig_l = &config->local_ep; > + else > + epconfig_l = &config->muxed_ep[config_id]; > + } > + > + return epconfig_l; > +} > + > +/* rmnet_unassociate_network_device() - Unassociate network device > + * @dev: Device to unassociate > + * > + * Frees all structures generate for device. Unregisters rx_handler > + */ > +static int rmnet_unassociate_network_device(struct net_device *dev) > +{ > + struct rmnet_phys_ep_conf_s *config; > + int config_id = RMNET_LOCAL_LOGICAL_ENDPOINT; > + struct rmnet_logical_ep_conf_s *epconfig_l; > + > + ASSERT_RTNL(); > + > + LOGL("(%s);", dev->name); > + > + if (!dev || !_rmnet_is_physical_endpoint_associated(dev)) > + return -EINVAL; > + > + for (; config_id < RMNET_MAX_LOGICAL_EP; config_id++) { > + epconfig_l = _rmnet_get_logical_ep(dev, config_id); > + if (epconfig_l && epconfig_l->refcount) > + return -EINVAL; > + } > + > + config = (struct rmnet_phys_ep_conf_s *) > + rcu_dereference(dev->rx_handler_data); Please don't directly reference rx_handler. There is already functions like netdev_is_rx_handler_busy() to abstract that API. > + > + if (!config) > + return -EINVAL; > + > + kfree(config); > + > + netdev_rx_handler_unregister(dev); > + > + dev_put(dev); > + return 0; > +} > + > +/* rmnet_set_ingress_data_format() - Set ingress data format on network > device > + * @dev: Device to ingress data format on > + * @egress_data_format: 32-bit unsigned bitmask of ingress format > + * > + * Network device must already have association with RmNet Data driver > + */ > +static int rmnet_set_ingress_data_format(struct net_device *dev, > + u32 ingress_data_format) > +{ > + struct rmnet_phys_ep_conf_s *config; > + > + ASSERT_RTNL(); > + > + LOGL("(%s,0x%08X);", dev->name, ingress_data_format); > + > + if (!dev) > + return -EINVAL; > + > + config = _rmnet_get_phys_ep_config(dev); > + if (!config) > + return -EINVAL; > + > + config->ingress_data_format = ingress_data_format; > + > + return 0; > +} > + > +/* rmnet_set_egress_data_format() - Set egress data format on network device > + * @dev: Device to egress data format on > + * @egress_data_format: 32-bit unsigned bitmask of egress format > + * > + * Network device must already have association with RmNet Data driver > + */ > +static int rmnet_set_egress_data_format(struct net_device *dev, > + u32 egress_data_format, > + u16 agg_size, > + u16 agg_count) > +{ > + struct rmnet_phys_ep_conf_s *config; > + > + ASSERT_RTNL(); > + > + LOGL("(%s,0x%08X, %d, %d);", > + dev->name, egress_data_format, agg_size, agg_count); > + > + if (!dev) > + return -EINVAL; > + > + config = _rmnet_get_phys_ep_config(dev); > + if (!config) > + return -EINVAL; > + > + config->egress_data_format = egress_data_format; > + > + return 0; > +} > + > +/* rmnet_associate_network_device() - Associate network device > + * @dev: Device to register with RmNet data > + * > + * Typically used on physical network devices. Registers RX handler and > private > + * metadata structures. > + */ > +static int rmnet_associate_network_device(struct net_device *dev) > +{ > + struct rmnet_phys_ep_conf_s *config; > + int rc; > + > + ASSERT_RTNL(); > + > + LOGL("(%s);\n", dev->name); > + > + if (!dev || _rmnet_is_physical_endpoint_associated(dev) || > + rmnet_vnd_is_vnd(dev)) { > + LOGM("cannot register with this dev"); > + return -EINVAL; > + } > + > + config = kmalloc(sizeof(*config), GFP_ATOMIC); > + if (!config) > + return -ENOMEM; > + > + memset(config, 0, sizeof(struct rmnet_phys_ep_conf_s)); > + config->dev = dev; > + > + rc = netdev_rx_handler_register(dev, rmnet_rx_handler, config); > + > + if (rc) { > + LOGM("netdev_rx_handler_register returns %d", rc); > + kfree(config); > + return -EBUSY; > + } > + > + dev_hold(dev); > + return 0; > +} > + > +/* __rmnet_set_logical_endpoint_config() - Set logical endpoing config on > device > + * @dev: Device to set endpoint configuration on > + * @config_id: logical endpoint id on device > + * @epconfig: endpoint configuration structure to set > + */ You are using docbook format here, but this is not a docbook comment. ie. /** * function - This is a docbook comment * @dev: this is a param */ Plus these are static functions so there is no point in documentating internal API with docbook. > +static int __rmnet_set_logical_endpoint_config > + (struct net_device *dev, > + int config_id, > + struct rmnet_logical_ep_conf_s *epconfig) > +{ > + struct rmnet_logical_ep_conf_s *epconfig_l; > + > + ASSERT_RTNL(); > + > + if (!dev || config_id < RMNET_LOCAL_LOGICAL_ENDPOINT || > + config_id >= RMNET_MAX_LOGICAL_EP) > + return -EINVAL; For internal API's you should validate parmeters at the external entry point not in each call. Otherwise you have a multitude of impossible error checks and dead code paths. > + > + epconfig_l = _rmnet_get_logical_ep(dev, config_id); > + > + if (!epconfig_l || epconfig_l->refcount) > + return -EINVAL; > + > + memcpy(epconfig_l, epconfig, sizeof(struct rmnet_logical_ep_conf_s)); > + if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT) > + epconfig_l->mux_id = 0; > + else > + epconfig_l->mux_id = config_id; > + > + /* Explicitly hold a reference to the egress device */ > + dev_hold(epconfig_l->egress_dev); > + return 0; > +} > + ... > + > +static struct notifier_block rmnet_dev_notifier = { > + .notifier_call = rmnet_config_notify_cb, > + .next = 0, > + .priority = 0 > +}; Don't initialize fields that are not used or should be zero. > + > +static int rmnet_newlink(struct net *src_net, struct net_device *dev, > + struct nlattr *tb[], struct nlattr *data[]) > +{ > + int ingress_format = RMNET_INGRESS_FORMAT_DEMUXING | > + RMNET_INGRESS_FORMAT_DEAGGREGATION | > + RMNET_INGRESS_FORMAT_MAP; > + int egress_format = RMNET_EGRESS_FORMAT_MUXING | > + RMNET_EGRESS_FORMAT_MAP; > + struct net_device *real_dev; > + int mode = RMNET_EPMODE_VND; > + u16 mux_id; > + > + real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); > + if (!real_dev) > + return -ENODEV; > + > + if (!data[IFLA_RMNET_MUX_ID]) > + return -EINVAL; So you are inventing private link netlink attributes. Why? Why can't you use device switch, bridge, or other master/slave model. > + > + mux_id = nla_get_u16(data[IFLA_VLAN_ID]); > + if (rmnet_vnd_newlink(mux_id, dev)) > + return -EINVAL; > + > + rmnet_associate_network_device(real_dev); > + rmnet_set_egress_data_format(real_dev, egress_format, 0, 0); > + rmnet_set_ingress_data_format(real_dev, ingress_format); > + rmnet_set_logical_endpoint_config(real_dev, mux_id, mode, dev); > + rmnet_set_logical_endpoint_config(dev, mux_id, mode, real_dev); > + return 0; > +} > + > +static void rmnet_delink(struct net_device *dev, struct list_head *head) > +{ > + struct rmnet_logical_ep_conf_s *cfg; > + int mux_id; > + > + mux_id = rmnet_vnd_is_vnd(dev); > + if (!mux_id) > + return; > + > +/* rmnet_vnd_is_vnd() gives mux_id + 1, so subtract 1 to get the correct > mux_id > + */ > + mux_id--; > + cfg = rmnet_vnd_get_le_config(dev); > + > + if (cfg && cfg->refcount) { > + _rmnet_unset_logical_endpoint_config(cfg->egress_dev, mux_id); > + _rmnet_unset_logical_endpoint_config(dev, mux_id); > + rmnet_vnd_remove_ref_dev(mux_id); > + rmnet_unassociate_network_device(cfg->egress_dev); > + } > + > + unregister_netdevice_queue(dev, head); > +} > + > +static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[]) > +{ > + u16 mux_id; > + > + if (!data || !data[IFLA_RMNET_MUX_ID]) > + return -EINVAL; > + > + mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]); > + if (!mux_id || mux_id > (RMNET_MAX_LOGICAL_EP - 1)) > + return -ERANGE; > + > + return 0; > +} > + > +static size_t rmnet_get_size(const struct net_device *dev) > +{ > + return nla_total_size(2); /* IFLA_RMNET_MUX_ID */ > +} > + > +struct rtnl_link_ops rmnet_link_ops __read_mostly = { > + .kind = "rmnet", > + .maxtype = __IFLA_RMNET_MAX, > + .priv_size = sizeof(struct rmnet_vnd_private_s), > + .setup = rmnet_vnd_setup, > + .validate = rmnet_rtnl_validate, > + .newlink = rmnet_newlink, > + .dellink = rmnet_delink, > + .get_size = rmnet_get_size, > +}; > + > +int rmnet_config_init(void) > +{ > + int rc; > + > + rc = register_netdevice_notifier(&rmnet_dev_notifier); > + if (rc != 0) { > + LOGE("Failed to register device notifier; rc=%d", rc); > + return rc; > + } > + > + rc = rtnl_link_register(&rmnet_link_ops); > + if (rc != 0) { > + unregister_netdevice_notifier(&rmnet_dev_notifier); > + LOGE("Failed to register netlink handler; rc=%d", rc); > + return rc; > + } > + return rc; > +} > + > +void rmnet_config_exit(void) > +{ > + unregister_netdevice_notifier(&rmnet_dev_notifier); > + rtnl_link_unregister(&rmnet_link_ops); > +} > diff --git a/drivers/net/rmnet/rmnet_config.h > b/drivers/net/rmnet/rmnet_config.h > new file mode 100644 > index 0000000..0ef58e8 > --- /dev/null > +++ b/drivers/net/rmnet/rmnet_config.h > @@ -0,0 +1,79 @@ > +/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights > reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only 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. > + * > + * RMNET Data configuration engine > + * > + */ > + > +#include <linux/types.h> > +#include <linux/time.h> > +#include <linux/skbuff.h> > + > +#ifndef _RMNET_CONFIG_H_ > +#define _RMNET_CONFIG_H_ > + > +#define RMNET_MAX_LOGICAL_EP 255 > + > +/* struct rmnet_logical_ep_conf_s - Logical end-point configuration > + * > + * @refcount: Reference count for this endpoint. 0 signifies the endpoint is > not > + * configured for use > + * @rmnet_mode: Specifies how the traffic should be finally delivered. > Possible > + * options are available in enum rmnet_config_endpoint_modes_e > + * @mux_id: Virtual channel ID used by MAP protocol > + * @egress_dev: Next device to deliver the packet to. Exact usage of this > + * parmeter depends on the rmnet_mode > + */ > +struct rmnet_logical_ep_conf_s { > + u8 refcount; > + u8 rmnet_mode; > + u8 mux_id; > + struct timespec flush_time; > + struct net_device *egress_dev; > +}; > + > +/* struct rmnet_phys_ep_conf_s - Physical endpoint configuration > + * One instance of this structure is instantiated for each net_device > associated > + * with rmnet. > + * > + * @dev: The device which is associated with rmnet. Corresponds to this > + * specific instance of rmnet_phys_ep_conf_s > + * @local_ep: Default non-muxed endpoint. Used for non-MAP protocols/formats > + * @muxed_ep: All multiplexed logical endpoints associated with this device > + * @ingress_data_format: RMNET_INGRESS_FORMAT_* flags from rmnet.h > + * @egress_data_format: RMNET_EGRESS_FORMAT_* flags from rmnet.h > + * > + * @egress_agg_size: Maximum size (bytes) of data which should be aggregated > + * @egress_agg_count: Maximum count (packets) of data which should be > aggregated > + * Smaller of the two parameters above are chosen for > + * aggregation > + * @tail_spacing: Guaranteed padding (bytes) when de-aggregating ingress > frames > + * @agg_time: Wall clock time when aggregated frame was created > + * @agg_last: Last time the aggregation routing was invoked > + */ > +struct rmnet_phys_ep_conf_s { > + struct net_device *dev; > + struct rmnet_logical_ep_conf_s local_ep; > + struct rmnet_logical_ep_conf_s muxed_ep[RMNET_MAX_LOGICAL_EP]; > + u32 ingress_data_format; > + u32 egress_data_format; > +}; > + > +int rmnet_config_init(void); > +void rmnet_config_exit(void); > +int rmnet_free_vnd(int id); > + > +extern struct rtnl_link_ops rmnet_link_ops; > + > +struct rmnet_vnd_private_s { > + struct rmnet_logical_ep_conf_s local_ep; > +}; > +#endif /* _RMNET_CONFIG_H_ */ > diff --git a/drivers/net/rmnet/rmnet_handlers.c > b/drivers/net/rmnet/rmnet_handlers.c > new file mode 100644 > index 0000000..bf8b3bb > --- /dev/null > +++ b/drivers/net/rmnet/rmnet_handlers.c > @@ -0,0 +1,517 @@ > +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only 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. > + * > + * RMNET Data ingress/egress handler > + * > + */ > + > +#include <linux/skbuff.h> > +#include <linux/netdevice.h> > +#include <linux/module.h> > +#include <linux/rmnet.h> > +#include <linux/netdev_features.h> > +#include <linux/ip.h> > +#include <linux/ipv6.h> > +#include "rmnet_private.h" > +#include "rmnet_config.h" > +#include "rmnet_vnd.h" > +#include "rmnet_map.h" > +#include "rmnet_stats.h" > +#include "rmnet_handlers.h" > + > +RMNET_LOG_MODULE(RMNET_LOGMASK_HANDLER); > + > +#ifdef CONFIG_RMNET_DEBUG > +unsigned int dump_pkt_rx; > +module_param(dump_pkt_rx, uint, 0644); > +MODULE_PARM_DESC(dump_pkt_rx, "Dump packets entering ingress handler"); > + > +unsigned int dump_pkt_tx; > +module_param(dump_pkt_tx, uint, 0644); > +MODULE_PARM_DESC(dump_pkt_tx, "Dump packets exiting egress handler"); > +#endif /* CONFIG_RMNET_DEBUG */ > + > +#define RMNET_IP_VERSION_4 0x40 > +#define RMNET_IP_VERSION_6 0x60 > + > +/* Helper Functions */ > + > +/* __rmnet_set_skb_proto() - Set skb->protocol field > + * @skb: packet being modified > + * > + * Peek at the first byte of the packet and set the protocol. There is not > + * good way to determine if a packet has a MAP header. As of writing this, > + * the reserved bit in the MAP frame will prevent it from overlapping with > + * IPv4/IPv6 frames. This could change in the future! > + */ > +static inline void __rmnet_set_skb_proto(struct sk_buff *skb) > +{ > + switch (skb->data[0] & 0xF0) { > + case RMNET_IP_VERSION_4: > + skb->protocol = htons(ETH_P_IP); > + break; > + case RMNET_IP_VERSION_6: > + skb->protocol = htons(ETH_P_IPV6); > + break; > + default: > + skb->protocol = htons(ETH_P_MAP); > + break; > + } > +} > + > +#ifdef CONFIG_RMNET_DEBUG > +/* rmnet_print_packet() - Print packet / diagnostics > + * @skb: Packet to print > + * @printlen: Number of bytes to print > + * @dev: Name of interface > + * @dir: Character representing direction (e.g.. 'r' for receive) > + * > + * This function prints out raw bytes in an SKB. Use of this will have major > + * performance impacts and may even trigger watchdog resets if too much is > being > + * printed. Hence, this should always be compiled out unless absolutely > needed. > + */ > +void rmnet_print_packet(const struct sk_buff *skb, const char *dev, char dir) > +{ > + char buffer[200]; > + unsigned int len, printlen; > + int i, buffloc = 0; > + > + switch (dir) { > + case 'r': > + printlen = dump_pkt_rx; > + break; > + > + case 't': > + printlen = dump_pkt_tx; > + break; > + > + default: > + printlen = 0; > + break; > + } > + > + if (!printlen) > + return; > + > + pr_err("[%s][%c] - PKT skb->len=%d skb->head=%pK skb->data=%pK\n", > + dev, dir, skb->len, (void *)skb->head, (void *)skb->data); > + pr_err("[%s][%c] - PKT skb->tail=%pK skb->end=%pK\n", > + dev, dir, skb_tail_pointer(skb), skb_end_pointer(skb)); > + > + if (skb->len > 0) > + len = skb->len; > + else > + len = ((unsigned int)(uintptr_t)skb->end) - > + ((unsigned int)(uintptr_t)skb->data); > + > + pr_err("[%s][%c] - PKT len: %d, printing first %d bytes\n", > + dev, dir, len, printlen); > + > + memset(buffer, 0, sizeof(buffer)); > + for (i = 0; (i < printlen) && (i < len); i++) { > + if ((i % 16) == 0) { > + pr_err("[%s][%c] - PKT%s\n", dev, dir, buffer); > + memset(buffer, 0, sizeof(buffer)); > + buffloc = 0; > + buffloc += snprintf(&buffer[buffloc], > + sizeof(buffer) - buffloc, "%04X:", > + i); > + } > + > + buffloc += snprintf(&buffer[buffloc], sizeof(buffer) - buffloc, > + " %02x", skb->data[i]); If you really have to do this. Use hex_dump_bytes API. > + } > + pr_err("[%s][%c] - PKT%s\n", dev, dir, buffer); > +} > +#else > +void rmnet_print_packet(const struct sk_buff *skb, const char *dev, char dir) > +{ > +} > +#endif /* CONFIG_RMNET_DEBUG */ > + > +/* Generic handler */ > + > +/* rmnet_bridge_handler() - Bridge related functionality > + */ > +static rx_handler_result_t rmnet_bridge_handler > + (struct sk_buff *skb, struct rmnet_logical_ep_conf_s *ep) > +{ > + if (!ep->egress_dev) { > + LOGD("Missing egress device for packet arriving on %s", > + skb->dev->name); > + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_BRDG_NO_EGRESS); > + } else { > + rmnet_egress_handler(skb, ep); > + } > + > + return RX_HANDLER_CONSUMED; > +} > + > +#ifdef NET_SKBUFF_DATA_USES_OFFSET > +static void rmnet_reset_mac_header(struct sk_buff *skb) > +{ > + skb->mac_header = 0; > + skb->mac_len = 0; > +} Why not use sbk_set_mac_header(skb, 0)? > +#else > +static void rmnet_reset_mac_header(struct sk_buff *skb) > +{ > + skb->mac_header = skb->network_header; > + skb->mac_len = 0; > +} > +#endif /*NET_SKBUFF_DATA_USES_OFFSET*/ > + > +/* __rmnet_deliver_skb() - Deliver skb > + * > + * Determines where to deliver skb. Options are: consume by network stack, > + * pass to bridge handler, or pass to virtual network device > + */ > +static rx_handler_result_t __rmnet_deliver_skb > + (struct sk_buff *skb, struct rmnet_logical_ep_conf_s *ep) > +{ > + switch (ep->rmnet_mode) { > + case RMNET_EPMODE_NONE: > + return RX_HANDLER_PASS; > + > + case RMNET_EPMODE_BRIDGE: > + return rmnet_bridge_handler(skb, ep); > + > + case RMNET_EPMODE_VND: > + skb_reset_transport_header(skb); > + skb_reset_network_header(skb); > + switch (rmnet_vnd_rx_fixup(skb, skb->dev)) { > + case RX_HANDLER_CONSUMED: > + return RX_HANDLER_CONSUMED; > + > + case RX_HANDLER_PASS: > + skb->pkt_type = PACKET_HOST; > + rmnet_reset_mac_header(skb); > + netif_receive_skb(skb); > + return RX_HANDLER_CONSUMED; > + } > + return RX_HANDLER_PASS; > + > + default: > + LOGD("Unknown ep mode %d", ep->rmnet_mode); > + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_DELIVER_NO_EP); > + return RX_HANDLER_CONSUMED; > + } > +} > + > +/* rmnet_ingress_deliver_packet() - Ingress handler for raw IP and bridged > + * MAP packets. > + * @skb: Packet needing a destination. > + * @config: Physical end point configuration that the packet arrived on. > + */ > +static rx_handler_result_t rmnet_ingress_deliver_packet > + (struct sk_buff *skb, struct rmnet_phys_ep_conf_s *config) > +{ > + if (!config) { > + LOGD("%s", "NULL physical EP provided"); > + kfree_skb(skb); > + return RX_HANDLER_CONSUMED; > + } > + > + if (!(config->local_ep.refcount)) { > + LOGD("Packet on %s has no local endpoint configuration", > + skb->dev->name); > + rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_IPINGRESS_NO_EP); > + return RX_HANDLER_CONSUMED; > + } > + > + skb->dev = config->local_ep.egress_dev; > + > + return __rmnet_deliver_skb(skb, &config->local_ep); > +} > + > +/* MAP handler */