diff options
-rw-r--r-- | drivers/char/tty_ldisc.c | 143 |
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 */ |
49 | static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; | 49 | static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; |
50 | 50 | ||
51 | static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld) | ||
52 | { | ||
53 | if (ld) | ||
54 | atomic_inc(&ld->users); | ||
55 | return ld; | ||
56 | } | ||
57 | |||
58 | static 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 | |||
195 | static 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 | |||
213 | static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) | 212 | static 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 | ||
291 | static int tty_ldisc_try(struct tty_struct *tty) | 290 | static 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 | ||
323 | struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) | 319 | struct 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 | } |
330 | EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); | 327 | EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); |
331 | 328 | ||
@@ -342,9 +339,7 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); | |||
342 | 339 | ||
343 | struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) | 340 | struct 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 | } |
349 | EXPORT_SYMBOL_GPL(tty_ldisc_ref); | 344 | EXPORT_SYMBOL_GPL(tty_ldisc_ref); |
350 | 345 | ||
@@ -360,19 +355,15 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref); | |||
360 | 355 | ||
361 | void tty_ldisc_deref(struct tty_ldisc *ld) | 356 | void 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 | } |
374 | EXPORT_SYMBOL_GPL(tty_ldisc_deref); | 360 | EXPORT_SYMBOL_GPL(tty_ldisc_deref); |
375 | 361 | ||
362 | static 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 | |||
533 | static 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 |