aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/tty_ldisc.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-08-03 14:11:19 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-08-04 16:46:30 -0400
commit65b770468e98941e45e19780dff9283e663e6b8b (patch)
treed6984bb568587433cc7f6d7c3bf65287bbad28ef /drivers/char/tty_ldisc.c
parent18eac1cc100fa2afd5f39085aae6b694e417734b (diff)
tty-ldisc: turn ldisc user count into a proper refcount
By using the user count for the actual lifetime rules, we can get rid of the silly "wait_for_idle" logic, because any busy ldisc will automatically stay around until the last user releases it. This avoids a host of odd issues, and simplifies the code. So now, when the last ldisc reference is dropped, we just release the ldisc operations struct reference, and free the ldisc. It looks obvious enough, and it does work for me, but the counting _could_ be off. It probably isn't (bad counting in the new version would generally imply that the old code did something really bad, like free an ldisc with a non-zero count), but it does need some testing, and preferably somebody looking at it. With this change, both 'tty_ldisc_put()' and 'tty_ldisc_deref()' are just aliases for the new ref-counting 'put_ldisc()'. Both of them decrement the ldisc user count and free it if it goes down to zero. They're identical functions, in other words. But the reason they still exist as sepate functions is that one of them was exported (tty_ldisc_deref) and had a stupid name (so I don't want to use it as the main name), and the other one was used in multiple places (and I didn't want to make the patch larger just to rename the users). In addition to the refcounting, I did do some minimal cleanup. For example, now "tty_ldisc_try()" actually returns the ldisc it got under the lock, rather than returning true/false and then the caller would look up the ldisc again (now without the protection of the lock). That said, there's tons of dubious use of 'tty->ldisc' without obviously proper locking or refcounting left. I expressly did _not_ want to try to fix it all, keeping the patch minimal. There may or may not be bugs in that kind of code, but they wouldn't be _new_ bugs. That said, even if the bugs aren't new, the timing and lifetime will change. For example, some silly code may depend on the 'tty->ldisc' pointer not changing because they hold a refcount on the 'ldisc'. And that's no longer true - if you hold a ref on the ldisc, the 'ldisc' itself is safe, but tty->ldisc may change. So the proper locking (remains) to hold tty->ldisc_mutex if you expect tty->ldisc to be stable. That's not really a _new_ rule, but it's an example of something that the old code might have unintentionally depended on and hidden bugs. Whatever. The patch _looks_ sensible to me. The only users of ldisc->users are: - get_ldisc() - atomically increment the count - put_ldisc() - atomically decrements the count and releases if zero - tty_ldisc_try_get() - creates the ldisc, and sets the count to 1. The ldisc should then either be released, or be attached to a tty. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Tested-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Tested-by: Sergey Senozhatsky <sergey.senozhatsky@mail.by> Acked-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/char/tty_ldisc.c')
-rw-r--r--drivers/char/tty_ldisc.c143
1 files changed, 46 insertions, 97 deletions
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index fd175e60aad5..be55dfcf59ac 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -48,6 +48,34 @@ static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
48/* Line disc dispatch table */ 48/* Line disc dispatch table */
49static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; 49static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
50 50
51static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld)
52{
53 if (ld)
54 atomic_inc(&ld->users);
55 return ld;
56}
57
58static inline void put_ldisc(struct tty_ldisc *ld)
59{
60 if (WARN_ON_ONCE(!ld))
61 return;
62
63 /*
64 * If this is the last user, free the ldisc, and
65 * release the ldisc ops.
66 */
67 if (atomic_dec_and_test(&ld->users)) {
68 unsigned long flags;
69 struct tty_ldisc_ops *ldo = ld->ops;
70
71 kfree(ld);
72 spin_lock_irqsave(&tty_ldisc_lock, flags);
73 ldo->refcount--;
74 module_put(ldo->owner);
75 spin_unlock_irqrestore(&tty_ldisc_lock, flags);
76 }
77}
78
51/** 79/**
52 * tty_register_ldisc - install a line discipline 80 * tty_register_ldisc - install a line discipline
53 * @disc: ldisc number 81 * @disc: ldisc number
@@ -142,7 +170,7 @@ static struct tty_ldisc *tty_ldisc_try_get(int disc)
142 /* lock it */ 170 /* lock it */
143 ldops->refcount++; 171 ldops->refcount++;
144 ld->ops = ldops; 172 ld->ops = ldops;
145 atomic_set(&ld->users, 0); 173 atomic_set(&ld->users, 1);
146 err = 0; 174 err = 0;
147 } 175 }
148 } 176 }
@@ -181,35 +209,6 @@ static struct tty_ldisc *tty_ldisc_get(int disc)
181 return ld; 209 return ld;
182} 210}
183 211
184/**
185 * tty_ldisc_put - drop ldisc reference
186 * @ld: ldisc
187 *
188 * Drop a reference to a line discipline. Manage refcounts and
189 * module usage counts. Free the ldisc once the recount hits zero.
190 *
191 * Locking:
192 * takes tty_ldisc_lock to guard against ldisc races
193 */
194
195static void tty_ldisc_put(struct tty_ldisc *ld)
196{
197 unsigned long flags;
198 int disc = ld->ops->num;
199 struct tty_ldisc_ops *ldo;
200
201 BUG_ON(disc < N_TTY || disc >= NR_LDISCS);
202
203 spin_lock_irqsave(&tty_ldisc_lock, flags);
204 ldo = tty_ldiscs[disc];
205 BUG_ON(ldo->refcount == 0);
206 ldo->refcount--;
207 module_put(ldo->owner);
208 spin_unlock_irqrestore(&tty_ldisc_lock, flags);
209 WARN_ON(atomic_read(&ld->users));
210 kfree(ld);
211}
212
213static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) 212static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos)
214{ 213{
215 return (*pos < NR_LDISCS) ? pos : NULL; 214 return (*pos < NR_LDISCS) ? pos : NULL;
@@ -234,7 +233,7 @@ static int tty_ldiscs_seq_show(struct seq_file *m, void *v)
234 if (IS_ERR(ld)) 233 if (IS_ERR(ld))
235 return 0; 234 return 0;
236 seq_printf(m, "%-10s %2d\n", ld->ops->name ? ld->ops->name : "???", i); 235 seq_printf(m, "%-10s %2d\n", ld->ops->name ? ld->ops->name : "???", i);
237 tty_ldisc_put(ld); 236 put_ldisc(ld);
238 return 0; 237 return 0;
239} 238}
240 239
@@ -288,20 +287,17 @@ static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
288 * Locking: takes tty_ldisc_lock 287 * Locking: takes tty_ldisc_lock
289 */ 288 */
290 289
291static int tty_ldisc_try(struct tty_struct *tty) 290static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty)
292{ 291{
293 unsigned long flags; 292 unsigned long flags;
294 struct tty_ldisc *ld; 293 struct tty_ldisc *ld;
295 int ret = 0;
296 294
297 spin_lock_irqsave(&tty_ldisc_lock, flags); 295 spin_lock_irqsave(&tty_ldisc_lock, flags);
298 ld = tty->ldisc; 296 ld = NULL;
299 if (test_bit(TTY_LDISC, &tty->flags)) { 297 if (test_bit(TTY_LDISC, &tty->flags))
300 atomic_inc(&ld->users); 298 ld = get_ldisc(tty->ldisc);
301 ret = 1;
302 }
303 spin_unlock_irqrestore(&tty_ldisc_lock, flags); 299 spin_unlock_irqrestore(&tty_ldisc_lock, flags);
304 return ret; 300 return ld;
305} 301}
306 302
307/** 303/**
@@ -322,10 +318,11 @@ static int tty_ldisc_try(struct tty_struct *tty)
322 318
323struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) 319struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
324{ 320{
321 struct tty_ldisc *ld;
322
325 /* wait_event is a macro */ 323 /* wait_event is a macro */
326 wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); 324 wait_event(tty_ldisc_wait, (ld = tty_ldisc_try(tty)) != NULL);
327 WARN_ON(atomic_read(&tty->ldisc->users) == 0); 325 return ld;
328 return tty->ldisc;
329} 326}
330EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); 327EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
331 328
@@ -342,9 +339,7 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
342 339
343struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) 340struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
344{ 341{
345 if (tty_ldisc_try(tty)) 342 return tty_ldisc_try(tty);
346 return tty->ldisc;
347 return NULL;
348} 343}
349EXPORT_SYMBOL_GPL(tty_ldisc_ref); 344EXPORT_SYMBOL_GPL(tty_ldisc_ref);
350 345
@@ -360,19 +355,15 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref);
360 355
361void tty_ldisc_deref(struct tty_ldisc *ld) 356void tty_ldisc_deref(struct tty_ldisc *ld)
362{ 357{
363 unsigned long flags; 358 put_ldisc(ld);
364
365 BUG_ON(ld == NULL);
366
367 spin_lock_irqsave(&tty_ldisc_lock, flags);
368 if (atomic_read(&ld->users) == 0)
369 printk(KERN_ERR "tty_ldisc_deref: no references.\n");
370 else if (atomic_dec_and_test(&ld->users))
371 wake_up(&tty_ldisc_wait);
372 spin_unlock_irqrestore(&tty_ldisc_lock, flags);
373} 359}
374EXPORT_SYMBOL_GPL(tty_ldisc_deref); 360EXPORT_SYMBOL_GPL(tty_ldisc_deref);
375 361
362static inline void tty_ldisc_put(struct tty_ldisc *ld)
363{
364 put_ldisc(ld);
365}
366
376/** 367/**
377 * tty_ldisc_enable - allow ldisc use 368 * tty_ldisc_enable - allow ldisc use
378 * @tty: terminal to activate ldisc on 369 * @tty: terminal to activate ldisc on
@@ -521,31 +512,6 @@ static int tty_ldisc_halt(struct tty_struct *tty)
521} 512}
522 513
523/** 514/**
524 * tty_ldisc_wait_idle - wait for the ldisc to become idle
525 * @tty: tty to wait for
526 *
527 * Wait for the line discipline to become idle. The discipline must
528 * have been halted for this to guarantee it remains idle.
529 *
530 * tty_ldisc_lock protects the ref counts currently.
531 */
532
533static int tty_ldisc_wait_idle(struct tty_struct *tty)
534{
535 unsigned long flags;
536 spin_lock_irqsave(&tty_ldisc_lock, flags);
537 while (atomic_read(&tty->ldisc->users)) {
538 spin_unlock_irqrestore(&tty_ldisc_lock, flags);
539 if (wait_event_timeout(tty_ldisc_wait,
540 atomic_read(&tty->ldisc->users) == 0, 5 * HZ) == 0)
541 return -EBUSY;
542 spin_lock_irqsave(&tty_ldisc_lock, flags);
543 }
544 spin_unlock_irqrestore(&tty_ldisc_lock, flags);
545 return 0;
546}
547
548/**
549 * tty_set_ldisc - set line discipline 515 * tty_set_ldisc - set line discipline
550 * @tty: the terminal to set 516 * @tty: the terminal to set
551 * @ldisc: the line discipline 517 * @ldisc: the line discipline
@@ -640,14 +606,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
640 606
641 flush_scheduled_work(); 607 flush_scheduled_work();
642 608
643 /* Let any existing reference holders finish */
644 retval = tty_ldisc_wait_idle(tty);
645 if (retval < 0) {
646 clear_bit(TTY_LDISC_CHANGING, &tty->flags);
647 tty_ldisc_put(new_ldisc);
648 return retval;
649 }
650
651 mutex_lock(&tty->ldisc_mutex); 609 mutex_lock(&tty->ldisc_mutex);
652 if (test_bit(TTY_HUPPED, &tty->flags)) { 610 if (test_bit(TTY_HUPPED, &tty->flags)) {
653 /* We were raced by the hangup method. It will have stomped 611 /* We were raced by the hangup method. It will have stomped
@@ -793,7 +751,6 @@ void tty_ldisc_hangup(struct tty_struct *tty)
793 if (tty->ldisc) { /* Not yet closed */ 751 if (tty->ldisc) { /* Not yet closed */
794 /* Switch back to N_TTY */ 752 /* Switch back to N_TTY */
795 tty_ldisc_halt(tty); 753 tty_ldisc_halt(tty);
796 tty_ldisc_wait_idle(tty);
797 tty_ldisc_reinit(tty); 754 tty_ldisc_reinit(tty);
798 /* At this point we have a closed ldisc and we want to 755 /* At this point we have a closed ldisc and we want to
799 reopen it. We could defer this to the next open but 756 reopen it. We could defer this to the next open but
@@ -858,14 +815,6 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
858 tty_ldisc_halt(tty); 815 tty_ldisc_halt(tty);
859 flush_scheduled_work(); 816 flush_scheduled_work();
860 817
861 /*
862 * Wait for any short term users (we know they are just driver
863 * side waiters as the file is closing so user count on the file
864 * side is zero.
865 */
866
867 tty_ldisc_wait_idle(tty);
868
869 mutex_lock(&tty->ldisc_mutex); 818 mutex_lock(&tty->ldisc_mutex);
870 /* 819 /*
871 * Now kill off the ldisc 820 * Now kill off the ldisc