diff options
| -rw-r--r-- | drivers/char/tty_ldisc.c | 152 | ||||
| -rw-r--r-- | include/linux/tty_ldisc.h | 2 |
2 files changed, 54 insertions, 100 deletions
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index acd76b767d4c..1733d3439ad2 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c | |||
| @@ -48,6 +48,41 @@ 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 void put_ldisc(struct tty_ldisc *ld) | ||
| 59 | { | ||
| 60 | unsigned long flags; | ||
| 61 | |||
| 62 | if (WARN_ON_ONCE(!ld)) | ||
| 63 | return; | ||
| 64 | |||
| 65 | /* | ||
| 66 | * If this is the last user, free the ldisc, and | ||
| 67 | * release the ldisc ops. | ||
| 68 | * | ||
| 69 | * We really want an "atomic_dec_and_lock_irqsave()", | ||
| 70 | * but we don't have it, so this does it by hand. | ||
| 71 | */ | ||
| 72 | local_irq_save(flags); | ||
| 73 | if (atomic_dec_and_lock(&ld->users, &tty_ldisc_lock)) { | ||
| 74 | struct tty_ldisc_ops *ldo = ld->ops; | ||
| 75 | |||
| 76 | ldo->refcount--; | ||
| 77 | module_put(ldo->owner); | ||
| 78 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
| 79 | |||
| 80 | kfree(ld); | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | local_irq_restore(flags); | ||
| 84 | } | ||
| 85 | |||
| 51 | /** | 86 | /** |
| 52 | * tty_register_ldisc - install a line discipline | 87 | * tty_register_ldisc - install a line discipline |
| 53 | * @disc: ldisc number | 88 | * @disc: ldisc number |
| @@ -142,7 +177,7 @@ static struct tty_ldisc *tty_ldisc_try_get(int disc) | |||
| 142 | /* lock it */ | 177 | /* lock it */ |
| 143 | ldops->refcount++; | 178 | ldops->refcount++; |
| 144 | ld->ops = ldops; | 179 | ld->ops = ldops; |
| 145 | ld->refcount = 0; | 180 | atomic_set(&ld->users, 1); |
| 146 | err = 0; | 181 | err = 0; |
| 147 | } | 182 | } |
| 148 | } | 183 | } |
| @@ -181,35 +216,6 @@ static struct tty_ldisc *tty_ldisc_get(int disc) | |||
| 181 | return ld; | 216 | return ld; |
| 182 | } | 217 | } |
| 183 | 218 | ||
| 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(ld->refcount); | ||
| 210 | kfree(ld); | ||
| 211 | } | ||
| 212 | |||
| 213 | static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) | 219 | static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) |
| 214 | { | 220 | { |
| 215 | return (*pos < NR_LDISCS) ? pos : NULL; | 221 | return (*pos < NR_LDISCS) ? pos : NULL; |
| @@ -234,7 +240,7 @@ static int tty_ldiscs_seq_show(struct seq_file *m, void *v) | |||
| 234 | if (IS_ERR(ld)) | 240 | if (IS_ERR(ld)) |
| 235 | return 0; | 241 | return 0; |
| 236 | seq_printf(m, "%-10s %2d\n", ld->ops->name ? ld->ops->name : "???", i); | 242 | seq_printf(m, "%-10s %2d\n", ld->ops->name ? ld->ops->name : "???", i); |
| 237 | tty_ldisc_put(ld); | 243 | put_ldisc(ld); |
| 238 | return 0; | 244 | return 0; |
| 239 | } | 245 | } |
| 240 | 246 | ||
| @@ -288,20 +294,17 @@ static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) | |||
| 288 | * Locking: takes tty_ldisc_lock | 294 | * Locking: takes tty_ldisc_lock |
| 289 | */ | 295 | */ |
| 290 | 296 | ||
| 291 | static int tty_ldisc_try(struct tty_struct *tty) | 297 | static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) |
| 292 | { | 298 | { |
| 293 | unsigned long flags; | 299 | unsigned long flags; |
| 294 | struct tty_ldisc *ld; | 300 | struct tty_ldisc *ld; |
| 295 | int ret = 0; | ||
| 296 | 301 | ||
| 297 | spin_lock_irqsave(&tty_ldisc_lock, flags); | 302 | spin_lock_irqsave(&tty_ldisc_lock, flags); |
| 298 | ld = tty->ldisc; | 303 | ld = NULL; |
| 299 | if (test_bit(TTY_LDISC, &tty->flags)) { | 304 | if (test_bit(TTY_LDISC, &tty->flags)) |
| 300 | ld->refcount++; | 305 | ld = get_ldisc(tty->ldisc); |
| 301 | ret = 1; | ||
| 302 | } | ||
| 303 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | 306 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); |
| 304 | return ret; | 307 | return ld; |
| 305 | } | 308 | } |
| 306 | 309 | ||
| 307 | /** | 310 | /** |
| @@ -322,10 +325,11 @@ static int tty_ldisc_try(struct tty_struct *tty) | |||
| 322 | 325 | ||
| 323 | struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) | 326 | struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) |
| 324 | { | 327 | { |
| 328 | struct tty_ldisc *ld; | ||
| 329 | |||
| 325 | /* wait_event is a macro */ | 330 | /* wait_event is a macro */ |
| 326 | wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); | 331 | wait_event(tty_ldisc_wait, (ld = tty_ldisc_try(tty)) != NULL); |
| 327 | WARN_ON(tty->ldisc->refcount == 0); | 332 | return ld; |
| 328 | return tty->ldisc; | ||
| 329 | } | 333 | } |
| 330 | EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); | 334 | EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); |
| 331 | 335 | ||
| @@ -342,9 +346,7 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); | |||
| 342 | 346 | ||
| 343 | struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) | 347 | struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) |
| 344 | { | 348 | { |
| 345 | if (tty_ldisc_try(tty)) | 349 | return tty_ldisc_try(tty); |
| 346 | return tty->ldisc; | ||
| 347 | return NULL; | ||
| 348 | } | 350 | } |
| 349 | EXPORT_SYMBOL_GPL(tty_ldisc_ref); | 351 | EXPORT_SYMBOL_GPL(tty_ldisc_ref); |
| 350 | 352 | ||
| @@ -360,21 +362,15 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref); | |||
| 360 | 362 | ||
| 361 | void tty_ldisc_deref(struct tty_ldisc *ld) | 363 | void tty_ldisc_deref(struct tty_ldisc *ld) |
| 362 | { | 364 | { |
| 363 | unsigned long flags; | 365 | put_ldisc(ld); |
| 364 | |||
| 365 | BUG_ON(ld == NULL); | ||
| 366 | |||
| 367 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
| 368 | if (ld->refcount == 0) | ||
| 369 | printk(KERN_ERR "tty_ldisc_deref: no references.\n"); | ||
| 370 | else | ||
| 371 | ld->refcount--; | ||
| 372 | if (ld->refcount == 0) | ||
| 373 | wake_up(&tty_ldisc_wait); | ||
| 374 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
| 375 | } | 366 | } |
| 376 | EXPORT_SYMBOL_GPL(tty_ldisc_deref); | 367 | EXPORT_SYMBOL_GPL(tty_ldisc_deref); |
| 377 | 368 | ||
| 369 | static inline void tty_ldisc_put(struct tty_ldisc *ld) | ||
| 370 | { | ||
| 371 | put_ldisc(ld); | ||
| 372 | } | ||
| 373 | |||
| 378 | /** | 374 | /** |
| 379 | * tty_ldisc_enable - allow ldisc use | 375 | * tty_ldisc_enable - allow ldisc use |
| 380 | * @tty: terminal to activate ldisc on | 376 | * @tty: terminal to activate ldisc on |
| @@ -523,31 +519,6 @@ static int tty_ldisc_halt(struct tty_struct *tty) | |||
| 523 | } | 519 | } |
| 524 | 520 | ||
| 525 | /** | 521 | /** |
| 526 | * tty_ldisc_wait_idle - wait for the ldisc to become idle | ||
| 527 | * @tty: tty to wait for | ||
| 528 | * | ||
| 529 | * Wait for the line discipline to become idle. The discipline must | ||
| 530 | * have been halted for this to guarantee it remains idle. | ||
| 531 | * | ||
| 532 | * tty_ldisc_lock protects the ref counts currently. | ||
| 533 | */ | ||
| 534 | |||
| 535 | static int tty_ldisc_wait_idle(struct tty_struct *tty) | ||
| 536 | { | ||
| 537 | unsigned long flags; | ||
| 538 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
| 539 | while (tty->ldisc->refcount) { | ||
| 540 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
| 541 | if (wait_event_timeout(tty_ldisc_wait, | ||
| 542 | tty->ldisc->refcount == 0, 5 * HZ) == 0) | ||
| 543 | return -EBUSY; | ||
| 544 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
| 545 | } | ||
| 546 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
| 547 | return 0; | ||
| 548 | } | ||
| 549 | |||
| 550 | /** | ||
| 551 | * tty_set_ldisc - set line discipline | 522 | * tty_set_ldisc - set line discipline |
| 552 | * @tty: the terminal to set | 523 | * @tty: the terminal to set |
| 553 | * @ldisc: the line discipline | 524 | * @ldisc: the line discipline |
| @@ -642,14 +613,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) | |||
| 642 | 613 | ||
| 643 | flush_scheduled_work(); | 614 | flush_scheduled_work(); |
| 644 | 615 | ||
| 645 | /* Let any existing reference holders finish */ | ||
| 646 | retval = tty_ldisc_wait_idle(tty); | ||
| 647 | if (retval < 0) { | ||
| 648 | clear_bit(TTY_LDISC_CHANGING, &tty->flags); | ||
| 649 | tty_ldisc_put(new_ldisc); | ||
| 650 | return retval; | ||
| 651 | } | ||
| 652 | |||
| 653 | mutex_lock(&tty->ldisc_mutex); | 616 | mutex_lock(&tty->ldisc_mutex); |
| 654 | if (test_bit(TTY_HUPPED, &tty->flags)) { | 617 | if (test_bit(TTY_HUPPED, &tty->flags)) { |
| 655 | /* We were raced by the hangup method. It will have stomped | 618 | /* We were raced by the hangup method. It will have stomped |
| @@ -795,7 +758,6 @@ void tty_ldisc_hangup(struct tty_struct *tty) | |||
| 795 | if (tty->ldisc) { /* Not yet closed */ | 758 | if (tty->ldisc) { /* Not yet closed */ |
| 796 | /* Switch back to N_TTY */ | 759 | /* Switch back to N_TTY */ |
| 797 | tty_ldisc_halt(tty); | 760 | tty_ldisc_halt(tty); |
| 798 | tty_ldisc_wait_idle(tty); | ||
| 799 | tty_ldisc_reinit(tty); | 761 | tty_ldisc_reinit(tty); |
| 800 | /* At this point we have a closed ldisc and we want to | 762 | /* At this point we have a closed ldisc and we want to |
| 801 | reopen it. We could defer this to the next open but | 763 | reopen it. We could defer this to the next open but |
| @@ -860,14 +822,6 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) | |||
| 860 | tty_ldisc_halt(tty); | 822 | tty_ldisc_halt(tty); |
| 861 | flush_scheduled_work(); | 823 | flush_scheduled_work(); |
| 862 | 824 | ||
| 863 | /* | ||
| 864 | * Wait for any short term users (we know they are just driver | ||
| 865 | * side waiters as the file is closing so user count on the file | ||
| 866 | * side is zero. | ||
| 867 | */ | ||
| 868 | |||
| 869 | tty_ldisc_wait_idle(tty); | ||
| 870 | |||
| 871 | mutex_lock(&tty->ldisc_mutex); | 825 | mutex_lock(&tty->ldisc_mutex); |
| 872 | /* | 826 | /* |
| 873 | * Now kill off the ldisc | 827 | * Now kill off the ldisc |
diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 40f38d896777..0c4ee9b88f85 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h | |||
| @@ -144,7 +144,7 @@ struct tty_ldisc_ops { | |||
| 144 | 144 | ||
| 145 | struct tty_ldisc { | 145 | struct tty_ldisc { |
| 146 | struct tty_ldisc_ops *ops; | 146 | struct tty_ldisc_ops *ops; |
| 147 | int refcount; | 147 | atomic_t users; |
| 148 | }; | 148 | }; |
| 149 | 149 | ||
| 150 | #define TTY_LDISC_MAGIC 0x5403 | 150 | #define TTY_LDISC_MAGIC 0x5403 |
