From: Alan Cox <alan@linux.intel.com>
Date: Sat, 19 Sep 2009 20:13:22 +0000 (-0700)
Subject: tty: make the kref destructor occur asynchronously
X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=b50989dc444599c8b21edc23536fc305f4e9b7d5;p=GitHub%2FLineageOS%2FG12%2Fandroid_kernel_amlogic_linux-4.9.git

tty: make the kref destructor occur asynchronously

We want to be able to sleep in the destructor for USB at least. It isn't a
hot path so just pushing it to a work queue doesn't really cause any
difficulty.

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---

diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 938448067738..385cca7074da 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -1386,10 +1386,14 @@ EXPORT_SYMBOL(tty_shutdown);
  *		tty_mutex - sometimes only
  *		takes the file list lock internally when working on the list
  *	of ttys that the driver keeps.
+ *
+ *	This method gets called from a work queue so that the driver private
+ *	shutdown ops can sleep (needed for USB at least)
  */
-static void release_one_tty(struct kref *kref)
+static void release_one_tty(struct work_struct *work)
 {
-	struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
+	struct tty_struct *tty =
+		container_of(work, struct tty_struct, hangup_work);
 	struct tty_driver *driver = tty->driver;
 
 	if (tty->ops->shutdown)
@@ -1407,6 +1411,15 @@ static void release_one_tty(struct kref *kref)
 	free_tty_struct(tty);
 }
 
+static void queue_release_one_tty(struct kref *kref)
+{
+	struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
+	/* The hangup queue is now free so we can reuse it rather than
+	   waste a chunk of memory for each port */
+	INIT_WORK(&tty->hangup_work, release_one_tty);
+	schedule_work(&tty->hangup_work);
+}
+
 /**
  *	tty_kref_put		-	release a tty kref
  *	@tty: tty device
@@ -1418,7 +1431,7 @@ static void release_one_tty(struct kref *kref)
 void tty_kref_put(struct tty_struct *tty)
 {
 	if (tty)
-		kref_put(&tty->kref, release_one_tty);
+		kref_put(&tty->kref, queue_release_one_tty);
 }
 EXPORT_SYMBOL(tty_kref_put);