diff options
author | Subash Abhinov Kasiviswanathan <subashab@codeaurora.org> | 2020-03-18 15:48:57 -0700 |
---|---|---|
committer | Subash Abhinov Kasiviswanathan <subashab@codeaurora.org> | 2020-03-18 15:51:37 -0700 |
commit | c390153e4f08c5b5fe985a0afc9f73c559ab0441 (patch) | |
tree | bb9ed1b4efeb0a91c26097f808c18694488f0eed | |
parent | af399a1677059208970e9f71ffa6bccb1f3db770 (diff) | |
download | data-kernel-c390153e4f08c5b5fe985a0afc9f73c559ab0441.tar.gz |
drivers: rmnet: shs: Snapshot of data.lnx.5.1
Snapshot of shs driver on data.lnx.5.1 up to the following
change id.
drivers: rmnet: shs: Unrevert Deadlock fix
I1307d82ffa12d0cc1115baa25a19df8ada924e89
Change-Id: I868f2fff8a90d1e99860803c994cee0f69af60b2
Acked-by: Raul Martinez <mraul@qti.qualcomm.com>
Signed-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
-rw-r--r-- | drivers/rmnet/shs/rmnet_shs_config.h | 3 | ||||
-rwxr-xr-x | drivers/rmnet/shs/rmnet_shs_main.c | 20 | ||||
-rw-r--r-- | drivers/rmnet/shs/rmnet_shs_wq.c | 67 | ||||
-rw-r--r-- | drivers/rmnet/shs/rmnet_shs_wq.h | 9 | ||||
-rw-r--r-- | drivers/rmnet/shs/rmnet_shs_wq_genl.c | 4 | ||||
-rw-r--r-- | drivers/rmnet/shs/rmnet_shs_wq_genl.h | 3 | ||||
-rw-r--r-- | drivers/rmnet/shs/rmnet_shs_wq_mem.c | 403 | ||||
-rw-r--r-- | drivers/rmnet/shs/rmnet_shs_wq_mem.h | 29 |
8 files changed, 458 insertions, 80 deletions
diff --git a/drivers/rmnet/shs/rmnet_shs_config.h b/drivers/rmnet/shs/rmnet_shs_config.h index dc385e4..e55f5f8 100644 --- a/drivers/rmnet/shs/rmnet_shs_config.h +++ b/drivers/rmnet/shs/rmnet_shs_config.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2020 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 @@ -46,6 +46,7 @@ enum rmnet_shs_crit_err_e { RMNET_SHS_WQ_INVALID_PTR_ERR, RMNET_SHS_WQ_NODE_MALLOC_ERR, RMNET_SHS_WQ_NL_SOCKET_ERR, + RMNET_SHS_CPU_FLOWS_BNDS_ERR, RMNET_SHS_CRIT_ERR_MAX }; diff --git a/drivers/rmnet/shs/rmnet_shs_main.c b/drivers/rmnet/shs/rmnet_shs_main.c index 60ba7ed..bb2f175 100755 --- a/drivers/rmnet/shs/rmnet_shs_main.c +++ b/drivers/rmnet/shs/rmnet_shs_main.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2020 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 @@ -44,6 +44,8 @@ #define GET_CTIMER(CPU) rmnet_shs_cfg.core_flush[CPU].core_timer #define SKB_FLUSH 0 +#define INCREMENT 1 +#define DECREMENT 0 /* Local Definitions and Declarations */ DEFINE_SPINLOCK(rmnet_shs_ht_splock); DEFINE_HASHTABLE(RMNET_SHS_HT, RMNET_SHS_HT_SIZE); @@ -125,13 +127,21 @@ unsigned int rmnet_shs_cpu_max_coresum[MAX_CPUS]; module_param_array(rmnet_shs_cpu_max_coresum, uint, 0, 0644); MODULE_PARM_DESC(rmnet_shs_cpu_max_coresum, "Max coresum seen of each core"); +static void rmnet_shs_change_cpu_num_flows(u16 map_cpu, bool inc) +{ + if (map_cpu < MAX_CPUS) + (inc) ? cpu_num_flows[map_cpu]++: cpu_num_flows[map_cpu]--; + else + rmnet_shs_crit_err[RMNET_SHS_CPU_FLOWS_BNDS_ERR]++; +} + void rmnet_shs_cpu_node_remove(struct rmnet_shs_skbn_s *node) { SHS_TRACE_LOW(RMNET_SHS_CPU_NODE, RMNET_SHS_CPU_NODE_FUNC_REMOVE, 0xDEF, 0xDEF, 0xDEF, 0xDEF, NULL, NULL); list_del_init(&node->node_id); - cpu_num_flows[node->map_cpu]--; + rmnet_shs_change_cpu_num_flows(node->map_cpu, DECREMENT); } @@ -142,7 +152,7 @@ void rmnet_shs_cpu_node_add(struct rmnet_shs_skbn_s *node, 0xDEF, 0xDEF, 0xDEF, 0xDEF, NULL, NULL); list_add(&node->node_id, hd); - cpu_num_flows[node->map_cpu]++; + rmnet_shs_change_cpu_num_flows(node->map_cpu, INCREMENT); } void rmnet_shs_cpu_node_move(struct rmnet_shs_skbn_s *node, @@ -152,8 +162,8 @@ void rmnet_shs_cpu_node_move(struct rmnet_shs_skbn_s *node, 0xDEF, 0xDEF, 0xDEF, 0xDEF, NULL, NULL); list_move(&node->node_id, hd); - cpu_num_flows[node->map_cpu]++; - cpu_num_flows[oldcpu]--; + rmnet_shs_change_cpu_num_flows(node->map_cpu, INCREMENT); + rmnet_shs_change_cpu_num_flows((u16) oldcpu, DECREMENT); } static void rmnet_shs_cpu_ooo(u8 cpu, int count) diff --git a/drivers/rmnet/shs/rmnet_shs_wq.c b/drivers/rmnet/shs/rmnet_shs_wq.c index 70589ce..53f5826 100644 --- a/drivers/rmnet/shs/rmnet_shs_wq.c +++ b/drivers/rmnet/shs/rmnet_shs_wq.c @@ -32,23 +32,19 @@ MODULE_LICENSE("GPL v2"); #define RMNET_SHS_FILTER_FLOW_RATE 100 #define PERIODIC_CLEAN 0 -/* FORCE_CLEAN should only used during module de-ini.*/ +/* FORCE_CLEAN should only used during module de-init.*/ #define FORCE_CLEAN 1 -/* Time to wait (in time ticks) before re-triggering the workqueue - * 1 tick = 10 ms (Maximum possible resolution) - * 100 ticks = 1 second - */ /* Local Definitions and Declarations */ unsigned int rmnet_shs_cpu_prio_dur __read_mostly = 3; module_param(rmnet_shs_cpu_prio_dur, uint, 0644); -MODULE_PARM_DESC(rmnet_shs_cpu_prio_dur, "Priority ignore duration(ticks)"); +MODULE_PARM_DESC(rmnet_shs_cpu_prio_dur, "Priority ignore duration (wq intervals)"); #define PRIO_BACKOFF ((!rmnet_shs_cpu_prio_dur) ? 2 : rmnet_shs_cpu_prio_dur) -unsigned int rmnet_shs_wq_frequency __read_mostly = RMNET_SHS_WQ_DELAY_TICKS; -module_param(rmnet_shs_wq_frequency, uint, 0644); -MODULE_PARM_DESC(rmnet_shs_wq_frequency, "Priodicity of Wq trigger(in ticks)"); +unsigned int rmnet_shs_wq_interval_ms __read_mostly = RMNET_SHS_WQ_INTERVAL_MS; +module_param(rmnet_shs_wq_interval_ms, uint, 0644); +MODULE_PARM_DESC(rmnet_shs_wq_interval_ms, "Interval between wq runs (ms)"); unsigned long rmnet_shs_max_flow_inactivity_sec __read_mostly = RMNET_SHS_MAX_SKB_INACTIVE_TSEC; @@ -91,7 +87,7 @@ module_param_array(rmnet_shs_cpu_rx_flows, uint, 0, 0444); MODULE_PARM_DESC(rmnet_shs_cpu_rx_flows, "Num flows processed per core"); unsigned int rmnet_shs_cpu_rx_filter_flows[MAX_CPUS]; -module_param_array(rmnet_shs_cpu_rx_filter_flows, uint, 0, 0644); +module_param_array(rmnet_shs_cpu_rx_filter_flows, uint, 0, 0444); MODULE_PARM_DESC(rmnet_shs_cpu_rx_filter_flows, "Num filtered flows per core"); unsigned long long rmnet_shs_cpu_rx_bytes[MAX_CPUS]; @@ -183,8 +179,7 @@ static struct rmnet_shs_wq_rx_flow_s rmnet_shs_rx_flow_tbl; static struct list_head rmnet_shs_wq_hstat_tbl = LIST_HEAD_INIT(rmnet_shs_wq_hstat_tbl); static int rmnet_shs_flow_dbg_stats_idx_cnt; -static struct list_head rmnet_shs_wq_ep_tbl = - LIST_HEAD_INIT(rmnet_shs_wq_ep_tbl); +struct list_head rmnet_shs_wq_ep_tbl = LIST_HEAD_INIT(rmnet_shs_wq_ep_tbl); /* Helper functions to add and remove entries to the table * that maintains a list of all endpoints (vnd's) available on this device. @@ -544,6 +539,17 @@ void rmnet_shs_wq_update_hstat_rps_msk(struct rmnet_shs_wq_hstat_s *hstat_p) hstat_p->rps_config_msk = ep->rps_config_msk; hstat_p->def_core_msk = ep->default_core_msk; hstat_p->pri_core_msk = ep->pri_core_msk; + + /* Update ep tput stats while we're here */ + if (hstat_p->skb_tport_proto == IPPROTO_TCP) { + rm_err("SHS_UDP: adding TCP bps %lu to ep_total %lu ep name %s", + hstat_p->rx_bps, ep->tcp_rx_bps, node_p->dev->name); + ep->tcp_rx_bps += hstat_p->rx_bps; + } else if (hstat_p->skb_tport_proto == IPPROTO_UDP) { + rm_err("SHS_UDP: adding UDP rx_bps %lu to ep_total %lu ep name %s", + hstat_p->rx_bps, ep->udp_rx_bps, node_p->dev->name); + ep->udp_rx_bps += hstat_p->rx_bps; + } break; } } @@ -1240,6 +1246,7 @@ int rmnet_shs_wq_check_cpu_move_for_ep(u16 current_cpu, u16 dest_cpu, int rmnet_shs_wq_try_to_move_flow(u16 cur_cpu, u16 dest_cpu, u32 hash_to_move, u32 sugg_type) { + unsigned long flags; struct rmnet_shs_wq_ep_s *ep; if (cur_cpu >= MAX_CPUS || dest_cpu >= MAX_CPUS) { @@ -1251,6 +1258,7 @@ int rmnet_shs_wq_try_to_move_flow(u16 cur_cpu, u16 dest_cpu, u32 hash_to_move, * on it if is online, rps mask, isolation, etc. then make * suggestion to change the cpu for the flow by passing its hash */ + spin_lock_irqsave(&rmnet_shs_ep_lock, flags); list_for_each_entry(ep, &rmnet_shs_wq_ep_tbl, ep_list_id) { if (!ep) continue; @@ -1272,9 +1280,13 @@ int rmnet_shs_wq_try_to_move_flow(u16 cur_cpu, u16 dest_cpu, u32 hash_to_move, rm_err("SHS_FDESC: >> flow 0x%x was suggested to" " move from cpu[%d] to cpu[%d] sugg_type [%d]", hash_to_move, cur_cpu, dest_cpu, sugg_type); + + spin_unlock_irqrestore(&rmnet_shs_ep_lock, flags); return 1; } } + + spin_unlock_irqrestore(&rmnet_shs_ep_lock, flags); return 0; } @@ -1283,8 +1295,10 @@ int rmnet_shs_wq_set_flow_segmentation(u32 hash_to_set, u8 seg_enable) { struct rmnet_shs_skbn_s *node_p; struct rmnet_shs_wq_hstat_s *hstat_p; + unsigned long ht_flags; u16 bkt; + spin_lock_irqsave(&rmnet_shs_ht_splock, ht_flags); hash_for_each(RMNET_SHS_HT, bkt, node_p, list) { if (!node_p) continue; @@ -1306,8 +1320,10 @@ int rmnet_shs_wq_set_flow_segmentation(u32 hash_to_set, u8 seg_enable) 0xDEF, 0xDEF, hstat_p, NULL); node_p->hstats->segment_enable = seg_enable; + spin_unlock_irqrestore(&rmnet_shs_ht_splock, ht_flags); return 1; } + spin_unlock_irqrestore(&rmnet_shs_ht_splock, ht_flags); rm_err("SHS_HT: >> segmentation on hash 0x%x enable %u not set - hash not found", hash_to_set, seg_enable); @@ -1452,6 +1468,7 @@ void rmnet_shs_wq_eval_cpus_caps_and_flows(struct list_head *cpu_caps, rmnet_shs_wq_mem_update_cached_cpu_caps(cpu_caps); rmnet_shs_wq_mem_update_cached_sorted_gold_flows(gold_flows); rmnet_shs_wq_mem_update_cached_sorted_ss_flows(ss_flows); + rmnet_shs_wq_mem_update_cached_netdevs(); rmnet_shs_genl_send_int_to_userspace_no_info(RMNET_SHS_SYNC_RESP_INT); @@ -1614,12 +1631,14 @@ int rmnet_shs_wq_get_lpwr_cpu_new_flow(struct net_device *dev) int cpu_assigned = -1; u8 is_match_found = 0; struct rmnet_shs_wq_ep_s *ep = NULL; + unsigned long flags; if (!dev) { rmnet_shs_crit_err[RMNET_SHS_NETDEV_ERR]++; return cpu_assigned; } + spin_lock_irqsave(&rmnet_shs_ep_lock, flags); list_for_each_entry(ep, &rmnet_shs_wq_ep_tbl, ep_list_id) { if (!ep) continue; @@ -1635,6 +1654,7 @@ int rmnet_shs_wq_get_lpwr_cpu_new_flow(struct net_device *dev) if (!is_match_found) { rmnet_shs_crit_err[RMNET_SHS_WQ_EP_ACCESS_ERR]++; + spin_unlock_irqrestore(&rmnet_shs_ep_lock, flags); return cpu_assigned; } @@ -1652,6 +1672,7 @@ int rmnet_shs_wq_get_lpwr_cpu_new_flow(struct net_device *dev) /* Increment CPU assignment idx to be ready for next flow assignment*/ if ((cpu_assigned >= 0) || ((ep->new_lo_idx + 1) >= ep->new_lo_max)) ep->new_lo_idx = ((ep->new_lo_idx + 1) % ep->new_lo_max); + spin_unlock_irqrestore(&rmnet_shs_ep_lock, flags); return cpu_assigned; } @@ -1663,12 +1684,14 @@ int rmnet_shs_wq_get_perf_cpu_new_flow(struct net_device *dev) u8 hi_idx; u8 hi_max; u8 is_match_found = 0; + unsigned long flags; if (!dev) { rmnet_shs_crit_err[RMNET_SHS_NETDEV_ERR]++; return cpu_assigned; } + spin_lock_irqsave(&rmnet_shs_ep_lock, flags); list_for_each_entry(ep, &rmnet_shs_wq_ep_tbl, ep_list_id) { if (!ep) continue; @@ -1684,6 +1707,7 @@ int rmnet_shs_wq_get_perf_cpu_new_flow(struct net_device *dev) if (!is_match_found) { rmnet_shs_crit_err[RMNET_SHS_WQ_EP_ACCESS_ERR]++; + spin_unlock_irqrestore(&rmnet_shs_ep_lock, flags); return cpu_assigned; } @@ -1700,6 +1724,7 @@ int rmnet_shs_wq_get_perf_cpu_new_flow(struct net_device *dev) /* Increment CPU assignment idx to be ready for next flow assignment*/ if (cpu_assigned >= 0) ep->new_hi_idx = ((hi_idx + 1) % hi_max); + spin_unlock_irqrestore(&rmnet_shs_ep_lock, flags); return cpu_assigned; } @@ -1874,6 +1899,11 @@ void rmnet_shs_wq_refresh_ep_masks(void) if (!ep->is_ep_active) continue; rmnet_shs_wq_update_ep_rps_msk(ep); + + /* These tput totals get re-added as we go through each flow */ + ep->udp_rx_bps = 0; + ep->tcp_rx_bps = 0; + } } @@ -1993,15 +2023,13 @@ void rmnet_shs_wq_update_stats(void) } rmnet_shs_wq_refresh_new_flow_list(); - /*Invoke after both the locks are released*/ - rmnet_shs_wq_cleanup_hash_tbl(PERIODIC_CLEAN); - rmnet_shs_wq_debug_print_flows(); rmnet_shs_wq_filter(); } void rmnet_shs_wq_process_wq(struct work_struct *work) { unsigned long flags; + unsigned long jiffies; trace_rmnet_shs_wq_high(RMNET_SHS_WQ_PROCESS_WQ, RMNET_SHS_WQ_PROCESS_WQ_START, @@ -2011,8 +2039,14 @@ void rmnet_shs_wq_process_wq(struct work_struct *work) rmnet_shs_wq_update_stats(); spin_unlock_irqrestore(&rmnet_shs_ep_lock, flags); + /*Invoke after both the locks are released*/ + rmnet_shs_wq_cleanup_hash_tbl(PERIODIC_CLEAN); + rmnet_shs_wq_debug_print_flows(); + + jiffies = msecs_to_jiffies(rmnet_shs_wq_interval_ms); + queue_delayed_work(rmnet_shs_wq, &rmnet_shs_delayed_wq->wq, - rmnet_shs_wq_frequency); + jiffies); trace_rmnet_shs_wq_high(RMNET_SHS_WQ_PROCESS_WQ, RMNET_SHS_WQ_PROCESS_WQ_END, @@ -2046,6 +2080,7 @@ void rmnet_shs_wq_exit(void) return; rmnet_shs_wq_mem_deinit(); + rmnet_shs_genl_send_int_to_userspace_no_info(RMNET_SHS_SYNC_WQ_EXIT); trace_rmnet_shs_wq_high(RMNET_SHS_WQ_EXIT, RMNET_SHS_WQ_EXIT_START, 0xDEF, 0xDEF, 0xDEF, 0xDEF, NULL, NULL); diff --git a/drivers/rmnet/shs/rmnet_shs_wq.h b/drivers/rmnet/shs/rmnet_shs_wq.h index 0d86200..aa0265c 100644 --- a/drivers/rmnet/shs/rmnet_shs_wq.h +++ b/drivers/rmnet/shs/rmnet_shs_wq.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2020, 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 @@ -31,14 +31,18 @@ #define RMNET_SHS_NSEC_TO_SEC(x) ((x)/1000000000) #define RMNET_SHS_BYTE_TO_BIT(x) ((x)*8) #define RMNET_SHS_MIN_HSTAT_NODES_REQD 16 -#define RMNET_SHS_WQ_DELAY_TICKS 10 +#define RMNET_SHS_WQ_INTERVAL_MS 100 extern unsigned long long rmnet_shs_cpu_rx_max_pps_thresh[MAX_CPUS]__read_mostly; extern unsigned long long rmnet_shs_cpu_rx_min_pps_thresh[MAX_CPUS]__read_mostly; +extern struct list_head rmnet_shs_wq_ep_tbl; + /* stores wq and end point details */ struct rmnet_shs_wq_ep_s { + u64 tcp_rx_bps; + u64 udp_rx_bps; struct list_head ep_list_id; struct net_device *ep; int new_lo_core[MAX_CPUS]; @@ -161,6 +165,7 @@ struct rmnet_shs_wq_cpu_cap_s { struct list_head cpu_cap_list; u64 pps_capacity; u64 avg_pps_capacity; + u64 bps; u16 cpu_num; }; diff --git a/drivers/rmnet/shs/rmnet_shs_wq_genl.c b/drivers/rmnet/shs/rmnet_shs_wq_genl.c index b28f0c2..2dff48a 100644 --- a/drivers/rmnet/shs/rmnet_shs_wq_genl.c +++ b/drivers/rmnet/shs/rmnet_shs_wq_genl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2019 The Linux Foundation. All rights reserved. +/* Copyright (c) 2019-2020 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 @@ -349,6 +349,8 @@ int rmnet_shs_wq_genl_deinit(void) { int ret; + rmnet_shs_genl_send_int_to_userspace_no_info(RMNET_SHS_SYNC_WQ_EXIT); + ret = genl_unregister_family(&rmnet_shs_genl_family); if(ret != 0){ rm_err("SHS_GNL: unregister family failed: %i\n",ret); diff --git a/drivers/rmnet/shs/rmnet_shs_wq_genl.h b/drivers/rmnet/shs/rmnet_shs_wq_genl.h index 333de48..9901d38 100644 --- a/drivers/rmnet/shs/rmnet_shs_wq_genl.h +++ b/drivers/rmnet/shs/rmnet_shs_wq_genl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019 The Linux Foundation. All rights reserved. +/* Copyright (c) 2019-2020 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 @@ -23,6 +23,7 @@ #define RMNET_SHS_GENL_VERSION 1 #define RMNET_SHS_GENL_FAMILY_NAME "RMNET_SHS" #define RMNET_SHS_SYNC_RESP_INT 828 /* Any number, sent after mem update */ +#define RMNET_SHS_SYNC_WQ_EXIT 42 extern int rmnet_shs_userspace_connected; diff --git a/drivers/rmnet/shs/rmnet_shs_wq_mem.c b/drivers/rmnet/shs/rmnet_shs_wq_mem.c index 1675517..062edb7 100644 --- a/drivers/rmnet/shs/rmnet_shs_wq_mem.c +++ b/drivers/rmnet/shs/rmnet_shs_wq_mem.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2019 The Linux Foundation. All rights reserved. +/* Copyright (c) 2019-2020 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 @@ -15,6 +15,7 @@ #include "rmnet_shs_wq_mem.h" #include <linux/proc_fs.h> +#include <linux/refcount.h> MODULE_LICENSE("GPL v2"); @@ -24,6 +25,7 @@ struct proc_dir_entry *shs_proc_dir; struct rmnet_shs_wq_cpu_cap_usr_s rmnet_shs_wq_cap_list_usr[MAX_CPUS]; struct rmnet_shs_wq_gflows_usr_s rmnet_shs_wq_gflows_usr[RMNET_SHS_MAX_USRFLOWS]; struct rmnet_shs_wq_ssflows_usr_s rmnet_shs_wq_ssflows_usr[RMNET_SHS_MAX_USRFLOWS]; +struct rmnet_shs_wq_netdev_usr_s rmnet_shs_wq_netdev_usr[RMNET_SHS_MAX_NETDEVS]; struct list_head gflows = LIST_HEAD_INIT(gflows); /* gold flows */ struct list_head ssflows = LIST_HEAD_INIT(ssflows); /* slow start flows */ @@ -32,6 +34,7 @@ struct list_head cpu_caps = LIST_HEAD_INIT(cpu_caps); /* capacities */ struct rmnet_shs_mmap_info *cap_shared; struct rmnet_shs_mmap_info *gflow_shared; struct rmnet_shs_mmap_info *ssflow_shared; +struct rmnet_shs_mmap_info *netdev_shared; /* Static Functions and Definitions */ static void rmnet_shs_vm_open(struct vm_area_struct *vma) @@ -44,32 +47,163 @@ static void rmnet_shs_vm_close(struct vm_area_struct *vma) return; } -static int rmnet_shs_vm_fault(struct vm_fault *vmf) +static int rmnet_shs_vm_fault_caps(struct vm_fault *vmf) { struct page *page = NULL; struct rmnet_shs_mmap_info *info; rmnet_shs_wq_ep_lock_bh(); - info = (struct rmnet_shs_mmap_info *) vmf->vma->vm_private_data; - if (info->data) { - page = virt_to_page(info->data); - get_page(page); - vmf->page = page; + if (cap_shared) { + info = (struct rmnet_shs_mmap_info *) vmf->vma->vm_private_data; + if (info->data) { + page = virt_to_page(info->data); + get_page(page); + vmf->page = page; + } else { + rmnet_shs_wq_ep_unlock_bh(); + return VM_FAULT_SIGSEGV; + } + } else { + rmnet_shs_wq_ep_unlock_bh(); + return VM_FAULT_SIGSEGV; + } + rmnet_shs_wq_ep_unlock_bh(); + + return 0; +} + + +static int rmnet_shs_vm_fault_g_flows(struct vm_fault *vmf) +{ + struct page *page = NULL; + struct rmnet_shs_mmap_info *info; + + rmnet_shs_wq_ep_lock_bh(); + if (gflow_shared) { + info = (struct rmnet_shs_mmap_info *) vmf->vma->vm_private_data; + if (info->data) { + page = virt_to_page(info->data); + get_page(page); + vmf->page = page; + } else { + rmnet_shs_wq_ep_unlock_bh(); + return VM_FAULT_SIGSEGV; + } + } else { + rmnet_shs_wq_ep_unlock_bh(); + return VM_FAULT_SIGSEGV; + + } + rmnet_shs_wq_ep_unlock_bh(); + + return 0; +} + +static int rmnet_shs_vm_fault_ss_flows(struct vm_fault *vmf) +{ + struct page *page = NULL; + struct rmnet_shs_mmap_info *info; + + rmnet_shs_wq_ep_lock_bh(); + if (ssflow_shared) { + info = (struct rmnet_shs_mmap_info *) vmf->vma->vm_private_data; + if (info->data) { + page = virt_to_page(info->data); + get_page(page); + vmf->page = page; + } else { + rmnet_shs_wq_ep_unlock_bh(); + return VM_FAULT_SIGSEGV; + } + } else { + rmnet_shs_wq_ep_unlock_bh(); + return VM_FAULT_SIGSEGV; } rmnet_shs_wq_ep_unlock_bh(); return 0; } -static const struct vm_operations_struct rmnet_shs_vm_ops = { +static int rmnet_shs_vm_fault_netdev(struct vm_fault *vmf) +{ + struct page *page = NULL; + struct rmnet_shs_mmap_info *info; + + rmnet_shs_wq_ep_lock_bh(); + if (netdev_shared) { + info = (struct rmnet_shs_mmap_info *) vmf->vma->vm_private_data; + if (info->data) { + page = virt_to_page(info->data); + get_page(page); + vmf->page = page; + } else { + rmnet_shs_wq_ep_unlock_bh(); + return VM_FAULT_SIGSEGV; + } + } else { + rmnet_shs_wq_ep_unlock_bh(); + return VM_FAULT_SIGSEGV; + } + rmnet_shs_wq_ep_unlock_bh(); + + return 0; +} + + +static const struct vm_operations_struct rmnet_shs_vm_ops_caps = { + .close = rmnet_shs_vm_close, + .open = rmnet_shs_vm_open, + .fault = rmnet_shs_vm_fault_caps, +}; + +static const struct vm_operations_struct rmnet_shs_vm_ops_g_flows = { .close = rmnet_shs_vm_close, .open = rmnet_shs_vm_open, - .fault = rmnet_shs_vm_fault, + .fault = rmnet_shs_vm_fault_g_flows, }; -static int rmnet_shs_mmap(struct file *filp, struct vm_area_struct *vma) +static const struct vm_operations_struct rmnet_shs_vm_ops_ss_flows = { + .close = rmnet_shs_vm_close, + .open = rmnet_shs_vm_open, + .fault = rmnet_shs_vm_fault_ss_flows, +}; + +static const struct vm_operations_struct rmnet_shs_vm_ops_netdev = { + .close = rmnet_shs_vm_close, + .open = rmnet_shs_vm_open, + .fault = rmnet_shs_vm_fault_netdev, +}; + +static int rmnet_shs_mmap_caps(struct file *filp, struct vm_area_struct *vma) { - vma->vm_ops = &rmnet_shs_vm_ops; + vma->vm_ops = &rmnet_shs_vm_ops_caps; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_private_data = filp->private_data; + + return 0; +} + +static int rmnet_shs_mmap_g_flows(struct file *filp, struct vm_area_struct *vma) +{ + vma->vm_ops = &rmnet_shs_vm_ops_g_flows; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_private_data = filp->private_data; + + return 0; +} + +static int rmnet_shs_mmap_ss_flows(struct file *filp, struct vm_area_struct *vma) +{ + vma->vm_ops = &rmnet_shs_vm_ops_ss_flows; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_private_data = filp->private_data; + + return 0; +} + +static int rmnet_shs_mmap_netdev(struct file *filp, struct vm_area_struct *vma) +{ + vma->vm_ops = &rmnet_shs_vm_ops_netdev; vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; vma->vm_private_data = filp->private_data; @@ -95,9 +229,12 @@ static int rmnet_shs_open_caps(struct inode *inode, struct file *filp) } cap_shared = info; + refcount_set(&cap_shared->refcnt, 1); rm_err("SHS_MEM: virt_to_phys = 0x%llx cap_shared = 0x%llx\n", (unsigned long long)virt_to_phys((void *)info), (unsigned long long)virt_to_phys((void *)cap_shared)); + } else { + refcount_inc(&cap_shared->refcnt); } filp->private_data = cap_shared; @@ -132,10 +269,14 @@ static int rmnet_shs_open_g_flows(struct inode *inode, struct file *filp) } gflow_shared = info; + refcount_set(&gflow_shared->refcnt, 1); rm_err("SHS_MEM: virt_to_phys = 0x%llx gflow_shared = 0x%llx\n", (unsigned long long)virt_to_phys((void *)info), (unsigned long long)virt_to_phys((void *)gflow_shared)); + } else { + refcount_inc(&gflow_shared->refcnt); } + filp->private_data = gflow_shared; rmnet_shs_wq_ep_unlock_bh(); @@ -166,10 +307,14 @@ static int rmnet_shs_open_ss_flows(struct inode *inode, struct file *filp) } ssflow_shared = info; + refcount_set(&ssflow_shared->refcnt, 1); rm_err("SHS_MEM: virt_to_phys = 0x%llx ssflow_shared = 0x%llx\n", (unsigned long long)virt_to_phys((void *)info), (unsigned long long)virt_to_phys((void *)ssflow_shared)); + } else { + refcount_inc(&ssflow_shared->refcnt); } + filp->private_data = ssflow_shared; rmnet_shs_wq_ep_unlock_bh(); @@ -181,40 +326,59 @@ fail: return -ENOMEM; } -static ssize_t rmnet_shs_read(struct file *filp, char __user *buf, size_t len, loff_t *off) +static int rmnet_shs_open_netdev(struct inode *inode, struct file *filp) { struct rmnet_shs_mmap_info *info; - int ret = 0; - rm_err("%s", "SHS_MEM: rmnet_shs_read - entry\n"); + rm_err("%s", "SHS_MEM: rmnet_shs_open netdev - entry\n"); rmnet_shs_wq_ep_lock_bh(); - info = filp->private_data; - ret = min_t(size_t, len, RMNET_SHS_BUFFER_SIZE); - if (copy_to_user(buf, info->data, ret)) - ret = -EFAULT; - rmnet_shs_wq_ep_unlock_bh(); + if (!netdev_shared) { + info = kzalloc(sizeof(struct rmnet_shs_mmap_info), GFP_ATOMIC); + if (!info) + goto fail; - return ret; -} + info->data = (char *)get_zeroed_page(GFP_ATOMIC); + if (!info->data) { + kfree(info); + goto fail; + } -static ssize_t rmnet_shs_write(struct file *filp, const char __user *buf, size_t len, loff_t *off) -{ - struct rmnet_shs_mmap_info *info; - int ret; + netdev_shared = info; + refcount_set(&netdev_shared->refcnt, 1); + rm_err("SHS_MEM: virt_to_phys = 0x%llx netdev_shared = 0x%llx\n", + (unsigned long long)virt_to_phys((void *)info), + (unsigned long long)virt_to_phys((void *)netdev_shared)); + } else { + refcount_inc(&netdev_shared->refcnt); + } - rm_err("%s", "SHS_MEM: rmnet_shs_write - entry\n"); + filp->private_data = netdev_shared; + rmnet_shs_wq_ep_unlock_bh(); - rmnet_shs_wq_ep_lock_bh(); - info = filp->private_data; - ret = min_t(size_t, len, RMNET_SHS_BUFFER_SIZE); - if (copy_from_user(info->data, buf, ret)) - ret = -EFAULT; - else - ret = len; + return 0; + +fail: rmnet_shs_wq_ep_unlock_bh(); + return -ENOMEM; +} - return ret; +static ssize_t rmnet_shs_read(struct file *filp, char __user *buf, size_t len, loff_t *off) +{ + /* + * Decline to expose file value and simply return benign value + */ + return RMNET_SHS_READ_VAL; +} + +static ssize_t rmnet_shs_write(struct file *filp, const char __user *buf, size_t len, loff_t *off) +{ + /* + * Returning zero here would result in echo commands hanging + * Instead return len and simply decline to allow echo'd values to + * take effect + */ + return len; } static int rmnet_shs_release_caps(struct inode *inode, struct file *filp) @@ -226,10 +390,14 @@ static int rmnet_shs_release_caps(struct inode *inode, struct file *filp) rmnet_shs_wq_ep_lock_bh(); if (cap_shared) { info = filp->private_data; - cap_shared = NULL; - free_page((unsigned long)info->data); - kfree(info); - filp->private_data = NULL; + if (refcount_read(&info->refcnt) <= 1) { + free_page((unsigned long)info->data); + kfree(info); + cap_shared = NULL; + filp->private_data = NULL; + } else { + refcount_dec(&info->refcnt); + } } rmnet_shs_wq_ep_unlock_bh(); @@ -245,10 +413,14 @@ static int rmnet_shs_release_g_flows(struct inode *inode, struct file *filp) rmnet_shs_wq_ep_lock_bh(); if (gflow_shared) { info = filp->private_data; - gflow_shared = NULL; - free_page((unsigned long)info->data); - kfree(info); - filp->private_data = NULL; + if (refcount_read(&info->refcnt) <= 1) { + free_page((unsigned long)info->data); + kfree(info); + gflow_shared = NULL; + filp->private_data = NULL; + } else { + refcount_dec(&info->refcnt); + } } rmnet_shs_wq_ep_unlock_bh(); @@ -264,10 +436,37 @@ static int rmnet_shs_release_ss_flows(struct inode *inode, struct file *filp) rmnet_shs_wq_ep_lock_bh(); if (ssflow_shared) { info = filp->private_data; - ssflow_shared = NULL; - free_page((unsigned long)info->data); - kfree(info); - filp->private_data = NULL; + if (refcount_read(&info->refcnt) <= 1) { + free_page((unsigned long)info->data); + kfree(info); + ssflow_shared = NULL; + filp->private_data = NULL; + } else { + refcount_dec(&info->refcnt); + } + } + rmnet_shs_wq_ep_unlock_bh(); + + return 0; +} + +static int rmnet_shs_release_netdev(struct inode *inode, struct file *filp) +{ + struct rmnet_shs_mmap_info *info; + + rm_err("%s", "SHS_MEM: rmnet_shs_release netdev - entry\n"); + + rmnet_shs_wq_ep_lock_bh(); + if (netdev_shared) { + info = filp->private_data; + if (refcount_read(&info->refcnt) <= 1) { + free_page((unsigned long)info->data); + kfree(info); + netdev_shared = NULL; + filp->private_data = NULL; + } else { + refcount_dec(&info->refcnt); + } } rmnet_shs_wq_ep_unlock_bh(); @@ -276,7 +475,7 @@ static int rmnet_shs_release_ss_flows(struct inode *inode, struct file *filp) static const struct file_operations rmnet_shs_caps_fops = { .owner = THIS_MODULE, - .mmap = rmnet_shs_mmap, + .mmap = rmnet_shs_mmap_caps, .open = rmnet_shs_open_caps, .release = rmnet_shs_release_caps, .read = rmnet_shs_read, @@ -285,7 +484,7 @@ static const struct file_operations rmnet_shs_caps_fops = { static const struct file_operations rmnet_shs_g_flows_fops = { .owner = THIS_MODULE, - .mmap = rmnet_shs_mmap, + .mmap = rmnet_shs_mmap_g_flows, .open = rmnet_shs_open_g_flows, .release = rmnet_shs_release_g_flows, .read = rmnet_shs_read, @@ -294,13 +493,21 @@ static const struct file_operations rmnet_shs_g_flows_fops = { static const struct file_operations rmnet_shs_ss_flows_fops = { .owner = THIS_MODULE, - .mmap = rmnet_shs_mmap, + .mmap = rmnet_shs_mmap_ss_flows, .open = rmnet_shs_open_ss_flows, .release = rmnet_shs_release_ss_flows, .read = rmnet_shs_read, .write = rmnet_shs_write, }; +static const struct file_operations rmnet_shs_netdev_fops = { + .owner = THIS_MODULE, + .mmap = rmnet_shs_mmap_netdev, + .open = rmnet_shs_open_netdev, + .release = rmnet_shs_release_netdev, + .read = rmnet_shs_read, + .write = rmnet_shs_write, +}; /* Global Functions */ /* Add a flow to the slow start flow list */ @@ -432,6 +639,7 @@ void rmnet_shs_wq_cpu_caps_list_add( if (flows <= 0) { cap_node->pps_capacity = pps_uthresh; cap_node->avg_pps_capacity = pps_uthresh; + cap_node->bps = 0; list_add(&cap_node->cpu_cap_list, cpu_caps); return; } @@ -452,6 +660,8 @@ void rmnet_shs_wq_cpu_caps_list_add( cap_node->avg_pps_capacity = 0; } + cap_node->bps = cpu_node->rx_bps; + list_add(&cap_node->cpu_cap_list, cpu_caps); } @@ -503,12 +713,13 @@ void rmnet_shs_wq_mem_update_cached_cpu_caps(struct list_head *cpu_caps) break; rm_err("SHS_SCAPS: > cpu[%d] with pps capacity = %llu | " - "avg pps cap = %llu", + "avg pps cap = %llu bps = %llu", cap_node->cpu_num, cap_node->pps_capacity, - cap_node->avg_pps_capacity); + cap_node->avg_pps_capacity, cap_node->bps); rmnet_shs_wq_cap_list_usr[idx].avg_pps_capacity = cap_node->avg_pps_capacity; rmnet_shs_wq_cap_list_usr[idx].pps_capacity = cap_node->pps_capacity; + rmnet_shs_wq_cap_list_usr[idx].bps = cap_node->bps; rmnet_shs_wq_cap_list_usr[idx].cpu_num = cap_node->cpu_num; idx += 1; } @@ -650,13 +861,97 @@ void rmnet_shs_wq_mem_update_cached_sorted_ss_flows(struct list_head *ss_flows) rm_err("SHS_SLOW: num ss flows = %u\n", idx); /* Copy num ss flows into first 2 bytes, - then copy in the cached gold flow array */ + then copy in the cached ss flow array */ memcpy(((char *)ssflow_shared->data), &idx, sizeof(idx)); memcpy(((char *)ssflow_shared->data + sizeof(uint16_t)), (void *) &rmnet_shs_wq_ssflows_usr[0], sizeof(rmnet_shs_wq_ssflows_usr)); } + +/* Extract info required from the rmnet_port array then memcpy to shared mem. + * > Add number of active netdevices/endpoints at the start. + * > After memcpy is complete, send userspace a message indicating that memcpy + * has just completed. + * > The netdev is formated like this: + * | num_netdevs | data_format | {rmnet_data0,ip_miss,rx_pkts} | ... | + * | 16 bits | 32 bits | | + */ +void rmnet_shs_wq_mem_update_cached_netdevs(void) +{ + struct rmnet_priv *priv; + struct rmnet_shs_wq_ep_s *ep = NULL; + u16 idx = 0; + u16 count = 0; + + rm_err("SHS_NETDEV: function enter %u\n", idx); + list_for_each_entry(ep, &rmnet_shs_wq_ep_tbl, ep_list_id) { + count += 1; + rm_err("SHS_NETDEV: function enter ep %u\n", count); + if (!ep) + continue; + + if (!ep->is_ep_active) { + rm_err("SHS_NETDEV: ep %u is NOT active\n", count); + continue; + } + + rm_err("SHS_NETDEV: ep %u is active and not null\n", count); + if (idx >= RMNET_SHS_MAX_NETDEVS) { + break; + } + + priv = netdev_priv(ep->ep); + if (!priv) { + rm_err("SHS_NETDEV: priv for ep %u is null\n", count); + continue; + } + + rm_err("SHS_NETDEV: ep %u has name = %s \n", count, + ep->ep->name); + rm_err("SHS_NETDEV: ep %u has mux_id = %u \n", count, + priv->mux_id); + rm_err("SHS_NETDEV: ep %u has ip_miss = %lu \n", count, + priv->stats.coal.close.ip_miss); + rm_err("SHS_NETDEV: ep %u has coal_rx_pkts = %lu \n", count, + priv->stats.coal.coal_pkts); + rm_err("SHS_NETDEV: ep %u has udp_rx_bps = %lu \n", count, + ep->udp_rx_bps); + rm_err("SHS_NETDEV: ep %u has tcp_rx_bps = %lu \n", count, + ep->tcp_rx_bps); + + /* Set netdev name and ip mismatch count */ + rmnet_shs_wq_netdev_usr[idx].coal_ip_miss = priv->stats.coal.close.ip_miss; + rmnet_shs_wq_netdev_usr[idx].hw_evict = priv->stats.coal.close.hw_evict; + rmnet_shs_wq_netdev_usr[idx].coal_tcp = priv->stats.coal.coal_tcp; + rmnet_shs_wq_netdev_usr[idx].coal_tcp_bytes = priv->stats.coal.coal_tcp_bytes; + rmnet_shs_wq_netdev_usr[idx].coal_udp = priv->stats.coal.coal_udp; + rmnet_shs_wq_netdev_usr[idx].coal_udp_bytes = priv->stats.coal.coal_udp_bytes; + rmnet_shs_wq_netdev_usr[idx].mux_id = priv->mux_id; + strlcpy(rmnet_shs_wq_netdev_usr[idx].name, + ep->ep->name, + sizeof(rmnet_shs_wq_netdev_usr[idx].name)); + + /* Set rx pkt from netdev stats */ + rmnet_shs_wq_netdev_usr[idx].coal_rx_pkts = priv->stats.coal.coal_pkts; + rmnet_shs_wq_netdev_usr[idx].tcp_rx_bps = ep->tcp_rx_bps; + rmnet_shs_wq_netdev_usr[idx].udp_rx_bps = ep->udp_rx_bps; + idx += 1; + } + + rm_err("SHS_MEM: netdev_shared = 0x%llx addr = 0x%pK\n", + (unsigned long long)virt_to_phys((void *)netdev_shared), netdev_shared); + if (!netdev_shared) { + rm_err("%s", "SHS_WRITE: netdev_shared is NULL"); + return; + } + + memcpy(((char *)netdev_shared->data), &idx, sizeof(idx)); + memcpy(((char *)netdev_shared->data + sizeof(uint16_t)), + (void *) &rmnet_shs_wq_netdev_usr[0], + sizeof(rmnet_shs_wq_netdev_usr)); +} + /* Creates the proc folder and files for shs shared memory */ void rmnet_shs_wq_mem_init(void) { @@ -665,11 +960,13 @@ void rmnet_shs_wq_mem_init(void) proc_create(RMNET_SHS_PROC_CAPS, 0644, shs_proc_dir, &rmnet_shs_caps_fops); proc_create(RMNET_SHS_PROC_G_FLOWS, 0644, shs_proc_dir, &rmnet_shs_g_flows_fops); proc_create(RMNET_SHS_PROC_SS_FLOWS, 0644, shs_proc_dir, &rmnet_shs_ss_flows_fops); + proc_create(RMNET_SHS_PROC_NETDEV, 0644, shs_proc_dir, &rmnet_shs_netdev_fops); rmnet_shs_wq_ep_lock_bh(); cap_shared = NULL; gflow_shared = NULL; ssflow_shared = NULL; + netdev_shared = NULL; rmnet_shs_wq_ep_unlock_bh(); } @@ -679,11 +976,13 @@ void rmnet_shs_wq_mem_deinit(void) remove_proc_entry(RMNET_SHS_PROC_CAPS, shs_proc_dir); remove_proc_entry(RMNET_SHS_PROC_G_FLOWS, shs_proc_dir); remove_proc_entry(RMNET_SHS_PROC_SS_FLOWS, shs_proc_dir); + remove_proc_entry(RMNET_SHS_PROC_NETDEV, shs_proc_dir); remove_proc_entry(RMNET_SHS_PROC_DIR, NULL); rmnet_shs_wq_ep_lock_bh(); cap_shared = NULL; gflow_shared = NULL; ssflow_shared = NULL; + netdev_shared = NULL; rmnet_shs_wq_ep_unlock_bh(); } diff --git a/drivers/rmnet/shs/rmnet_shs_wq_mem.h b/drivers/rmnet/shs/rmnet_shs_wq_mem.h index 2e5e889..e955606 100644 --- a/drivers/rmnet/shs/rmnet_shs_wq_mem.h +++ b/drivers/rmnet/shs/rmnet_shs_wq_mem.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019 The Linux Foundation. All rights reserved. +/* Copyright (c) 2019-2020 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 @@ -23,16 +23,23 @@ #define RMNET_SHS_PROC_CAPS "rmnet_shs_caps" #define RMNET_SHS_PROC_G_FLOWS "rmnet_shs_flows" #define RMNET_SHS_PROC_SS_FLOWS "rmnet_shs_ss_flows" +#define RMNET_SHS_PROC_NETDEV "rmnet_shs_netdev" #define RMNET_SHS_MAX_USRFLOWS (128) +#define RMNET_SHS_MAX_NETDEVS (40) +#define RMNET_SHS_IFNAMSIZ (16) +#define RMNET_SHS_READ_VAL (0) +/* NOTE: Make sure these structs fit in one page */ +/* 26 bytes * 8 max cpus = 208 bytes < 4096 */ struct __attribute__((__packed__)) rmnet_shs_wq_cpu_cap_usr_s { u64 pps_capacity; u64 avg_pps_capacity; - u64 bps_capacity; + u64 bps; u16 cpu_num; }; +/* 30 bytes * 128 max = 3840 bytes < 4096 */ struct __attribute__((__packed__)) rmnet_shs_wq_gflows_usr_s { u64 rx_pps; u64 avg_pps; @@ -41,6 +48,7 @@ struct __attribute__((__packed__)) rmnet_shs_wq_gflows_usr_s { u16 cpu_num; }; +/* 30 bytes * 128 max = 3840 bytes < 4096 */ struct __attribute__((__packed__)) rmnet_shs_wq_ssflows_usr_s { u64 rx_pps; u64 avg_pps; @@ -49,6 +57,21 @@ struct __attribute__((__packed__)) rmnet_shs_wq_ssflows_usr_s { u16 cpu_num; }; +/* 16 + 8*9 + 8 = 89 bytes, 89*40 netdev = 3560 bytes < 4096 */ +struct __attribute__((__packed__)) rmnet_shs_wq_netdev_usr_s { + char name[RMNET_SHS_IFNAMSIZ]; + u64 coal_ip_miss; + u64 hw_evict; + u64 coal_rx_pkts; + u64 coal_tcp; + u64 coal_tcp_bytes; + u64 coal_udp; + u64 coal_udp_bytes; + u64 udp_rx_bps; + u64 tcp_rx_bps; + u8 mux_id; +}; + extern struct list_head gflows; extern struct list_head ssflows; extern struct list_head cpu_caps; @@ -58,6 +81,7 @@ enum {RMNET_SHS_BUFFER_SIZE = 4096}; struct rmnet_shs_mmap_info { char *data; + refcount_t refcnt; }; /* Function Definitions */ @@ -81,6 +105,7 @@ void rmnet_shs_wq_mem_update_cached_cpu_caps(struct list_head *cpu_caps); void rmnet_shs_wq_mem_update_cached_sorted_gold_flows(struct list_head *gold_flows); void rmnet_shs_wq_mem_update_cached_sorted_ss_flows(struct list_head *ss_flows); +void rmnet_shs_wq_mem_update_cached_netdevs(void); void rmnet_shs_wq_mem_init(void); |