summaryrefslogtreecommitdiff
path: root/drivers/rmnet
diff options
context:
space:
mode:
authorSubash Abhinov Kasiviswanathan <subashab@codeaurora.org>2019-10-07 19:52:10 -0700
committerSubash Abhinov Kasiviswanathan <subashab@codeaurora.org>2019-10-09 18:31:21 -0700
commita21a3f1f1e42148c14fae334a1b7dd57995ec43a (patch)
treed38174957ef4f58646e87c4126d0a42cb647edb2 /drivers/rmnet
parent1ff991750f195087f1d0015e2c9c9c099598f77c (diff)
downloaddata-kernel-a21a3f1f1e42148c14fae334a1b7dd57995ec43a.tar.gz
drivers: rmnet_shs: Remove rmnet ep access
Rmnet driver allocates rmnet_endpoint which rmnet_shs was using to keep track of endpoints that needed. However rmnet driver frees the memory before endpoint unregistration so this leaves a potential race condition where the wq can run after freeing. Change is to instead use net_dev refrerences we keep track of from net_dev_cb and no longer use rmnet_endpoints allocated by rmnet driver. Rmnet_shs was only using netdev references in rmnet_endpoint so no impact should be expected. This use-after-free would cause the following crash-signature. <6> Unable to handle kernel paging request at virtual address 00005000 <6> Mem abort info: <6> Exception class = DABT (current EL), IL = 32 bits <6> SET = 0, FnV = 0 <6> EA = 0, S1PTW = 0 <6> FSC = 5 <6> Data abort info: <6> ISV = 0, ISS = 0x00000005 <6> CM = 0, WnR = 0 <6> user pgtable: 4k pages, 39-bit VAs, pgd = 0000000070b0b425 <6> Internal error: Oops: 96000005 [#1] PREEMPT SMP <6> Workqueue: rmnet_shs_wq rmnet_shs_wq_process_wq [rmnet_shs] <6> task: 00000000deaad59d task.stack: 00000000053e0949 <2> pc : rmnet_shs_wq_update_ep_rps_msk+0x3c/0xd8 [rmnet_shs] <2> lr : rmnet_shs_wq_update_ep_rps_msk+0x28/0xd8 [rmnet_shs] <2> Call trace: <2> rmnet_shs_wq_update_ep_rps_msk+0x3c/0xd8 [rmnet_shs] <2> rmnet_shs_wq_update_stats+0x98/0x928 [rmnet_shs] <2> rmnet_shs_wq_process_wq+0x10c/0x248 [rmnet_shs] <2> process_one_work+0x1f0/0x458 <2> worker_thread+0x2ec/0x450 <2> kthread+0x11c/0x130 <2> ret_from_fork+0x10/0x1c CRs-Fixed: 2541604 Change-Id: I7026f2564c463f4ca989af97572e2a8fe5652087 Acked-by: Raul Martinez <mraul@qti.qualcomm.com> Signed-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
Diffstat (limited to 'drivers/rmnet')
-rw-r--r--drivers/rmnet/shs/rmnet_shs_config.c7
-rwxr-xr-xdrivers/rmnet/shs/rmnet_shs_main.c2
-rw-r--r--drivers/rmnet/shs/rmnet_shs_wq.c109
-rw-r--r--drivers/rmnet/shs/rmnet_shs_wq.h8
4 files changed, 36 insertions, 90 deletions
diff --git a/drivers/rmnet/shs/rmnet_shs_config.c b/drivers/rmnet/shs/rmnet_shs_config.c
index ffa4f1d..4112e1a 100644
--- a/drivers/rmnet/shs/rmnet_shs_config.c
+++ b/drivers/rmnet/shs/rmnet_shs_config.c
@@ -164,14 +164,15 @@ static int rmnet_shs_dev_notify_cb(struct notifier_block *nb,
if (ret)
pr_err("%s(): rmnet ps_ind registration fail\n",
__func__);
-
+ rmnet_shs_update_cfg_mask();
+ rmnet_shs_wq_refresh_new_flow_list();
rmnet_shs_cfg.is_reg_dl_mrk_ind = 1;
trace_rmnet_shs_high(RMNET_SHS_MODULE,
RMNET_SHS_MODULE_INIT_WQ,
0xDEF, 0xDEF, 0xDEF,
0xDEF, NULL, NULL);
- RCU_INIT_POINTER(rmnet_shs_skb_entry,
- rmnet_shs_assign);
+ RCU_INIT_POINTER(rmnet_shs_skb_entry,
+ rmnet_shs_assign);
}
break;
diff --git a/drivers/rmnet/shs/rmnet_shs_main.c b/drivers/rmnet/shs/rmnet_shs_main.c
index b52e608..2ea09dc 100755
--- a/drivers/rmnet/shs/rmnet_shs_main.c
+++ b/drivers/rmnet/shs/rmnet_shs_main.c
@@ -1354,7 +1354,7 @@ void rmnet_shs_init(struct net_device *dev, struct net_device *vnd)
map_len = 0;
} else {
map_mask = rmnet_shs_mask_from_map(map);
- map_len = rmnet_shs_get_mask_len(rmnet_shs_cfg.map_mask);
+ map_len = rmnet_shs_get_mask_len(map_mask);
}
rmnet_shs_cfg.port = rmnet_get_port(dev);
diff --git a/drivers/rmnet/shs/rmnet_shs_wq.c b/drivers/rmnet/shs/rmnet_shs_wq.c
index 0f08137..1ec35ec 100644
--- a/drivers/rmnet/shs/rmnet_shs_wq.c
+++ b/drivers/rmnet/shs/rmnet_shs_wq.c
@@ -168,24 +168,16 @@ static struct list_head rmnet_shs_wq_ep_tbl =
*/
void rmnet_shs_wq_ep_tbl_add(struct rmnet_shs_wq_ep_s *ep)
{
- unsigned long flags;
trace_rmnet_shs_wq_low(RMNET_SHS_WQ_EP_TBL, RMNET_SHS_WQ_EP_TBL_ADD,
0xDEF, 0xDEF, 0xDEF, 0xDEF, ep, NULL);
- spin_lock_irqsave(&rmnet_shs_hstat_tbl_lock, flags);
list_add(&ep->ep_list_id, &rmnet_shs_wq_ep_tbl);
- spin_unlock_irqrestore(&rmnet_shs_hstat_tbl_lock, flags);
}
void rmnet_shs_wq_ep_tbl_remove(struct rmnet_shs_wq_ep_s *ep)
{
- unsigned long flags;
trace_rmnet_shs_wq_low(RMNET_SHS_WQ_EP_TBL, RMNET_SHS_WQ_EP_TBL_DEL,
0xDEF, 0xDEF, 0xDEF, 0xDEF, ep, NULL);
-
- spin_lock_irqsave(&rmnet_shs_hstat_tbl_lock, flags);
list_del_init(&ep->ep_list_id);
- spin_unlock_irqrestore(&rmnet_shs_hstat_tbl_lock, flags);
-
}
/* Helper functions to add and remove entries to the table
@@ -415,7 +407,7 @@ void rmnet_shs_wq_update_hstat_rps_msk(struct rmnet_shs_wq_hstat_s *hstat_p)
/*Map RPS mask from the endpoint associated with this flow*/
list_for_each_entry(ep, &rmnet_shs_wq_ep_tbl, ep_list_id) {
- if (ep && (node_p->dev == ep->ep->egress_dev)) {
+ if (ep && (node_p->dev == ep->ep)) {
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;
@@ -556,7 +548,7 @@ static void rmnet_shs_wq_refresh_dl_mrkr_stats(void)
tbl_p->dl_mrk_last_rx_bytes = tbl_p->dl_mrk_rx_bytes;
tbl_p->dl_mrk_last_rx_pkts = tbl_p->dl_mrk_rx_pkts;
- port = rmnet_get_port(rmnet_shs_delayed_wq->netdev);
+ port = rmnet_shs_cfg.port;
if (!port) {
rmnet_shs_crit_err[RMNET_SHS_WQ_GET_RMNET_PORT_ERR]++;
return;
@@ -735,7 +727,7 @@ void rmnet_shs_wq_chng_suggested_cpu(u16 old_cpu, u16 new_cpu,
hstat_p = node_p->hstats;
if ((hstat_p->suggested_cpu == old_cpu) &&
- (node_p->dev == ep->ep->egress_dev)) {
+ (node_p->dev == ep->ep)) {
trace_rmnet_shs_wq_high(RMNET_SHS_WQ_FLOW_STATS,
RMNET_SHS_WQ_FLOW_STATS_SUGGEST_NEW_CPU,
@@ -762,24 +754,6 @@ u64 rmnet_shs_wq_get_max_pps_among_cores(u32 core_msk)
return max_pps;
}
-u32 rmnet_shs_wq_get_dev_rps_msk(struct net_device *dev)
-{
- u32 dev_rps_msk = 0;
- struct rmnet_shs_wq_ep_s *ep = NULL;
-
- list_for_each_entry(ep, &rmnet_shs_wq_ep_tbl, ep_list_id) {
- if (!ep)
- continue;
-
- if (!ep->is_ep_active)
- continue;
-
- if (ep->ep->egress_dev == dev)
- dev_rps_msk = ep->rps_config_msk;
- }
- return dev_rps_msk;
-}
-
/* Returns the least utilized core from a core mask
* In order of priority
* 1) Returns leftmost core with no flows (Fully Idle)
@@ -1102,7 +1076,7 @@ int rmnet_shs_wq_get_lpwr_cpu_new_flow(struct net_device *dev)
if (!ep->is_ep_active)
continue;
- if (ep->ep->egress_dev == dev) {
+ if (ep->ep == dev) {
is_match_found = 1;
break;
}
@@ -1152,7 +1126,7 @@ int rmnet_shs_wq_get_perf_cpu_new_flow(struct net_device *dev)
if (!ep->is_ep_active)
continue;
- if (ep->ep->egress_dev == dev) {
+ if (ep->ep == dev) {
is_match_found = 1;
break;
}
@@ -1254,18 +1228,18 @@ void rmnet_shs_wq_update_ep_rps_msk(struct rmnet_shs_wq_ep_s *ep)
struct rps_map *map;
u8 len = 0;
- if (!ep || !ep->ep || !ep->ep->egress_dev) {
+ if (!ep || !ep->ep ) {
rmnet_shs_crit_err[RMNET_SHS_WQ_EP_ACCESS_ERR]++;
return;
}
rcu_read_lock();
- if (!ep->ep || !ep->ep->egress_dev) {
+ if (!ep->ep) {
pr_info(" rmnet_shs invalid state %p", ep->ep);
rmnet_shs_crit_err[RMNET_SHS_WQ_EP_ACCESS_ERR]++;
return;
}
- map = rcu_dereference(ep->ep->egress_dev->_rx->rps_map);
+ map = rcu_dereference(ep->ep->_rx->rps_map);
ep->rps_config_msk = 0;
if (map != NULL) {
@@ -1281,20 +1255,23 @@ void rmnet_shs_wq_update_ep_rps_msk(struct rmnet_shs_wq_ep_s *ep)
void rmnet_shs_wq_reset_ep_active(struct net_device *dev)
{
struct rmnet_shs_wq_ep_s *ep = NULL;
+ struct rmnet_shs_wq_ep_s *tmp = NULL;
unsigned long flags;
spin_lock_irqsave(&rmnet_shs_ep_lock, flags);
- list_for_each_entry(ep, &rmnet_shs_wq_ep_tbl, ep_list_id) {
+ list_for_each_entry_safe(ep, tmp, &rmnet_shs_wq_ep_tbl, ep_list_id) {
if (!ep)
continue;
- if (ep->netdev == dev){
+ if (ep->ep == dev){
ep->is_ep_active = 0;
- ep->netdev = NULL;
+ rmnet_shs_wq_ep_tbl_remove(ep);
+ kfree(ep);
+ break;
}
}
- spin_unlock_irqrestore(&rmnet_shs_ep_lock, flags);
+ spin_unlock_irqrestore(&rmnet_shs_ep_lock, flags);
}
void rmnet_shs_wq_set_ep_active(struct net_device *dev)
@@ -1303,16 +1280,21 @@ void rmnet_shs_wq_set_ep_active(struct net_device *dev)
unsigned long flags;
spin_lock_irqsave(&rmnet_shs_ep_lock, flags);
- list_for_each_entry(ep, &rmnet_shs_wq_ep_tbl, ep_list_id) {
- if (!ep)
- continue;
- if (ep->ep->egress_dev == dev){
- ep->is_ep_active = 1;
- ep->netdev = dev;
+ ep = kzalloc(sizeof(*ep), GFP_ATOMIC);
- }
+ if (!ep) {
+ rmnet_shs_crit_err[RMNET_SHS_WQ_ALLOC_EP_TBL_ERR]++;
+ spin_unlock_irqrestore(&rmnet_shs_ep_lock, flags);
+ return;
}
+ ep->ep = dev;
+ ep->is_ep_active = 1;
+
+ INIT_LIST_HEAD(&ep->ep_list_id);
+ rmnet_shs_wq_update_ep_rps_msk(ep);
+ rmnet_shs_wq_ep_tbl_add(ep);
+
spin_unlock_irqrestore(&rmnet_shs_ep_lock, flags);
}
@@ -1443,34 +1425,6 @@ void rmnet_shs_wq_exit(void)
0xDEF, 0xDEF, 0xDEF, 0xDEF, NULL, NULL);
}
-void rmnet_shs_wq_gather_rmnet_ep(struct net_device *dev)
-{
- u8 mux_id;
- struct rmnet_port *port;
- struct rmnet_endpoint *ep;
- struct rmnet_shs_wq_ep_s *ep_wq;
-
- port = rmnet_get_port(dev);
-
- for (mux_id = 1; mux_id < 255; mux_id++) {
- ep = rmnet_get_endpoint(port, mux_id);
- if (!ep)
- continue;
-
- trace_rmnet_shs_wq_high(RMNET_SHS_WQ_EP_TBL,
- RMNET_SHS_WQ_EP_TBL_INIT,
- 0xDEF, 0xDEF, 0xDEF, 0xDEF, ep, NULL);
- ep_wq = kzalloc(sizeof(*ep_wq), GFP_ATOMIC);
- if (!ep_wq) {
- rmnet_shs_crit_err[RMNET_SHS_WQ_ALLOC_EP_TBL_ERR]++;
- return;
- }
- INIT_LIST_HEAD(&ep_wq->ep_list_id);
- ep_wq->ep = ep;
- rmnet_shs_wq_update_ep_rps_msk(ep_wq);
- rmnet_shs_wq_ep_tbl_add(ep_wq);
- }
-}
void rmnet_shs_wq_init_cpu_rx_flow_tbl(void)
{
u8 cpu_num;
@@ -1527,21 +1481,12 @@ void rmnet_shs_wq_init(struct net_device *dev)
return;
}
- rmnet_shs_delayed_wq->netdev = dev;
- rmnet_shs_wq_gather_rmnet_ep(dev);
-
/*All hstat nodes allocated during Wq init will be held for ever*/
rmnet_shs_wq_hstat_alloc_nodes(RMNET_SHS_MIN_HSTAT_NODES_REQD, 1);
rmnet_shs_wq_init_cpu_rx_flow_tbl();
INIT_DEFERRABLE_WORK(&rmnet_shs_delayed_wq->wq,
rmnet_shs_wq_process_wq);
- /* During initialization, we can start workqueue without a delay
- * to initialize all meta data and pre allocated memory
- * for hash stats, if required
- */
- queue_delayed_work(rmnet_shs_wq, &rmnet_shs_delayed_wq->wq, 0);
-
trace_rmnet_shs_wq_high(RMNET_SHS_WQ_INIT, RMNET_SHS_WQ_INIT_END,
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 b0bbd9c..90d1604 100644
--- a/drivers/rmnet/shs/rmnet_shs_wq.h
+++ b/drivers/rmnet/shs/rmnet_shs_wq.h
@@ -32,8 +32,7 @@
struct rmnet_shs_wq_ep_s {
struct list_head ep_list_id;
- struct rmnet_endpoint *ep;
- struct net_device *netdev;
+ struct net_device *ep;
int new_lo_core[MAX_CPUS];
int new_hi_core[MAX_CPUS];
u16 default_core_msk;
@@ -133,7 +132,6 @@ struct rmnet_shs_wq_rx_flow_s {
struct rmnet_shs_delay_wq_s {
struct delayed_work wq;
- struct net_device *netdev;
};
@@ -214,6 +212,8 @@ void rmnet_shs_wq_exit(void);
void rmnet_shs_wq_restart(void);
void rmnet_shs_wq_pause(void);
+void rmnet_shs_update_cfg_mask(void);
+
u64 rmnet_shs_wq_get_max_pps_among_cores(u32 core_msk);
void rmnet_shs_wq_create_new_flow(struct rmnet_shs_skbn_s *node_p);
int rmnet_shs_wq_get_least_utilized_core(u16 core_msk);
@@ -221,9 +221,9 @@ int rmnet_shs_wq_get_lpwr_cpu_new_flow(struct net_device *dev);
int rmnet_shs_wq_get_perf_cpu_new_flow(struct net_device *dev);
u64 rmnet_shs_wq_get_max_allowed_pps(u16 cpu);
void rmnet_shs_wq_inc_cpu_flow(u16 cpu);
-u32 rmnet_shs_wq_get_dev_rps_msk(struct net_device *dev);
void rmnet_shs_wq_dec_cpu_flow(u16 cpu);
void rmnet_shs_hstat_tbl_delete(void);
void rmnet_shs_wq_set_ep_active(struct net_device *dev);
void rmnet_shs_wq_reset_ep_active(struct net_device *dev);
+void rmnet_shs_wq_refresh_new_flow_list(void);
#endif /*_RMNET_SHS_WQ_H_*/