[RFC PATCH 6.6.y 2/4] misc: ti-st: account published transport users

Hongyan Xu <[email protected]>
Newsgroups org.kernel.vger.linux-gpio,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
The shared transport entry points obtain a raw core pointer through
st_kim_ref().  Removal needs to stop new lookups and wait for existing
users before freeing that core.

Serialize publication and lookup, count successful lookups, and use a
scope cleanup to release the count on every return path.  This prepares
the removal path to drain the users without a large error-path rewrite.

Fixes: 53618cc1e51e ("Staging: sources for ST core")
Cc: [email protected]
Signed-off-by: Hongyan Xu <[email protected]>
---
 drivers/misc/ti-st/st_core.c | 15 +++++++++++----
 drivers/misc/ti-st/st_kim.c  | 29 +++++++++++++++++++++++------
 include/linux/ti_wilink_st.h |  8 ++++++++
 3 files changed, 42 insertions(+), 10 deletions(-)

diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c
index b878431..43d2587 100644
--- a/drivers/misc/ti-st/st_core.c
+++ b/drivers/misc/ti-st/st_core.c
@@ -7,6 +7,7 @@
  */
 
 #define pr_fmt(fmt)	"(stc): " fmt
+#include <linux/cleanup.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/tty.h>
@@ -24,6 +25,8 @@
  */
 static void (*st_recv)(void *disc_data, const u8 *ptr, size_t count);
 
+DEFINE_FREE(st_kim, struct st_data_s *, st_kim_put(_T));
+
 /********************************************************************/
 static void add_channel_to_table(struct st_data_s *st_gdata,
 		struct st_proto_s *new_proto)
@@ -522,7 +525,7 @@ void kim_st_list_protocols(struct st_data_s *st_gdata, void *buf)
  */
 long st_register(struct st_proto_s *new_proto)
 {
-	struct st_data_s	*st_gdata;
+	struct st_data_s *st_gdata __free(st_kim) = NULL;
 	long err = 0;
 	unsigned long flags = 0;
 
@@ -640,7 +643,7 @@ long st_unregister(struct st_proto_s *proto)
 {
 	long err = 0;
 	unsigned long flags = 0;
-	struct st_data_s	*st_gdata;
+	struct st_data_s *st_gdata __free(st_kim) = NULL;
 
 	pr_debug("%s: %d ", __func__, proto->chnl_id);
 
@@ -688,7 +691,7 @@ long st_unregister(struct st_proto_s *proto)
  */
 long st_write(struct sk_buff *skb)
 {
-	struct st_data_s *st_gdata;
+	struct st_data_s *st_gdata __free(st_kim) = NULL;
 	long len;
 
 	st_kim_ref(&st_gdata, 0);
@@ -719,10 +722,12 @@ EXPORT_SYMBOL_GPL(st_unregister);
  */
 static int st_tty_open(struct tty_struct *tty)
 {
-	struct st_data_s *st_gdata;
+	struct st_data_s *st_gdata __free(st_kim) = NULL;
 	pr_info("%s ", __func__);
 
 	st_kim_ref(&st_gdata, 0);
+	if (!st_gdata)
+		return -ENODEV;
 	st_gdata->tty = tty;
 	tty->disc_data = st_gdata;
 
@@ -878,6 +883,8 @@ int st_core_init(struct st_data_s **core_data)
 
 	/* Locking used in st_int_enqueue() to avoid multiple execution */
 	spin_lock_init(&st_gdata->lock);
+	atomic_set(&st_gdata->active_users, 0);
+	init_waitqueue_head(&st_gdata->users_wait);
 
 	err = st_ll_init(st_gdata);
 	if (err) {
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index ec265a6..11eadc1 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -23,9 +23,11 @@
 #include <linux/skbuff.h>
 #include <linux/ti_wilink_st.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 
 #define MAX_ST_DEVICES	3	/* Imagine 1 on each UART for now */
 static struct platform_device *st_kim_devices[MAX_ST_DEVICES];
+static DEFINE_MUTEX(st_kim_mutex);
 
 /**********************************************************************/
 /* internal functions */
@@ -671,18 +673,31 @@ void st_kim_ref(struct st_data_s **core_data, int id)
 {
 	struct platform_device	*pdev;
 	struct kim_data_s	*kim_gdata;
+
+	*core_data = NULL;
+	mutex_lock(&st_kim_mutex);
 	/* get kim_gdata reference from platform device */
 	pdev = st_get_plat_device(id);
 	if (!pdev)
-		goto err;
+		goto out;
 	kim_gdata = platform_get_drvdata(pdev);
-	if (!kim_gdata)
-		goto err;
+	if (!kim_gdata || !kim_gdata->core_data ||
+	    kim_gdata->core_data->removing)
+		goto out;
 
+	atomic_inc(&kim_gdata->core_data->active_users);
 	*core_data = kim_gdata->core_data;
-	return;
-err:
-	*core_data = NULL;
+out:
+	mutex_unlock(&st_kim_mutex);
+}
+
+void st_kim_put(struct st_data_s *st_gdata)
+{
+	if (!st_gdata)
+		return;
+
+	if (atomic_dec_and_test(&st_gdata->active_users))
+		wake_up(&st_gdata->users_wait);
 }
 
 DEFINE_SHOW_ATTRIBUTE(version);
@@ -756,7 +771,9 @@ static int kim_probe(struct platform_device *pdev)
 				kim_gdata, &list_fops);
 	if (pdev->id >= 0 && pdev->id < MAX_ST_DEVICES)
 		id = pdev->id;
+	mutex_lock(&st_kim_mutex);
 	st_kim_devices[id] = pdev;
+	mutex_unlock(&st_kim_mutex);
 	return 0;
 
 err_sysfs_group:
diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h
index 10642d4..f91300d 100644
--- a/include/linux/ti_wilink_st.h
+++ b/include/linux/ti_wilink_st.h
@@ -13,6 +13,7 @@
 #define TI_WILINK_ST_H
 
 #include <linux/skbuff.h>
+#include <linux/wait.h>
 
 /**
  * enum proto-type - The protocol on WiLink chips which share a
@@ -125,6 +126,9 @@ extern long st_unregister(struct st_proto_s *);
  * @ll_state: the various PM states the chip can be, the states are notified
  *	to us, when the chip sends relevant PM packets(SLEEP_IND, WAKE_IND).
  * @kim_data: reference to the parent encapsulating structure.
+ * @active_users: number of published core users being drained by removal.
+ * @users_wait: wait queue used by removal to drain published core users.
+ * @removing: set once the core is no longer available to new users.
  *
  */
 struct st_data_s {
@@ -144,6 +148,9 @@ struct st_data_s {
 	unsigned char	protos_registered;
 	unsigned long ll_state;
 	void *kim_data;
+	atomic_t active_users;
+	wait_queue_head_t users_wait;
+	bool removing;
 	struct tty_struct *tty;
 	struct work_struct work_write_wakeup;
 };
@@ -179,6 +186,7 @@ void st_core_exit(struct st_data_s *);
 
 /* ask for reference from KIM */
 void st_kim_ref(struct st_data_s **, int);
+void st_kim_put(struct st_data_s *st_gdata);
 
 #define GPS_STUB_TEST
 #ifdef GPS_STUB_TEST
-- 
2.50.1.windows.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.