aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Cox <alan@linux.intel.com>2009-09-19 13:13:22 -0700
committerLive-CD User <linux@linux.site>2009-09-19 13:13:22 -0700
commitb50989dc444599c8b21edc23536fc305f4e9b7d5 (patch)
tree9a340199b17a680ea41ac65bf5b8ff0c5d84cb14
parente936ffd5cb2b4c7ee04925c9b92b616c01f1e022 (diff)
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>
-rw-r--r--drivers/char/tty_io.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 93844806773..385cca7074d 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);