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 |
