فهرست منبع

dfc: add stall recovery timer

Add watchdog timer to recover potential data stall when data is
not going to the expected DRB and no DFC indication is received.

Change-Id: Iaa4b4814967cf9400c36115a083922376d23928d
Acked-by: Weiyi Chen <[email protected]>
Signed-off-by: Subash Abhinov Kasiviswanathan <[email protected]>
Subash Abhinov Kasiviswanathan 5 سال پیش
والد
کامیت
1842e30e8a
6فایلهای تغییر یافته به همراه163 افزوده شده و 10 حذف شده
  1. 22 0
      core/dfc.h
  2. 8 3
      core/dfc_qmi.c
  3. 107 3
      core/qmi_rmnet.c
  4. 4 2
      core/qmi_rmnet.h
  5. 19 0
      core/qmi_rmnet_i.h
  6. 3 2
      core/rmnet_vnd.c

+ 22 - 0
core/dfc.h

@@ -292,6 +292,28 @@ TRACE_EVENT(dfc_adjust_grant,
 		__entry->rx_bytes, __entry->inflight, __entry->a_grant)
 );
 
+TRACE_EVENT(dfc_watchdog,
+
+	TP_PROTO(u8 mux_id, u8 bearer_id, u8 event),
+
+	TP_ARGS(mux_id, bearer_id, event),
+
+	TP_STRUCT__entry(
+		__field(u8, mux_id)
+		__field(u8, bearer_id)
+		__field(u8, event)
+	),
+
+	TP_fast_assign(
+		__entry->mux_id = mux_id;
+		__entry->bearer_id = bearer_id;
+		__entry->event = event;
+	),
+
+	TP_printk("mid=%u bid=%u event=%u",
+		__entry->mux_id, __entry->bearer_id, __entry->event)
+);
+
 #endif /* _TRACE_DFC_H */
 
 /* This part must be outside protection */

+ 8 - 3
core/dfc_qmi.c

@@ -1034,14 +1034,18 @@ static int dfc_update_fc_map(struct net_device *dev, struct qos_info *qos,
 		itm->grant_size = adjusted_grant;
 
 		/* No further query if the adjusted grant is less
-		 * than 20% of the original grant
+		 * than 20% of the original grant. Add to watch to
+		 * recover if no indication is received.
 		 */
 		if (dfc_qmap && is_query &&
-		    itm->grant_size < (fc_info->num_bytes / 5))
+		    itm->grant_size < (fc_info->num_bytes / 5)) {
 			itm->grant_thresh = itm->grant_size;
-		else
+			qmi_rmnet_watchdog_add(itm);
+		} else {
 			itm->grant_thresh =
 				qmi_rmnet_grant_per(itm->grant_size);
+			qmi_rmnet_watchdog_remove(itm);
+		}
 
 		itm->seq = fc_info->seq_num;
 		itm->ack_req = ack_req;
@@ -1143,6 +1147,7 @@ static void dfc_update_tx_link_status(struct net_device *dev,
 		itm->grant_size = 0;
 		itm->tcp_bidir = false;
 		itm->bytes_in_flight = 0;
+		qmi_rmnet_watchdog_remove(itm);
 		dfc_bearer_flow_ctl(dev, itm, qos);
 	} else if (itm->grant_size == 0 && tx_status && !itm->rat_switch) {
 		itm->grant_size = DEFAULT_GRANT;

+ 107 - 3
core/qmi_rmnet.c

@@ -53,6 +53,7 @@ unsigned int rmnet_wq_frequency __read_mostly = 1000;
 					1 : rmnet_wq_frequency/10) * (HZ/100))
 #define NO_DELAY (0x0000 * HZ)
 #define PS_INTERVAL_KT (ms_to_ktime(1000))
+#define WATCHDOG_EXPIRE_JF (msecs_to_jiffies(50))
 
 #ifdef CONFIG_QTI_QMI_DFC
 static unsigned int qmi_rmnet_scale_factor = 5;
@@ -233,6 +234,89 @@ static void qmi_rmnet_reset_txq(struct net_device *dev, unsigned int txq)
 	}
 }
 
+/**
+ * qmi_rmnet_watchdog_fn - watchdog timer func
+ */
+static void qmi_rmnet_watchdog_fn(struct timer_list *t)
+{
+	struct rmnet_bearer_map *bearer;
+
+	bearer = container_of(t, struct rmnet_bearer_map, watchdog);
+
+	trace_dfc_watchdog(bearer->qos->mux_id, bearer->bearer_id, 2);
+
+	spin_lock_bh(&bearer->qos->qos_lock);
+
+	if (bearer->watchdog_quit)
+		goto done;
+
+	/*
+	 * Possible stall, try to recover. Enable 80% query and jumpstart
+	 * the bearer if disabled.
+	 */
+	bearer->watchdog_expire_cnt++;
+	bearer->bytes_in_flight = 0;
+	if (!bearer->grant_size) {
+		bearer->grant_size = DEFAULT_CALL_GRANT;
+		bearer->grant_thresh = qmi_rmnet_grant_per(bearer->grant_size);
+		dfc_bearer_flow_ctl(bearer->qos->vnd_dev, bearer, bearer->qos);
+	} else {
+		bearer->grant_thresh = qmi_rmnet_grant_per(bearer->grant_size);
+	}
+
+done:
+	bearer->watchdog_started = false;
+	spin_unlock_bh(&bearer->qos->qos_lock);
+}
+
+/**
+ * qmi_rmnet_watchdog_add - add the bearer to watch
+ * Needs to be called with qos_lock
+ */
+void qmi_rmnet_watchdog_add(struct rmnet_bearer_map *bearer)
+{
+	bearer->watchdog_quit = false;
+
+	if (bearer->watchdog_started)
+		return;
+
+	bearer->watchdog_started = true;
+	mod_timer(&bearer->watchdog, jiffies + WATCHDOG_EXPIRE_JF);
+
+	trace_dfc_watchdog(bearer->qos->mux_id, bearer->bearer_id, 1);
+}
+
+/**
+ * qmi_rmnet_watchdog_remove - remove the bearer from watch
+ * Needs to be called with qos_lock
+ */
+void qmi_rmnet_watchdog_remove(struct rmnet_bearer_map *bearer)
+{
+	bearer->watchdog_quit = true;
+
+	if (!bearer->watchdog_started)
+		return;
+
+	del_timer(&bearer->watchdog);
+	bearer->watchdog_started = false;
+
+	trace_dfc_watchdog(bearer->qos->mux_id, bearer->bearer_id, 0);
+}
+
+/**
+ * qmi_rmnet_bearer_clean - clean the removed bearer
+ * Needs to be called with rtn_lock but not qos_lock
+ */
+static void qmi_rmnet_bearer_clean(struct qos_info *qos)
+{
+	if (qos->removed_bearer) {
+		qos->removed_bearer->watchdog_quit = true;
+		del_timer_sync(&qos->removed_bearer->watchdog);
+		kfree(qos->removed_bearer);
+		qos->removed_bearer = NULL;
+	}
+}
+
 static struct rmnet_bearer_map *__qmi_rmnet_bearer_get(
 				struct qos_info *qos_info, u8 bearer_id)
 {
@@ -252,6 +336,8 @@ static struct rmnet_bearer_map *__qmi_rmnet_bearer_get(
 		bearer->grant_thresh = qmi_rmnet_grant_per(bearer->grant_size);
 		bearer->mq_idx = INVALID_MQ;
 		bearer->ack_mq_idx = INVALID_MQ;
+		bearer->qos = qos_info;
+		timer_setup(&bearer->watchdog, qmi_rmnet_watchdog_fn, 0);
 		list_add(&bearer->list, &qos_info->bearer_head);
 	}
 
@@ -287,7 +373,7 @@ static void __qmi_rmnet_bearer_put(struct net_device *dev,
 
 		/* Remove from bearer map */
 		list_del(&bearer->list);
-		kfree(bearer);
+		qos_info->removed_bearer = bearer;
 	}
 }
 
@@ -415,6 +501,9 @@ again:
 
 done:
 	spin_unlock_bh(&qos_info->qos_lock);
+
+	qmi_rmnet_bearer_clean(qos_info);
+
 	return rc;
 }
 
@@ -458,6 +547,9 @@ qmi_rmnet_del_flow(struct net_device *dev, struct tcmsg *tcm,
 		netif_tx_wake_all_queues(dev);
 
 	spin_unlock_bh(&qos_info->qos_lock);
+
+	qmi_rmnet_bearer_clean(qos_info);
+
 	return 0;
 }
 
@@ -726,6 +818,8 @@ void qmi_rmnet_enable_all_flows(struct net_device *dev)
 		bearer->tcp_bidir = false;
 		bearer->rat_switch = false;
 
+		qmi_rmnet_watchdog_remove(bearer);
+
 		if (bearer->tx_off)
 			continue;
 
@@ -873,7 +967,8 @@ inline unsigned int qmi_rmnet_grant_per(unsigned int grant)
 }
 EXPORT_SYMBOL(qmi_rmnet_grant_per);
 
-void *qmi_rmnet_qos_init(struct net_device *real_dev, u8 mux_id)
+void *qmi_rmnet_qos_init(struct net_device *real_dev,
+			 struct net_device *vnd_dev, u8 mux_id)
 {
 	struct qos_info *qos;
 
@@ -883,6 +978,7 @@ void *qmi_rmnet_qos_init(struct net_device *real_dev, u8 mux_id)
 
 	qos->mux_id = mux_id;
 	qos->real_dev = real_dev;
+	qos->vnd_dev = vnd_dev;
 	qos->tran_num = 0;
 	INIT_LIST_HEAD(&qos->flow_head);
 	INIT_LIST_HEAD(&qos->bearer_head);
@@ -894,10 +990,18 @@ EXPORT_SYMBOL(qmi_rmnet_qos_init);
 
 void qmi_rmnet_qos_exit_pre(void *qos)
 {
+	struct qos_info *qosi = (struct qos_info *)qos;
+	struct rmnet_bearer_map *bearer;
+
 	if (!qos)
 		return;
 
-	list_add(&((struct qos_info *)qos)->list, &qos_cleanup_list);
+	list_for_each_entry(bearer, &qosi->bearer_head, list) {
+		bearer->watchdog_quit = true;
+		del_timer_sync(&bearer->watchdog);
+	}
+
+	list_add(&qosi->list, &qos_cleanup_list);
 }
 EXPORT_SYMBOL(qmi_rmnet_qos_exit_pre);
 

+ 4 - 2
core/qmi_rmnet.h

@@ -55,7 +55,8 @@ qmi_rmnet_all_flows_enabled(struct net_device *dev)
 #endif
 
 #ifdef CONFIG_QTI_QMI_DFC
-void *qmi_rmnet_qos_init(struct net_device *real_dev, u8 mux_id);
+void *qmi_rmnet_qos_init(struct net_device *real_dev,
+			 struct net_device *vnd_dev, u8 mux_id);
 void qmi_rmnet_qos_exit_pre(void *qos);
 void qmi_rmnet_qos_exit_post(void);
 void qmi_rmnet_burst_fc_check(struct net_device *dev,
@@ -63,7 +64,8 @@ void qmi_rmnet_burst_fc_check(struct net_device *dev,
 int qmi_rmnet_get_queue(struct net_device *dev, struct sk_buff *skb);
 #else
 static inline void *
-qmi_rmnet_qos_init(struct net_device *real_dev, u8 mux_id)
+qmi_rmnet_qos_init(struct net_device *real_dev,
+		   struct net_device *vnd_dev, u8 mux_id)
 {
 	return NULL;
 }

+ 19 - 0
core/qmi_rmnet_i.h

@@ -16,6 +16,7 @@
 
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/timer.h>
 
 #define MAX_MQ_NUM 16
 #define MAX_CLIENT_NUM 2
@@ -36,6 +37,8 @@
 extern int dfc_mode;
 extern int dfc_qmap;
 
+struct qos_info;
+
 struct rmnet_bearer_map {
 	struct list_head list;
 	u8 bearer_id;
@@ -54,6 +57,11 @@ struct rmnet_bearer_map {
 	u32 ack_txid;
 	u32 mq_idx;
 	u32 ack_mq_idx;
+	struct qos_info *qos;
+	struct timer_list watchdog;
+	bool watchdog_started;
+	bool watchdog_quit;
+	u32 watchdog_expire_cnt;
 };
 
 struct rmnet_flow_map {
@@ -79,11 +87,13 @@ struct qos_info {
 	struct list_head list;
 	u8 mux_id;
 	struct net_device *real_dev;
+	struct net_device *vnd_dev;
 	struct list_head flow_head;
 	struct list_head bearer_head;
 	struct mq_map mq[MAX_MQ_NUM];
 	u32 tran_num;
 	spinlock_t qos_lock;
+	struct rmnet_bearer_map *removed_bearer;
 };
 
 struct qmi_info {
@@ -154,6 +164,11 @@ void dfc_qmap_send_ack(struct qos_info *qos, u8 bearer_id, u16 seq, u8 type);
 
 struct rmnet_bearer_map *qmi_rmnet_get_bearer_noref(struct qos_info *qos_info,
 						    u8 bearer_id);
+
+void qmi_rmnet_watchdog_add(struct rmnet_bearer_map *bearer);
+
+void qmi_rmnet_watchdog_remove(struct rmnet_bearer_map *bearer);
+
 #else
 static inline struct rmnet_flow_map *
 qmi_rmnet_get_flow_map(struct qos_info *qos_info,
@@ -197,6 +212,10 @@ dfc_qmap_client_init(void *port, int index, struct svc_info *psvc,
 static inline void dfc_qmap_client_exit(void *dfc_data)
 {
 }
+
+static inline void qmi_rmnet_watchdog_remove(struct rmnet_bearer_map *bearer)
+{
+}
 #endif
 
 #ifdef CONFIG_QTI_QMI_POWER_COLLAPSE

+ 3 - 2
core/rmnet_vnd.c

@@ -128,7 +128,7 @@ static void rmnet_vnd_uninit(struct net_device *dev)
 	gro_cells_destroy(&priv->gro_cells);
 	free_percpu(priv->pcpu_stats);
 
-	qos = priv->qos_info;
+	qos = rcu_dereference(priv->qos_info);
 	RCU_INIT_POINTER(priv->qos_info, NULL);
 	qmi_rmnet_qos_exit_pre(qos);
 }
@@ -402,7 +402,8 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
 		rmnet_dev->rtnl_link_ops = &rmnet_link_ops;
 
 		priv->mux_id = id;
-		priv->qos_info = qmi_rmnet_qos_init(real_dev, id);
+		rcu_assign_pointer(priv->qos_info,
+			qmi_rmnet_qos_init(real_dev, rmnet_dev, id));
 
 		netdev_dbg(rmnet_dev, "rmnet dev created\n");
 	}