diff options
author | David S. Miller <davem@davemloft.net> | 2009-08-12 20:44:53 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-08-12 20:44:53 -0400 |
commit | aa11d958d1a6572eda08214d7c6a735804fe48a5 (patch) | |
tree | d025b05270ad1e010660d17eeadc6ac3c1abbd7d /drivers/char/tty_ldisc.c | |
parent | 07f6642ee9418e962e54cbc07471cfe2e559c568 (diff) | |
parent | 9799218ae36910af50f002a5db1802d576fffb43 (diff) |
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
Conflicts:
arch/microblaze/include/asm/socket.h
Diffstat (limited to 'drivers/char/tty_ldisc.c')
-rw-r--r-- | drivers/char/tty_ldisc.c | 176 |
1 files changed, 67 insertions, 109 deletions
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index 913aa8d3f1c5..1733d3439ad2 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c | |||
@@ -21,7 +21,6 @@ | |||
21 | #include <linux/proc_fs.h> | 21 | #include <linux/proc_fs.h> |
22 | #include <linux/init.h> | 22 | #include <linux/init.h> |
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/smp_lock.h> | ||
25 | #include <linux/device.h> | 24 | #include <linux/device.h> |
26 | #include <linux/wait.h> | 25 | #include <linux/wait.h> |
27 | #include <linux/bitops.h> | 26 | #include <linux/bitops.h> |
@@ -49,6 +48,41 @@ static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); | |||
49 | /* Line disc dispatch table */ | 48 | /* Line disc dispatch table */ |
50 | static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; | 49 | static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; |
51 | 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 | |||
52 | /** | 86 | /** |
53 | * tty_register_ldisc - install a line discipline | 87 | * tty_register_ldisc - install a line discipline |
54 | * @disc: ldisc number | 88 | * @disc: ldisc number |
@@ -143,7 +177,7 @@ static struct tty_ldisc *tty_ldisc_try_get(int disc) | |||
143 | /* lock it */ | 177 | /* lock it */ |
144 | ldops->refcount++; | 178 | ldops->refcount++; |
145 | ld->ops = ldops; | 179 | ld->ops = ldops; |
146 | ld->refcount = 0; | 180 | atomic_set(&ld->users, 1); |
147 | err = 0; | 181 | err = 0; |
148 | } | 182 | } |
149 | } | 183 | } |
@@ -182,35 +216,6 @@ static struct tty_ldisc *tty_ldisc_get(int disc) | |||
182 | return ld; | 216 | return ld; |
183 | } | 217 | } |
184 | 218 | ||
185 | /** | ||
186 | * tty_ldisc_put - drop ldisc reference | ||
187 | * @ld: ldisc | ||
188 | * | ||
189 | * Drop a reference to a line discipline. Manage refcounts and | ||
190 | * module usage counts. Free the ldisc once the recount hits zero. | ||
191 | * | ||
192 | * Locking: | ||
193 | * takes tty_ldisc_lock to guard against ldisc races | ||
194 | */ | ||
195 | |||
196 | static void tty_ldisc_put(struct tty_ldisc *ld) | ||
197 | { | ||
198 | unsigned long flags; | ||
199 | int disc = ld->ops->num; | ||
200 | struct tty_ldisc_ops *ldo; | ||
201 | |||
202 | BUG_ON(disc < N_TTY || disc >= NR_LDISCS); | ||
203 | |||
204 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
205 | ldo = tty_ldiscs[disc]; | ||
206 | BUG_ON(ldo->refcount == 0); | ||
207 | ldo->refcount--; | ||
208 | module_put(ldo->owner); | ||
209 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
210 | WARN_ON(ld->refcount); | ||
211 | kfree(ld); | ||
212 | } | ||
213 | |||
214 | 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) |
215 | { | 220 | { |
216 | return (*pos < NR_LDISCS) ? pos : NULL; | 221 | return (*pos < NR_LDISCS) ? pos : NULL; |
@@ -235,7 +240,7 @@ static int tty_ldiscs_seq_show(struct seq_file *m, void *v) | |||
235 | if (IS_ERR(ld)) | 240 | if (IS_ERR(ld)) |
236 | return 0; | 241 | return 0; |
237 | 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); |
238 | tty_ldisc_put(ld); | 243 | put_ldisc(ld); |
239 | return 0; | 244 | return 0; |
240 | } | 245 | } |
241 | 246 | ||
@@ -289,20 +294,17 @@ static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) | |||
289 | * Locking: takes tty_ldisc_lock | 294 | * Locking: takes tty_ldisc_lock |
290 | */ | 295 | */ |
291 | 296 | ||
292 | static int tty_ldisc_try(struct tty_struct *tty) | 297 | static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) |
293 | { | 298 | { |
294 | unsigned long flags; | 299 | unsigned long flags; |
295 | struct tty_ldisc *ld; | 300 | struct tty_ldisc *ld; |
296 | int ret = 0; | ||
297 | 301 | ||
298 | spin_lock_irqsave(&tty_ldisc_lock, flags); | 302 | spin_lock_irqsave(&tty_ldisc_lock, flags); |
299 | ld = tty->ldisc; | 303 | ld = NULL; |
300 | if (test_bit(TTY_LDISC, &tty->flags)) { | 304 | if (test_bit(TTY_LDISC, &tty->flags)) |
301 | ld->refcount++; | 305 | ld = get_ldisc(tty->ldisc); |
302 | ret = 1; | ||
303 | } | ||
304 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | 306 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); |
305 | return ret; | 307 | return ld; |
306 | } | 308 | } |
307 | 309 | ||
308 | /** | 310 | /** |
@@ -323,10 +325,11 @@ static int tty_ldisc_try(struct tty_struct *tty) | |||
323 | 325 | ||
324 | struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) | 326 | struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) |
325 | { | 327 | { |
328 | struct tty_ldisc *ld; | ||
329 | |||
326 | /* wait_event is a macro */ | 330 | /* wait_event is a macro */ |
327 | wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); | 331 | wait_event(tty_ldisc_wait, (ld = tty_ldisc_try(tty)) != NULL); |
328 | WARN_ON(tty->ldisc->refcount == 0); | 332 | return ld; |
329 | return tty->ldisc; | ||
330 | } | 333 | } |
331 | EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); | 334 | EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); |
332 | 335 | ||
@@ -343,9 +346,7 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); | |||
343 | 346 | ||
344 | struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) | 347 | struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) |
345 | { | 348 | { |
346 | if (tty_ldisc_try(tty)) | 349 | return tty_ldisc_try(tty); |
347 | return tty->ldisc; | ||
348 | return NULL; | ||
349 | } | 350 | } |
350 | EXPORT_SYMBOL_GPL(tty_ldisc_ref); | 351 | EXPORT_SYMBOL_GPL(tty_ldisc_ref); |
351 | 352 | ||
@@ -361,21 +362,15 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref); | |||
361 | 362 | ||
362 | void tty_ldisc_deref(struct tty_ldisc *ld) | 363 | void tty_ldisc_deref(struct tty_ldisc *ld) |
363 | { | 364 | { |
364 | unsigned long flags; | 365 | put_ldisc(ld); |
365 | |||
366 | BUG_ON(ld == NULL); | ||
367 | |||
368 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
369 | if (ld->refcount == 0) | ||
370 | printk(KERN_ERR "tty_ldisc_deref: no references.\n"); | ||
371 | else | ||
372 | ld->refcount--; | ||
373 | if (ld->refcount == 0) | ||
374 | wake_up(&tty_ldisc_wait); | ||
375 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
376 | } | 366 | } |
377 | EXPORT_SYMBOL_GPL(tty_ldisc_deref); | 367 | EXPORT_SYMBOL_GPL(tty_ldisc_deref); |
378 | 368 | ||
369 | static inline void tty_ldisc_put(struct tty_ldisc *ld) | ||
370 | { | ||
371 | put_ldisc(ld); | ||
372 | } | ||
373 | |||
379 | /** | 374 | /** |
380 | * tty_ldisc_enable - allow ldisc use | 375 | * tty_ldisc_enable - allow ldisc use |
381 | * @tty: terminal to activate ldisc on | 376 | * @tty: terminal to activate ldisc on |
@@ -524,31 +519,6 @@ static int tty_ldisc_halt(struct tty_struct *tty) | |||
524 | } | 519 | } |
525 | 520 | ||
526 | /** | 521 | /** |
527 | * tty_ldisc_wait_idle - wait for the ldisc to become idle | ||
528 | * @tty: tty to wait for | ||
529 | * | ||
530 | * Wait for the line discipline to become idle. The discipline must | ||
531 | * have been halted for this to guarantee it remains idle. | ||
532 | * | ||
533 | * tty_ldisc_lock protects the ref counts currently. | ||
534 | */ | ||
535 | |||
536 | static int tty_ldisc_wait_idle(struct tty_struct *tty) | ||
537 | { | ||
538 | unsigned long flags; | ||
539 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
540 | while (tty->ldisc->refcount) { | ||
541 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
542 | if (wait_event_timeout(tty_ldisc_wait, | ||
543 | tty->ldisc->refcount == 0, 5 * HZ) == 0) | ||
544 | return -EBUSY; | ||
545 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
546 | } | ||
547 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | /** | ||
552 | * tty_set_ldisc - set line discipline | 522 | * tty_set_ldisc - set line discipline |
553 | * @tty: the terminal to set | 523 | * @tty: the terminal to set |
554 | * @ldisc: the line discipline | 524 | * @ldisc: the line discipline |
@@ -643,14 +613,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) | |||
643 | 613 | ||
644 | flush_scheduled_work(); | 614 | flush_scheduled_work(); |
645 | 615 | ||
646 | /* Let any existing reference holders finish */ | ||
647 | retval = tty_ldisc_wait_idle(tty); | ||
648 | if (retval < 0) { | ||
649 | clear_bit(TTY_LDISC_CHANGING, &tty->flags); | ||
650 | tty_ldisc_put(new_ldisc); | ||
651 | return retval; | ||
652 | } | ||
653 | |||
654 | mutex_lock(&tty->ldisc_mutex); | 616 | mutex_lock(&tty->ldisc_mutex); |
655 | if (test_bit(TTY_HUPPED, &tty->flags)) { | 617 | if (test_bit(TTY_HUPPED, &tty->flags)) { |
656 | /* We were raced by the hangup method. It will have stomped | 618 | /* We were raced by the hangup method. It will have stomped |
@@ -791,17 +753,19 @@ void tty_ldisc_hangup(struct tty_struct *tty) | |||
791 | * N_TTY. | 753 | * N_TTY. |
792 | */ | 754 | */ |
793 | if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { | 755 | if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { |
794 | /* Avoid racing set_ldisc */ | 756 | /* Avoid racing set_ldisc or tty_ldisc_release */ |
795 | mutex_lock(&tty->ldisc_mutex); | 757 | mutex_lock(&tty->ldisc_mutex); |
796 | /* Switch back to N_TTY */ | 758 | if (tty->ldisc) { /* Not yet closed */ |
797 | tty_ldisc_halt(tty); | 759 | /* Switch back to N_TTY */ |
798 | tty_ldisc_wait_idle(tty); | 760 | tty_ldisc_halt(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 |
802 | it means auditing a lot of other paths so this is a FIXME */ | 764 | it means auditing a lot of other paths so this is |
803 | WARN_ON(tty_ldisc_open(tty, tty->ldisc)); | 765 | a FIXME */ |
804 | tty_ldisc_enable(tty); | 766 | WARN_ON(tty_ldisc_open(tty, tty->ldisc)); |
767 | tty_ldisc_enable(tty); | ||
768 | } | ||
805 | mutex_unlock(&tty->ldisc_mutex); | 769 | mutex_unlock(&tty->ldisc_mutex); |
806 | tty_reset_termios(tty); | 770 | tty_reset_termios(tty); |
807 | } | 771 | } |
@@ -858,14 +822,7 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) | |||
858 | tty_ldisc_halt(tty); | 822 | tty_ldisc_halt(tty); |
859 | flush_scheduled_work(); | 823 | flush_scheduled_work(); |
860 | 824 | ||
861 | /* | 825 | mutex_lock(&tty->ldisc_mutex); |
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 | /* | 826 | /* |
870 | * Now kill off the ldisc | 827 | * Now kill off the ldisc |
871 | */ | 828 | */ |
@@ -876,6 +833,7 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) | |||
876 | 833 | ||
877 | /* Ensure the next open requests the N_TTY ldisc */ | 834 | /* Ensure the next open requests the N_TTY ldisc */ |
878 | tty_set_termios_ldisc(tty, N_TTY); | 835 | tty_set_termios_ldisc(tty, N_TTY); |
836 | mutex_unlock(&tty->ldisc_mutex); | ||
879 | 837 | ||
880 | /* This will need doing differently if we need to lock */ | 838 | /* This will need doing differently if we need to lock */ |
881 | if (o_tty) | 839 | if (o_tty) |