Loading drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +171 −20 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2013-2018, 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 * Loading @@ -21,6 +13,9 @@ #include "rmnet_handlers.h" #include "rmnet_vnd.h" #include "rmnet_private.h" #include "rmnet_map.h" #include <soc/qcom/rmnet_qmi.h> #include <soc/qcom/qmi_rmnet.h> /* Locking scheme - * The shared resource which needs to be protected is realdev->rx_handler_data. Loading @@ -43,15 +38,17 @@ /* Local Definitions and Declarations */ static const struct nla_policy rmnet_policy[IFLA_RMNET_MAX + 1] = { static const struct nla_policy rmnet_policy[IFLA_RMNET_MAX + 2] = { [IFLA_RMNET_MUX_ID] = { .type = NLA_U16 }, [IFLA_RMNET_FLAGS] = { .len = sizeof(struct ifla_rmnet_flags) }, [IFLA_VLAN_EGRESS_QOS] = { .len = sizeof(struct tcmsg) }, }; static int rmnet_is_real_dev_registered(const struct net_device *real_dev) int rmnet_is_real_dev_registered(const struct net_device *real_dev) { return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler; } EXPORT_SYMBOL(rmnet_is_real_dev_registered); /* Needs rtnl lock */ static struct rmnet_port* Loading @@ -66,6 +63,9 @@ static int rmnet_unregister_real_device(struct net_device *real_dev, if (port->nr_rmnet_devs) return -EINVAL; rmnet_map_cmd_exit(port); rmnet_map_tx_aggregate_exit(port); kfree(port); netdev_rx_handler_unregister(real_dev); Loading Loading @@ -97,13 +97,15 @@ static int rmnet_register_real_device(struct net_device *real_dev) kfree(port); return -EBUSY; } /* hold on to real dev for MAP data */ dev_hold(real_dev); for (entry = 0; entry < RMNET_MAX_LOGICAL_EP; entry++) INIT_HLIST_HEAD(&port->muxed_ep[entry]); rmnet_map_tx_aggregate_init(port); rmnet_map_cmd_init(port); netdev_dbg(real_dev, "registered with rmnet\n"); return 0; } Loading Loading @@ -211,6 +213,7 @@ static void rmnet_dellink(struct net_device *dev, struct list_head *head) hlist_del_init_rcu(&ep->hlnode); rmnet_unregister_bridge(dev, port); rmnet_vnd_dellink(mux_id, port, ep); synchronize_rcu(); kfree(ep); } rmnet_unregister_real_device(real_dev, port); Loading @@ -234,7 +237,6 @@ static void rmnet_force_unassociate_device(struct net_device *dev) port = rmnet_get_port_rtnl(dev); rcu_read_lock(); rmnet_unregister_bridge(dev, port); hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) { Loading @@ -242,12 +244,14 @@ static void rmnet_force_unassociate_device(struct net_device *dev) rmnet_vnd_dellink(ep->mux_id, port, ep); hlist_del_init_rcu(&ep->hlnode); synchronize_rcu(); kfree(ep); } rcu_read_unlock(); unregister_netdevice_many(&list); qmi_rmnet_qmi_exit(port->qmi_info, port); rmnet_unregister_real_device(real_dev, port); } Loading Loading @@ -281,12 +285,15 @@ static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[], { u16 mux_id; if (!data || !data[IFLA_RMNET_MUX_ID]) if (!data) { return -EINVAL; } else { if (data[IFLA_RMNET_MUX_ID]) { mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]); if (mux_id > (RMNET_MAX_LOGICAL_EP - 1)) return -ERANGE; } } return 0; } Loading Loading @@ -329,6 +336,13 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[], port->data_format = flags->flags & flags->mask; } if (data[IFLA_VLAN_EGRESS_QOS]) { struct tcmsg *tcm; tcm = nla_data(data[IFLA_VLAN_EGRESS_QOS]); qmi_rmnet_change_link(dev, port, tcm); } return 0; } Loading @@ -338,7 +352,8 @@ static size_t rmnet_get_size(const struct net_device *dev) /* IFLA_RMNET_MUX_ID */ nla_total_size(2) + /* IFLA_RMNET_FLAGS */ nla_total_size(sizeof(struct ifla_rmnet_flags)); nla_total_size(sizeof(struct ifla_rmnet_flags)) + nla_total_size(sizeof(struct tcmsg)); } static int rmnet_fill_info(struct sk_buff *skb, const struct net_device *dev) Loading Loading @@ -393,6 +408,7 @@ struct rmnet_port *rmnet_get_port(struct net_device *real_dev) else return NULL; } EXPORT_SYMBOL(rmnet_get_port); struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id) { Loading @@ -405,6 +421,7 @@ struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id) return NULL; } EXPORT_SYMBOL(rmnet_get_endpoint); int rmnet_add_bridge(struct net_device *rmnet_dev, struct net_device *slave_dev, Loading Loading @@ -459,6 +476,140 @@ int rmnet_del_bridge(struct net_device *rmnet_dev, return 0; } #ifdef CONFIG_QCOM_QMI_RMNET void *rmnet_get_qmi_pt(void *port) { if (port) return ((struct rmnet_port *)port)->qmi_info; return NULL; } EXPORT_SYMBOL(rmnet_get_qmi_pt); void *rmnet_get_qos_pt(struct net_device *dev) { struct rmnet_priv *priv; if (dev) { priv = netdev_priv(dev); return rcu_dereference(priv->qos_info); } return NULL; } EXPORT_SYMBOL(rmnet_get_qos_pt); void *rmnet_get_rmnet_port(struct net_device *dev) { struct rmnet_priv *priv; if (dev) { priv = netdev_priv(dev); return (void *)rmnet_get_port(priv->real_dev); } return NULL; } EXPORT_SYMBOL(rmnet_get_rmnet_port); struct net_device *rmnet_get_rmnet_dev(void *port, u8 mux_id) { struct rmnet_endpoint *ep; if (port) { ep = rmnet_get_endpoint((struct rmnet_port *)port, mux_id); if (ep) return ep->egress_dev; } return NULL; } EXPORT_SYMBOL(rmnet_get_rmnet_dev); void rmnet_reset_qmi_pt(void *port) { if (port) ((struct rmnet_port *)port)->qmi_info = NULL; } EXPORT_SYMBOL(rmnet_reset_qmi_pt); void rmnet_init_qmi_pt(void *port, void *qmi) { if (port) ((struct rmnet_port *)port)->qmi_info = qmi; } EXPORT_SYMBOL(rmnet_init_qmi_pt); void rmnet_get_packets(void *port, u64 *rx, u64 *tx) { struct rmnet_priv *priv; struct rmnet_pcpu_stats *ps; unsigned int cpu, start; struct rmnet_endpoint *ep; unsigned long bkt; if (!port || !tx || !rx) return; *tx = 0; *rx = 0; hash_for_each(((struct rmnet_port *)port)->muxed_ep, bkt, ep, hlnode) { priv = netdev_priv(ep->egress_dev); for_each_possible_cpu(cpu) { ps = per_cpu_ptr(priv->pcpu_stats, cpu); do { start = u64_stats_fetch_begin_irq(&ps->syncp); *tx += ps->stats.tx_pkts; *rx += ps->stats.rx_pkts; } while (u64_stats_fetch_retry_irq(&ps->syncp, start)); } } } EXPORT_SYMBOL(rmnet_get_packets); void rmnet_set_powersave_format(void *port) { if (!port) return; ((struct rmnet_port *)port)->data_format |= RMNET_INGRESS_FORMAT_PS; } EXPORT_SYMBOL(rmnet_set_powersave_format); void rmnet_clear_powersave_format(void *port) { if (!port) return; ((struct rmnet_port *)port)->data_format &= ~RMNET_INGRESS_FORMAT_PS; } EXPORT_SYMBOL(rmnet_clear_powersave_format); void rmnet_enable_all_flows(void *port) { struct rmnet_endpoint *ep; unsigned long bkt; if (unlikely(!port)) return; rcu_read_lock(); hash_for_each_rcu(((struct rmnet_port *)port)->muxed_ep, bkt, ep, hlnode) { qmi_rmnet_enable_all_flows(ep->egress_dev); } rcu_read_unlock(); } EXPORT_SYMBOL(rmnet_enable_all_flows); int rmnet_get_powersave_notif(void *port) { if (!port) return 0; return ((struct rmnet_port *)port)->data_format & RMNET_FORMAT_PS_NOTIF; } EXPORT_SYMBOL(rmnet_get_powersave_notif); #endif /* Startup/Shutdown */ static int __init rmnet_init(void) Loading drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h +53 −9 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2013-2014, 2016-2018 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 * Loading @@ -27,6 +19,20 @@ struct rmnet_endpoint { struct hlist_node hlnode; }; struct rmnet_port_priv_stats { u64 dl_hdr_last_seq; u64 dl_hdr_last_bytes; u64 dl_hdr_last_pkts; u64 dl_hdr_last_flows; u64 dl_hdr_count; u64 dl_hdr_total_bytes; u64 dl_hdr_total_pkts; u64 dl_hdr_avg_bytes; u64 dl_hdr_avg_pkts; u64 dl_trl_last_seq; u64 dl_trl_count; }; /* One instance of this structure is instantiated for each real_dev associated * with rmnet. */ Loading @@ -37,6 +43,27 @@ struct rmnet_port { u8 rmnet_mode; struct hlist_head muxed_ep[RMNET_MAX_LOGICAL_EP]; struct net_device *bridge_ep; void *rmnet_perf; u16 egress_agg_size; u16 egress_agg_count; /* Protect aggregation related elements */ spinlock_t agg_lock; struct sk_buff *agg_skb; int agg_state; u8 agg_count; struct timespec agg_time; struct timespec agg_last; struct hrtimer hrtimer; void *qmi_info; /* dl marker elements */ struct list_head dl_list; struct rmnet_port_priv_stats stats; int dl_marker_flush; }; extern struct rtnl_link_ops rmnet_link_ops; Loading Loading @@ -72,8 +99,25 @@ struct rmnet_priv { struct rmnet_pcpu_stats __percpu *pcpu_stats; struct gro_cells gro_cells; struct rmnet_priv_stats stats; void __rcu *qos_info; }; enum rmnet_trace_func { RMNET_MODULE, NW_STACK_MODULE, }; enum rmnet_trace_evt { RMNET_DLVR_SKB, RMNET_RCV_FROM_PND, RMNET_TX_UL_PKT, NW_STACK_DEV_Q_XMIT, NW_STACK_NAPI_GRO_FLUSH, NW_STACK_RX, NW_STACK_TX, }; int rmnet_is_real_dev_registered(const struct net_device *real_dev); struct rmnet_port *rmnet_get_port(struct net_device *real_dev); struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id); int rmnet_add_bridge(struct net_device *rmnet_dev, Loading drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +156 −23 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2013-2018, 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 * Loading @@ -16,19 +8,58 @@ #include <linux/netdevice.h> #include <linux/netdev_features.h> #include <linux/if_arp.h> #include <linux/ip.h> #include <linux/ipv6.h> #include <net/sock.h> #include <linux/tracepoint.h> #include "rmnet_private.h" #include "rmnet_config.h" #include "rmnet_vnd.h" #include "rmnet_map.h" #include "rmnet_handlers.h" #ifdef CONFIG_QCOM_QMI_HELPERS #include <soc/qcom/rmnet_qmi.h> #include <soc/qcom/qmi_rmnet.h> #endif #define RMNET_IP_VERSION_4 0x40 #define RMNET_IP_VERSION_6 0x60 #define CREATE_TRACE_POINTS #include <trace/events/rmnet.h> EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_low); EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_high); EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_err); EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_wq_low); EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_wq_high); EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_wq_err); EXPORT_TRACEPOINT_SYMBOL(rmnet_perf_low); EXPORT_TRACEPOINT_SYMBOL(rmnet_perf_high); EXPORT_TRACEPOINT_SYMBOL(rmnet_perf_err); EXPORT_TRACEPOINT_SYMBOL(rmnet_low); EXPORT_TRACEPOINT_SYMBOL(rmnet_high); EXPORT_TRACEPOINT_SYMBOL(rmnet_err); /* Helper Functions */ static void rmnet_set_skb_proto(struct sk_buff *skb) static int rmnet_check_skb_can_gro(struct sk_buff *skb) { switch (skb->protocol) { case htons(ETH_P_IP): if (ip_hdr(skb)->protocol == IPPROTO_TCP) return 0; break; case htons(ETH_P_IPV6): if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP) return 0; /* Fall through */ } return -EPROTONOSUPPORT; } void rmnet_set_skb_proto(struct sk_buff *skb) { switch (skb->data[0] & 0xF0) { case RMNET_IP_VERSION_4: Loading @@ -42,22 +73,55 @@ static void rmnet_set_skb_proto(struct sk_buff *skb) break; } } EXPORT_SYMBOL(rmnet_set_skb_proto); /* Shs hook handler */ int (*rmnet_shs_skb_entry)(struct sk_buff *skb, struct rmnet_port *port) __rcu __read_mostly; EXPORT_SYMBOL(rmnet_shs_skb_entry); /* Generic handler */ static void rmnet_deliver_skb(struct sk_buff *skb) void rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port) { int (*rmnet_shs_stamp)(struct sk_buff *skb, struct rmnet_port *port); struct rmnet_priv *priv = netdev_priv(skb->dev); trace_rmnet_low(RMNET_MODULE, RMNET_DLVR_SKB, 0xDEF, 0xDEF, 0xDEF, 0xDEF, (void *)skb, NULL); skb_reset_transport_header(skb); skb_reset_network_header(skb); rmnet_vnd_rx_fixup(skb, skb->dev); rmnet_vnd_rx_fixup(skb->dev, skb->len); skb->pkt_type = PACKET_HOST; skb_set_mac_header(skb, 0); rmnet_shs_stamp = rcu_dereference(rmnet_shs_skb_entry); if (rmnet_shs_stamp) { rmnet_shs_stamp(skb, port); return; } if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) { if (!rmnet_check_skb_can_gro(skb) && port->dl_marker_flush >= 0) { struct napi_struct *napi = get_current_napi_context(); napi_gro_receive(napi, skb); port->dl_marker_flush++; } else { netif_receive_skb(skb); } } else { if (!rmnet_check_skb_can_gro(skb)) gro_cells_receive(&priv->gro_cells, skb); else netif_receive_skb(skb); } } EXPORT_SYMBOL(rmnet_deliver_skb); /* MAP handler */ Loading @@ -70,6 +134,11 @@ __rmnet_map_ingress_handler(struct sk_buff *skb, u8 mux_id; if (RMNET_MAP_GET_CD_BIT(skb)) { if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) { if (!rmnet_map_flow_command(skb, port, false)) return; } if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS) return rmnet_map_command(skb, port); Loading Loading @@ -98,19 +167,27 @@ __rmnet_map_ingress_handler(struct sk_buff *skb, skb->ip_summed = CHECKSUM_UNNECESSARY; } #ifdef CONFIG_QCOM_QMI_HELPERS if (port->data_format & RMNET_INGRESS_FORMAT_PS) qmi_rmnet_work_maybe_restart(port); #endif skb_trim(skb, len); rmnet_deliver_skb(skb); rmnet_deliver_skb(skb, port); return; free_skb: kfree_skb(skb); } int (*rmnet_perf_deag_entry)(struct sk_buff *skb, struct rmnet_port *port) __rcu __read_mostly; EXPORT_SYMBOL(rmnet_perf_deag_entry); static void rmnet_map_ingress_handler(struct sk_buff *skb, struct rmnet_port *port) { struct sk_buff *skbn; if (skb->dev->type == ARPHRD_ETHER) { if (pskb_expand_head(skb, ETH_HLEN, 0, GFP_ATOMIC)) { Loading @@ -122,10 +199,30 @@ rmnet_map_ingress_handler(struct sk_buff *skb, } if (port->data_format & RMNET_FLAGS_INGRESS_DEAGGREGATION) { while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL) __rmnet_map_ingress_handler(skbn, port); int (*rmnet_perf_core_deaggregate)(struct sk_buff *skb, struct rmnet_port *port); /* Deaggregation and freeing of HW originating * buffers is done within here */ rmnet_perf_core_deaggregate = rcu_dereference(rmnet_perf_deag_entry); if (rmnet_perf_core_deaggregate) { rmnet_perf_core_deaggregate(skb, port); } else { struct sk_buff *skbn; while (skb) { struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list; skb_shinfo(skb)->frag_list = NULL; while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL) __rmnet_map_ingress_handler(skbn, port); consume_skb(skb); skb = skb_frag; } } } else { __rmnet_map_ingress_handler(skb, port); } Loading @@ -151,6 +248,11 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, return -ENOMEM; } #ifdef CONFIG_QCOM_QMI_HELPERS if (port->data_format & RMNET_INGRESS_FORMAT_PS) qmi_rmnet_work_maybe_restart(port); #endif if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) rmnet_map_checksum_uplink_packet(skb, orig_dev); Loading @@ -160,8 +262,26 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, map_header->mux_id = mux_id; skb->protocol = htons(ETH_P_MAP); if (port->data_format & RMNET_EGRESS_FORMAT_AGGREGATION) { int non_linear_skb; if (rmnet_map_tx_agg_skip(skb, required_headroom)) goto done; non_linear_skb = (orig_dev->features & NETIF_F_GSO) && skb_is_nonlinear(skb); if (non_linear_skb) { if (unlikely(__skb_linearize(skb))) goto done; } rmnet_map_tx_aggregate(skb, port); return -EINPROGRESS; } done: skb->protocol = htons(ETH_P_MAP); return 0; } Loading Loading @@ -192,6 +312,8 @@ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) if (skb->pkt_type == PACKET_LOOPBACK) return RX_HANDLER_PASS; trace_rmnet_low(RMNET_MODULE, RMNET_RCV_FROM_PND, 0xDEF, 0xDEF, 0xDEF, 0xDEF, NULL, NULL); dev = skb->dev; port = rmnet_get_port(dev); Loading @@ -207,6 +329,7 @@ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) done: return RX_HANDLER_CONSUMED; } EXPORT_SYMBOL(rmnet_rx_handler); /* Modifies packet as per logical endpoint configuration and egress data format * for egress device configured in logical endpoint. Packet is then transmitted Loading @@ -218,7 +341,11 @@ void rmnet_egress_handler(struct sk_buff *skb) struct rmnet_port *port; struct rmnet_priv *priv; u8 mux_id; int err; u32 skb_len; trace_rmnet_low(RMNET_MODULE, RMNET_TX_UL_PKT, 0xDEF, 0xDEF, 0xDEF, 0xDEF, (void *)skb, NULL); sk_pacing_shift_update(skb->sk, 8); orig_dev = skb->dev; Loading @@ -230,10 +357,16 @@ void rmnet_egress_handler(struct sk_buff *skb) if (!port) goto drop; if (rmnet_map_egress_handler(skb, port, mux_id, orig_dev)) skb_len = skb->len; err = rmnet_map_egress_handler(skb, port, mux_id, orig_dev); if (err == -ENOMEM) { goto drop; } else if (err == -EINPROGRESS) { rmnet_vnd_tx_fixup(orig_dev, skb_len); return; } rmnet_vnd_tx_fixup(skb, orig_dev); rmnet_vnd_tx_fixup(orig_dev, skb_len); dev_queue_xmit(skb); return; Loading drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h +6 −11 Original line number Diff line number Diff line /* Copyright (c) 2013, 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. /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2013, 2016-2018 The Linux Foundation. All rights reserved. * * RMNET Data ingress/egress handler * Loading @@ -19,7 +11,10 @@ #include "rmnet_config.h" void rmnet_egress_handler(struct sk_buff *skb); void rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port); void rmnet_set_skb_proto(struct sk_buff *skb); rx_handler_result_t _rmnet_map_ingress_handler(struct sk_buff *skb, struct rmnet_port *port); rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb); #endif /* _RMNET_HANDLERS_H_ */ drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +75 −12 Original line number Diff line number Diff line /* Copyright (c) 2013-2018, 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. */ /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. */ #ifndef _RMNET_MAP_H_ #define _RMNET_MAP_H_ #include "rmnet_config.h" struct rmnet_map_control_command { u8 command_name; Loading @@ -34,6 +26,8 @@ enum rmnet_map_commands { RMNET_MAP_COMMAND_NONE, RMNET_MAP_COMMAND_FLOW_DISABLE, RMNET_MAP_COMMAND_FLOW_ENABLE, RMNET_MAP_COMMAND_FLOW_START = 7, RMNET_MAP_COMMAND_FLOW_END = 8, /* These should always be the last 2 elements */ RMNET_MAP_COMMAND_UNKNOWN, RMNET_MAP_COMMAND_ENUM_LENGTH Loading Loading @@ -63,6 +57,60 @@ struct rmnet_map_ul_csum_header { u16 csum_enabled:1; } __aligned(1); struct rmnet_map_control_command_header { u8 command_name; u8 cmd_type:2; u8 reserved:6; u16 reserved2; u32 transaction_id; } __aligned(1); struct rmnet_map_flow_info_le { __be32 mux_id; __be32 flow_id; __be32 bytes; __be32 pkts; } __aligned(1); struct rmnet_map_flow_info_be { u32 mux_id; u32 flow_id; u32 bytes; u32 pkts; } __aligned(1); struct rmnet_map_dl_ind_hdr { union { struct { u32 seq; u32 bytes; u32 pkts; u32 flows; struct rmnet_map_flow_info_le flow[0]; } le __aligned(1); struct { __be32 seq; __be32 bytes; __be32 pkts; __be32 flows; struct rmnet_map_flow_info_be flow[0]; } be __aligned(1); } __aligned(1); } __aligned(1); struct rmnet_map_dl_ind_trl { union { __be32 seq_be; u32 seq_le; } __aligned(1); } __aligned(1); struct rmnet_map_dl_ind { void (*dl_hdr_handler)(struct rmnet_map_dl_ind_hdr *dlhdr); void (*dl_trl_handler)(struct rmnet_map_dl_ind_trl *dltrl); struct list_head list; }; #define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \ (Y)->data)->mux_id) #define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \ Loading @@ -75,6 +123,9 @@ struct rmnet_map_ul_csum_header { #define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header *) \ (Y)->data)->pkt_len)) #define RMNET_MAP_DEAGGR_SPACING 64 #define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2) #define RMNET_MAP_COMMAND_REQUEST 0 #define RMNET_MAP_COMMAND_ACK 1 #define RMNET_MAP_COMMAND_UNSUPPORTED 2 Loading @@ -91,5 +142,17 @@ void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port); int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len); void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, struct net_device *orig_dev); int rmnet_map_tx_agg_skip(struct sk_buff *skb, int offset); void rmnet_map_tx_aggregate(struct sk_buff *skb, struct rmnet_port *port); void rmnet_map_tx_aggregate_init(struct rmnet_port *port); void rmnet_map_tx_aggregate_exit(struct rmnet_port *port); int rmnet_map_flow_command(struct sk_buff *skb, struct rmnet_port *port, bool rmnet_perf); void rmnet_map_cmd_init(struct rmnet_port *port); int rmnet_map_dl_ind_register(struct rmnet_port *port, struct rmnet_map_dl_ind *dl_ind); int rmnet_map_dl_ind_deregister(struct rmnet_port *port, struct rmnet_map_dl_ind *dl_ind); void rmnet_map_cmd_exit(struct rmnet_port *port); #endif /* _RMNET_MAP_H_ */ Loading
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +171 −20 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2013-2018, 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 * Loading @@ -21,6 +13,9 @@ #include "rmnet_handlers.h" #include "rmnet_vnd.h" #include "rmnet_private.h" #include "rmnet_map.h" #include <soc/qcom/rmnet_qmi.h> #include <soc/qcom/qmi_rmnet.h> /* Locking scheme - * The shared resource which needs to be protected is realdev->rx_handler_data. Loading @@ -43,15 +38,17 @@ /* Local Definitions and Declarations */ static const struct nla_policy rmnet_policy[IFLA_RMNET_MAX + 1] = { static const struct nla_policy rmnet_policy[IFLA_RMNET_MAX + 2] = { [IFLA_RMNET_MUX_ID] = { .type = NLA_U16 }, [IFLA_RMNET_FLAGS] = { .len = sizeof(struct ifla_rmnet_flags) }, [IFLA_VLAN_EGRESS_QOS] = { .len = sizeof(struct tcmsg) }, }; static int rmnet_is_real_dev_registered(const struct net_device *real_dev) int rmnet_is_real_dev_registered(const struct net_device *real_dev) { return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler; } EXPORT_SYMBOL(rmnet_is_real_dev_registered); /* Needs rtnl lock */ static struct rmnet_port* Loading @@ -66,6 +63,9 @@ static int rmnet_unregister_real_device(struct net_device *real_dev, if (port->nr_rmnet_devs) return -EINVAL; rmnet_map_cmd_exit(port); rmnet_map_tx_aggregate_exit(port); kfree(port); netdev_rx_handler_unregister(real_dev); Loading Loading @@ -97,13 +97,15 @@ static int rmnet_register_real_device(struct net_device *real_dev) kfree(port); return -EBUSY; } /* hold on to real dev for MAP data */ dev_hold(real_dev); for (entry = 0; entry < RMNET_MAX_LOGICAL_EP; entry++) INIT_HLIST_HEAD(&port->muxed_ep[entry]); rmnet_map_tx_aggregate_init(port); rmnet_map_cmd_init(port); netdev_dbg(real_dev, "registered with rmnet\n"); return 0; } Loading Loading @@ -211,6 +213,7 @@ static void rmnet_dellink(struct net_device *dev, struct list_head *head) hlist_del_init_rcu(&ep->hlnode); rmnet_unregister_bridge(dev, port); rmnet_vnd_dellink(mux_id, port, ep); synchronize_rcu(); kfree(ep); } rmnet_unregister_real_device(real_dev, port); Loading @@ -234,7 +237,6 @@ static void rmnet_force_unassociate_device(struct net_device *dev) port = rmnet_get_port_rtnl(dev); rcu_read_lock(); rmnet_unregister_bridge(dev, port); hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) { Loading @@ -242,12 +244,14 @@ static void rmnet_force_unassociate_device(struct net_device *dev) rmnet_vnd_dellink(ep->mux_id, port, ep); hlist_del_init_rcu(&ep->hlnode); synchronize_rcu(); kfree(ep); } rcu_read_unlock(); unregister_netdevice_many(&list); qmi_rmnet_qmi_exit(port->qmi_info, port); rmnet_unregister_real_device(real_dev, port); } Loading Loading @@ -281,12 +285,15 @@ static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[], { u16 mux_id; if (!data || !data[IFLA_RMNET_MUX_ID]) if (!data) { return -EINVAL; } else { if (data[IFLA_RMNET_MUX_ID]) { mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]); if (mux_id > (RMNET_MAX_LOGICAL_EP - 1)) return -ERANGE; } } return 0; } Loading Loading @@ -329,6 +336,13 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[], port->data_format = flags->flags & flags->mask; } if (data[IFLA_VLAN_EGRESS_QOS]) { struct tcmsg *tcm; tcm = nla_data(data[IFLA_VLAN_EGRESS_QOS]); qmi_rmnet_change_link(dev, port, tcm); } return 0; } Loading @@ -338,7 +352,8 @@ static size_t rmnet_get_size(const struct net_device *dev) /* IFLA_RMNET_MUX_ID */ nla_total_size(2) + /* IFLA_RMNET_FLAGS */ nla_total_size(sizeof(struct ifla_rmnet_flags)); nla_total_size(sizeof(struct ifla_rmnet_flags)) + nla_total_size(sizeof(struct tcmsg)); } static int rmnet_fill_info(struct sk_buff *skb, const struct net_device *dev) Loading Loading @@ -393,6 +408,7 @@ struct rmnet_port *rmnet_get_port(struct net_device *real_dev) else return NULL; } EXPORT_SYMBOL(rmnet_get_port); struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id) { Loading @@ -405,6 +421,7 @@ struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id) return NULL; } EXPORT_SYMBOL(rmnet_get_endpoint); int rmnet_add_bridge(struct net_device *rmnet_dev, struct net_device *slave_dev, Loading Loading @@ -459,6 +476,140 @@ int rmnet_del_bridge(struct net_device *rmnet_dev, return 0; } #ifdef CONFIG_QCOM_QMI_RMNET void *rmnet_get_qmi_pt(void *port) { if (port) return ((struct rmnet_port *)port)->qmi_info; return NULL; } EXPORT_SYMBOL(rmnet_get_qmi_pt); void *rmnet_get_qos_pt(struct net_device *dev) { struct rmnet_priv *priv; if (dev) { priv = netdev_priv(dev); return rcu_dereference(priv->qos_info); } return NULL; } EXPORT_SYMBOL(rmnet_get_qos_pt); void *rmnet_get_rmnet_port(struct net_device *dev) { struct rmnet_priv *priv; if (dev) { priv = netdev_priv(dev); return (void *)rmnet_get_port(priv->real_dev); } return NULL; } EXPORT_SYMBOL(rmnet_get_rmnet_port); struct net_device *rmnet_get_rmnet_dev(void *port, u8 mux_id) { struct rmnet_endpoint *ep; if (port) { ep = rmnet_get_endpoint((struct rmnet_port *)port, mux_id); if (ep) return ep->egress_dev; } return NULL; } EXPORT_SYMBOL(rmnet_get_rmnet_dev); void rmnet_reset_qmi_pt(void *port) { if (port) ((struct rmnet_port *)port)->qmi_info = NULL; } EXPORT_SYMBOL(rmnet_reset_qmi_pt); void rmnet_init_qmi_pt(void *port, void *qmi) { if (port) ((struct rmnet_port *)port)->qmi_info = qmi; } EXPORT_SYMBOL(rmnet_init_qmi_pt); void rmnet_get_packets(void *port, u64 *rx, u64 *tx) { struct rmnet_priv *priv; struct rmnet_pcpu_stats *ps; unsigned int cpu, start; struct rmnet_endpoint *ep; unsigned long bkt; if (!port || !tx || !rx) return; *tx = 0; *rx = 0; hash_for_each(((struct rmnet_port *)port)->muxed_ep, bkt, ep, hlnode) { priv = netdev_priv(ep->egress_dev); for_each_possible_cpu(cpu) { ps = per_cpu_ptr(priv->pcpu_stats, cpu); do { start = u64_stats_fetch_begin_irq(&ps->syncp); *tx += ps->stats.tx_pkts; *rx += ps->stats.rx_pkts; } while (u64_stats_fetch_retry_irq(&ps->syncp, start)); } } } EXPORT_SYMBOL(rmnet_get_packets); void rmnet_set_powersave_format(void *port) { if (!port) return; ((struct rmnet_port *)port)->data_format |= RMNET_INGRESS_FORMAT_PS; } EXPORT_SYMBOL(rmnet_set_powersave_format); void rmnet_clear_powersave_format(void *port) { if (!port) return; ((struct rmnet_port *)port)->data_format &= ~RMNET_INGRESS_FORMAT_PS; } EXPORT_SYMBOL(rmnet_clear_powersave_format); void rmnet_enable_all_flows(void *port) { struct rmnet_endpoint *ep; unsigned long bkt; if (unlikely(!port)) return; rcu_read_lock(); hash_for_each_rcu(((struct rmnet_port *)port)->muxed_ep, bkt, ep, hlnode) { qmi_rmnet_enable_all_flows(ep->egress_dev); } rcu_read_unlock(); } EXPORT_SYMBOL(rmnet_enable_all_flows); int rmnet_get_powersave_notif(void *port) { if (!port) return 0; return ((struct rmnet_port *)port)->data_format & RMNET_FORMAT_PS_NOTIF; } EXPORT_SYMBOL(rmnet_get_powersave_notif); #endif /* Startup/Shutdown */ static int __init rmnet_init(void) Loading
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h +53 −9 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2013-2014, 2016-2018 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 * Loading @@ -27,6 +19,20 @@ struct rmnet_endpoint { struct hlist_node hlnode; }; struct rmnet_port_priv_stats { u64 dl_hdr_last_seq; u64 dl_hdr_last_bytes; u64 dl_hdr_last_pkts; u64 dl_hdr_last_flows; u64 dl_hdr_count; u64 dl_hdr_total_bytes; u64 dl_hdr_total_pkts; u64 dl_hdr_avg_bytes; u64 dl_hdr_avg_pkts; u64 dl_trl_last_seq; u64 dl_trl_count; }; /* One instance of this structure is instantiated for each real_dev associated * with rmnet. */ Loading @@ -37,6 +43,27 @@ struct rmnet_port { u8 rmnet_mode; struct hlist_head muxed_ep[RMNET_MAX_LOGICAL_EP]; struct net_device *bridge_ep; void *rmnet_perf; u16 egress_agg_size; u16 egress_agg_count; /* Protect aggregation related elements */ spinlock_t agg_lock; struct sk_buff *agg_skb; int agg_state; u8 agg_count; struct timespec agg_time; struct timespec agg_last; struct hrtimer hrtimer; void *qmi_info; /* dl marker elements */ struct list_head dl_list; struct rmnet_port_priv_stats stats; int dl_marker_flush; }; extern struct rtnl_link_ops rmnet_link_ops; Loading Loading @@ -72,8 +99,25 @@ struct rmnet_priv { struct rmnet_pcpu_stats __percpu *pcpu_stats; struct gro_cells gro_cells; struct rmnet_priv_stats stats; void __rcu *qos_info; }; enum rmnet_trace_func { RMNET_MODULE, NW_STACK_MODULE, }; enum rmnet_trace_evt { RMNET_DLVR_SKB, RMNET_RCV_FROM_PND, RMNET_TX_UL_PKT, NW_STACK_DEV_Q_XMIT, NW_STACK_NAPI_GRO_FLUSH, NW_STACK_RX, NW_STACK_TX, }; int rmnet_is_real_dev_registered(const struct net_device *real_dev); struct rmnet_port *rmnet_get_port(struct net_device *real_dev); struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id); int rmnet_add_bridge(struct net_device *rmnet_dev, Loading
drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +156 −23 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2013-2018, 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 * Loading @@ -16,19 +8,58 @@ #include <linux/netdevice.h> #include <linux/netdev_features.h> #include <linux/if_arp.h> #include <linux/ip.h> #include <linux/ipv6.h> #include <net/sock.h> #include <linux/tracepoint.h> #include "rmnet_private.h" #include "rmnet_config.h" #include "rmnet_vnd.h" #include "rmnet_map.h" #include "rmnet_handlers.h" #ifdef CONFIG_QCOM_QMI_HELPERS #include <soc/qcom/rmnet_qmi.h> #include <soc/qcom/qmi_rmnet.h> #endif #define RMNET_IP_VERSION_4 0x40 #define RMNET_IP_VERSION_6 0x60 #define CREATE_TRACE_POINTS #include <trace/events/rmnet.h> EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_low); EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_high); EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_err); EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_wq_low); EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_wq_high); EXPORT_TRACEPOINT_SYMBOL(rmnet_shs_wq_err); EXPORT_TRACEPOINT_SYMBOL(rmnet_perf_low); EXPORT_TRACEPOINT_SYMBOL(rmnet_perf_high); EXPORT_TRACEPOINT_SYMBOL(rmnet_perf_err); EXPORT_TRACEPOINT_SYMBOL(rmnet_low); EXPORT_TRACEPOINT_SYMBOL(rmnet_high); EXPORT_TRACEPOINT_SYMBOL(rmnet_err); /* Helper Functions */ static void rmnet_set_skb_proto(struct sk_buff *skb) static int rmnet_check_skb_can_gro(struct sk_buff *skb) { switch (skb->protocol) { case htons(ETH_P_IP): if (ip_hdr(skb)->protocol == IPPROTO_TCP) return 0; break; case htons(ETH_P_IPV6): if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP) return 0; /* Fall through */ } return -EPROTONOSUPPORT; } void rmnet_set_skb_proto(struct sk_buff *skb) { switch (skb->data[0] & 0xF0) { case RMNET_IP_VERSION_4: Loading @@ -42,22 +73,55 @@ static void rmnet_set_skb_proto(struct sk_buff *skb) break; } } EXPORT_SYMBOL(rmnet_set_skb_proto); /* Shs hook handler */ int (*rmnet_shs_skb_entry)(struct sk_buff *skb, struct rmnet_port *port) __rcu __read_mostly; EXPORT_SYMBOL(rmnet_shs_skb_entry); /* Generic handler */ static void rmnet_deliver_skb(struct sk_buff *skb) void rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port) { int (*rmnet_shs_stamp)(struct sk_buff *skb, struct rmnet_port *port); struct rmnet_priv *priv = netdev_priv(skb->dev); trace_rmnet_low(RMNET_MODULE, RMNET_DLVR_SKB, 0xDEF, 0xDEF, 0xDEF, 0xDEF, (void *)skb, NULL); skb_reset_transport_header(skb); skb_reset_network_header(skb); rmnet_vnd_rx_fixup(skb, skb->dev); rmnet_vnd_rx_fixup(skb->dev, skb->len); skb->pkt_type = PACKET_HOST; skb_set_mac_header(skb, 0); rmnet_shs_stamp = rcu_dereference(rmnet_shs_skb_entry); if (rmnet_shs_stamp) { rmnet_shs_stamp(skb, port); return; } if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) { if (!rmnet_check_skb_can_gro(skb) && port->dl_marker_flush >= 0) { struct napi_struct *napi = get_current_napi_context(); napi_gro_receive(napi, skb); port->dl_marker_flush++; } else { netif_receive_skb(skb); } } else { if (!rmnet_check_skb_can_gro(skb)) gro_cells_receive(&priv->gro_cells, skb); else netif_receive_skb(skb); } } EXPORT_SYMBOL(rmnet_deliver_skb); /* MAP handler */ Loading @@ -70,6 +134,11 @@ __rmnet_map_ingress_handler(struct sk_buff *skb, u8 mux_id; if (RMNET_MAP_GET_CD_BIT(skb)) { if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) { if (!rmnet_map_flow_command(skb, port, false)) return; } if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS) return rmnet_map_command(skb, port); Loading Loading @@ -98,19 +167,27 @@ __rmnet_map_ingress_handler(struct sk_buff *skb, skb->ip_summed = CHECKSUM_UNNECESSARY; } #ifdef CONFIG_QCOM_QMI_HELPERS if (port->data_format & RMNET_INGRESS_FORMAT_PS) qmi_rmnet_work_maybe_restart(port); #endif skb_trim(skb, len); rmnet_deliver_skb(skb); rmnet_deliver_skb(skb, port); return; free_skb: kfree_skb(skb); } int (*rmnet_perf_deag_entry)(struct sk_buff *skb, struct rmnet_port *port) __rcu __read_mostly; EXPORT_SYMBOL(rmnet_perf_deag_entry); static void rmnet_map_ingress_handler(struct sk_buff *skb, struct rmnet_port *port) { struct sk_buff *skbn; if (skb->dev->type == ARPHRD_ETHER) { if (pskb_expand_head(skb, ETH_HLEN, 0, GFP_ATOMIC)) { Loading @@ -122,10 +199,30 @@ rmnet_map_ingress_handler(struct sk_buff *skb, } if (port->data_format & RMNET_FLAGS_INGRESS_DEAGGREGATION) { while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL) __rmnet_map_ingress_handler(skbn, port); int (*rmnet_perf_core_deaggregate)(struct sk_buff *skb, struct rmnet_port *port); /* Deaggregation and freeing of HW originating * buffers is done within here */ rmnet_perf_core_deaggregate = rcu_dereference(rmnet_perf_deag_entry); if (rmnet_perf_core_deaggregate) { rmnet_perf_core_deaggregate(skb, port); } else { struct sk_buff *skbn; while (skb) { struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list; skb_shinfo(skb)->frag_list = NULL; while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL) __rmnet_map_ingress_handler(skbn, port); consume_skb(skb); skb = skb_frag; } } } else { __rmnet_map_ingress_handler(skb, port); } Loading @@ -151,6 +248,11 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, return -ENOMEM; } #ifdef CONFIG_QCOM_QMI_HELPERS if (port->data_format & RMNET_INGRESS_FORMAT_PS) qmi_rmnet_work_maybe_restart(port); #endif if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) rmnet_map_checksum_uplink_packet(skb, orig_dev); Loading @@ -160,8 +262,26 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, map_header->mux_id = mux_id; skb->protocol = htons(ETH_P_MAP); if (port->data_format & RMNET_EGRESS_FORMAT_AGGREGATION) { int non_linear_skb; if (rmnet_map_tx_agg_skip(skb, required_headroom)) goto done; non_linear_skb = (orig_dev->features & NETIF_F_GSO) && skb_is_nonlinear(skb); if (non_linear_skb) { if (unlikely(__skb_linearize(skb))) goto done; } rmnet_map_tx_aggregate(skb, port); return -EINPROGRESS; } done: skb->protocol = htons(ETH_P_MAP); return 0; } Loading Loading @@ -192,6 +312,8 @@ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) if (skb->pkt_type == PACKET_LOOPBACK) return RX_HANDLER_PASS; trace_rmnet_low(RMNET_MODULE, RMNET_RCV_FROM_PND, 0xDEF, 0xDEF, 0xDEF, 0xDEF, NULL, NULL); dev = skb->dev; port = rmnet_get_port(dev); Loading @@ -207,6 +329,7 @@ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) done: return RX_HANDLER_CONSUMED; } EXPORT_SYMBOL(rmnet_rx_handler); /* Modifies packet as per logical endpoint configuration and egress data format * for egress device configured in logical endpoint. Packet is then transmitted Loading @@ -218,7 +341,11 @@ void rmnet_egress_handler(struct sk_buff *skb) struct rmnet_port *port; struct rmnet_priv *priv; u8 mux_id; int err; u32 skb_len; trace_rmnet_low(RMNET_MODULE, RMNET_TX_UL_PKT, 0xDEF, 0xDEF, 0xDEF, 0xDEF, (void *)skb, NULL); sk_pacing_shift_update(skb->sk, 8); orig_dev = skb->dev; Loading @@ -230,10 +357,16 @@ void rmnet_egress_handler(struct sk_buff *skb) if (!port) goto drop; if (rmnet_map_egress_handler(skb, port, mux_id, orig_dev)) skb_len = skb->len; err = rmnet_map_egress_handler(skb, port, mux_id, orig_dev); if (err == -ENOMEM) { goto drop; } else if (err == -EINPROGRESS) { rmnet_vnd_tx_fixup(orig_dev, skb_len); return; } rmnet_vnd_tx_fixup(skb, orig_dev); rmnet_vnd_tx_fixup(orig_dev, skb_len); dev_queue_xmit(skb); return; Loading
drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h +6 −11 Original line number Diff line number Diff line /* Copyright (c) 2013, 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. /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2013, 2016-2018 The Linux Foundation. All rights reserved. * * RMNET Data ingress/egress handler * Loading @@ -19,7 +11,10 @@ #include "rmnet_config.h" void rmnet_egress_handler(struct sk_buff *skb); void rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port); void rmnet_set_skb_proto(struct sk_buff *skb); rx_handler_result_t _rmnet_map_ingress_handler(struct sk_buff *skb, struct rmnet_port *port); rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb); #endif /* _RMNET_HANDLERS_H_ */
drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +75 −12 Original line number Diff line number Diff line /* Copyright (c) 2013-2018, 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. */ /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. */ #ifndef _RMNET_MAP_H_ #define _RMNET_MAP_H_ #include "rmnet_config.h" struct rmnet_map_control_command { u8 command_name; Loading @@ -34,6 +26,8 @@ enum rmnet_map_commands { RMNET_MAP_COMMAND_NONE, RMNET_MAP_COMMAND_FLOW_DISABLE, RMNET_MAP_COMMAND_FLOW_ENABLE, RMNET_MAP_COMMAND_FLOW_START = 7, RMNET_MAP_COMMAND_FLOW_END = 8, /* These should always be the last 2 elements */ RMNET_MAP_COMMAND_UNKNOWN, RMNET_MAP_COMMAND_ENUM_LENGTH Loading Loading @@ -63,6 +57,60 @@ struct rmnet_map_ul_csum_header { u16 csum_enabled:1; } __aligned(1); struct rmnet_map_control_command_header { u8 command_name; u8 cmd_type:2; u8 reserved:6; u16 reserved2; u32 transaction_id; } __aligned(1); struct rmnet_map_flow_info_le { __be32 mux_id; __be32 flow_id; __be32 bytes; __be32 pkts; } __aligned(1); struct rmnet_map_flow_info_be { u32 mux_id; u32 flow_id; u32 bytes; u32 pkts; } __aligned(1); struct rmnet_map_dl_ind_hdr { union { struct { u32 seq; u32 bytes; u32 pkts; u32 flows; struct rmnet_map_flow_info_le flow[0]; } le __aligned(1); struct { __be32 seq; __be32 bytes; __be32 pkts; __be32 flows; struct rmnet_map_flow_info_be flow[0]; } be __aligned(1); } __aligned(1); } __aligned(1); struct rmnet_map_dl_ind_trl { union { __be32 seq_be; u32 seq_le; } __aligned(1); } __aligned(1); struct rmnet_map_dl_ind { void (*dl_hdr_handler)(struct rmnet_map_dl_ind_hdr *dlhdr); void (*dl_trl_handler)(struct rmnet_map_dl_ind_trl *dltrl); struct list_head list; }; #define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \ (Y)->data)->mux_id) #define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \ Loading @@ -75,6 +123,9 @@ struct rmnet_map_ul_csum_header { #define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header *) \ (Y)->data)->pkt_len)) #define RMNET_MAP_DEAGGR_SPACING 64 #define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2) #define RMNET_MAP_COMMAND_REQUEST 0 #define RMNET_MAP_COMMAND_ACK 1 #define RMNET_MAP_COMMAND_UNSUPPORTED 2 Loading @@ -91,5 +142,17 @@ void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port); int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len); void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, struct net_device *orig_dev); int rmnet_map_tx_agg_skip(struct sk_buff *skb, int offset); void rmnet_map_tx_aggregate(struct sk_buff *skb, struct rmnet_port *port); void rmnet_map_tx_aggregate_init(struct rmnet_port *port); void rmnet_map_tx_aggregate_exit(struct rmnet_port *port); int rmnet_map_flow_command(struct sk_buff *skb, struct rmnet_port *port, bool rmnet_perf); void rmnet_map_cmd_init(struct rmnet_port *port); int rmnet_map_dl_ind_register(struct rmnet_port *port, struct rmnet_map_dl_ind *dl_ind); int rmnet_map_dl_ind_deregister(struct rmnet_port *port, struct rmnet_map_dl_ind *dl_ind); void rmnet_map_cmd_exit(struct rmnet_port *port); #endif /* _RMNET_MAP_H_ */