Merge "drivers: rmnet_shs: Fix shs_boost_wq memleak"
diff --git a/drivers/rmnet/perf/rmnet_perf_config.c b/drivers/rmnet/perf/rmnet_perf_config.c
index 2489d6d..0ebc6f4 100644
--- a/drivers/rmnet/perf/rmnet_perf_config.c
+++ b/drivers/rmnet/perf/rmnet_perf_config.c
@@ -125,6 +125,9 @@
 	 */
 	rmnet_perf_core_free_held_skbs();
 
+	/* Clean up any remaining nodes in the flow table before freeing */
+	rmnet_perf_free_hash_table();
+
 	/* Since we allocated in one chunk, we will also free in one chunk */
 	kfree(perf);
 
@@ -427,6 +430,7 @@
 
 static struct notifier_block rmnet_perf_dev_notifier __read_mostly = {
 	.notifier_call = rmnet_perf_config_notify_cb,
+	.priority = 1,
 };
 
 int __init rmnet_perf_init(void)
diff --git a/drivers/rmnet/perf/rmnet_perf_opt.c b/drivers/rmnet/perf/rmnet_perf_opt.c
index 1a695d8..b411483 100644
--- a/drivers/rmnet/perf/rmnet_perf_opt.c
+++ b/drivers/rmnet/perf/rmnet_perf_opt.c
@@ -709,6 +709,18 @@
 	if (pkt_info->trans_proto == IPPROTO_TCP)
 		flow_node->next_seq += payload_len;
 }
+void
+rmnet_perf_free_hash_table()
+{
+	int i;
+	struct rmnet_perf_opt_flow_node *flow_node;
+	struct hlist_node *tmp;
+
+	hash_for_each_safe(rmnet_perf_opt_fht, i, tmp, flow_node, list) {
+		hash_del(&flow_node->list);
+	}
+
+}
 
 /* rmnet_perf_opt_ingress() - Core business logic of optimization framework
  * @pkt_info: characteristics of the current packet
diff --git a/drivers/rmnet/perf/rmnet_perf_opt.h b/drivers/rmnet/perf/rmnet_perf_opt.h
index b90a7a9..b98261d 100644
--- a/drivers/rmnet/perf/rmnet_perf_opt.h
+++ b/drivers/rmnet/perf/rmnet_perf_opt.h
@@ -107,5 +107,6 @@
 			struct rmnet_perf_opt_flow_node *flow_node,
 			struct rmnet_perf_pkt_info *pkt_info);
 bool rmnet_perf_opt_ingress(struct rmnet_perf_pkt_info *pkt_info);
+void rmnet_perf_free_hash_table(void);
 
 #endif /* _RMNET_PERF_OPT_H_ */
diff --git a/drivers/rmnet/shs/rmnet_shs_config.c b/drivers/rmnet/shs/rmnet_shs_config.c
index 74799ee..341dcc9 100644
--- a/drivers/rmnet/shs/rmnet_shs_config.c
+++ b/drivers/rmnet/shs/rmnet_shs_config.c
@@ -48,13 +48,7 @@
 /* Enable smart hashing capability upon call to initialize module*/
 int __init rmnet_shs_module_init(void)
 {
-
-	if (unlikely(rmnet_shs_debug))
-		pr_info("%s(): Initializing rmnet SHS module\n", __func__);
-
-	if (!rmnet_shs_skb_entry)
-		RCU_INIT_POINTER(rmnet_shs_skb_entry, rmnet_shs_assign);
-
+	pr_info("%s(): Starting rmnet SHS module\n", __func__);
 	trace_rmnet_shs_high(RMNET_SHS_MODULE, RMNET_SHS_MODULE_INIT,
 			    0xDEF, 0xDEF, 0xDEF, 0xDEF, NULL, NULL);
 	return register_netdevice_notifier(&rmnet_shs_dev_notifier);
@@ -63,21 +57,10 @@
 /* Remove smart hashing capability upon call to initialize module */
 void __exit rmnet_shs_module_exit(void)
 {
-	RCU_INIT_POINTER(rmnet_shs_skb_entry, NULL);
-
-	if (rmnet_shs_cfg.rmnet_shs_init_complete) {
-		qmi_rmnet_ps_ind_deregister(rmnet_shs_cfg.port,
-					    &rmnet_shs_cfg.rmnet_idl_ind_cb);
-		rmnet_shs_cancel_table();
-		rmnet_shs_rx_wq_exit();
-		rmnet_shs_wq_exit();
-		rmnet_shs_exit();
-	}
-	unregister_netdevice_notifier(&rmnet_shs_dev_notifier);
-	if (unlikely(rmnet_shs_debug))
-		pr_info("Exiting rmnet_shs module");
 	trace_rmnet_shs_high(RMNET_SHS_MODULE, RMNET_SHS_MODULE_EXIT,
 			    0xDEF, 0xDEF, 0xDEF, 0xDEF, NULL, NULL);
+	unregister_netdevice_notifier(&rmnet_shs_dev_notifier);
+	pr_info("%s(): Exiting rmnet SHS module\n", __func__);
 }
 
 static int rmnet_shs_dev_notify_cb(struct notifier_block *nb,
@@ -85,7 +68,6 @@
 {
 
 	struct net_device *dev = netdev_notifier_info_to_dev(data);
-	static struct net_device *phy_dev = NULL;
 	struct rmnet_priv *priv;
 	struct rmnet_port *port;
 
@@ -108,6 +90,7 @@
 		    (!strcmp(dev->name, "rmnet_ipa0") ||
 		    !strcmp(dev->name, "rmnet_mhi0"))) &&
 		    rmnet_shs_cfg.rmnet_shs_init_complete) {
+			pr_info("rmnet_shs deinit %s going down ", dev->name);
 			RCU_INIT_POINTER(rmnet_shs_skb_entry, NULL);
 			qmi_rmnet_ps_ind_deregister(rmnet_shs_cfg.port,
 					    &rmnet_shs_cfg.rmnet_idl_ind_cb);
@@ -123,10 +106,6 @@
 		break;
 
 	case NETDEV_UP:
-		if (strncmp(dev->name, "rmnet_ipa0", 10) == 0 ||
-		    strncmp(dev->name, "rmnet_mhi0", 10) == 0)
-			phy_dev = dev;
-
 
 		if (strncmp(dev->name, "rmnet_data", 10) == 0){
 			rmnet_vnd_total++;
@@ -137,15 +116,20 @@
 			 * NULL dereferencing
 			 */
 
-			if (phy_dev && !rmnet_shs_cfg.rmnet_shs_init_complete) {
-				rmnet_shs_init(phy_dev, dev);
-				rmnet_shs_wq_init(phy_dev);
+			if (!rmnet_shs_cfg.rmnet_shs_init_complete) {
+				pr_info("rmnet_shs initializing %s", dev->name);
+				priv = netdev_priv(dev);
+				port = rmnet_get_port(priv->real_dev);
+				if (!port) {
+					pr_err("rmnet_shs: invalid rmnet_port");
+					break;
+				}
+				rmnet_shs_init(priv->real_dev, dev);
+				rmnet_shs_wq_init(priv->real_dev);
 				rmnet_shs_rx_wq_init();
 				rmnet_shs_cfg.is_timer_init = 1;
 				rmnet_shs_cfg.dl_mrk_ind_cb.priority =
 				   RMNET_SHS;
-				priv = netdev_priv(dev);
-				port = rmnet_get_port(priv->real_dev);
 				if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER_V2) {
 					rmnet_shs_cfg.dl_mrk_ind_cb.dl_hdr_handler_v2 =
 						&rmnet_shs_dl_hdr_handler_v2;
diff --git a/drivers/rmnet/shs/rmnet_shs_main.c b/drivers/rmnet/shs/rmnet_shs_main.c
index 6c7acd5..4b7734b 100755
--- a/drivers/rmnet/shs/rmnet_shs_main.c
+++ b/drivers/rmnet/shs/rmnet_shs_main.c
@@ -1659,6 +1659,7 @@
 		hrtimer_cancel(&rmnet_shs_cfg.hrtimer_shs);
 
 	memset(&rmnet_shs_cfg, 0, sizeof(rmnet_shs_cfg));
+	rmnet_shs_cfg.port = NULL;
 	rmnet_shs_cfg.rmnet_shs_init_complete = 0;
 
 }
diff --git a/drivers/rmnet/shs/rmnet_shs_wq.c b/drivers/rmnet/shs/rmnet_shs_wq.c
index 6c61dfa..1321d31 100644
--- a/drivers/rmnet/shs/rmnet_shs_wq.c
+++ b/drivers/rmnet/shs/rmnet_shs_wq.c
@@ -1250,7 +1250,13 @@
 	}
 
 	rcu_read_lock();
+	if (!ep->ep || !ep->ep->egress_dev) {
+		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);
+
 	ep->rps_config_msk = 0;
 	if (map != NULL) {
 		for (len = 0; len < map->len; len++)