diff options
Diffstat (limited to 'drivers/char/tty_io.c')
| -rw-r--r-- | drivers/char/tty_io.c | 87 |
1 files changed, 59 insertions, 28 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 9d657127f313..e5953f3433f3 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c | |||
| @@ -469,21 +469,19 @@ static void tty_ldisc_enable(struct tty_struct *tty) | |||
| 469 | 469 | ||
| 470 | static int tty_set_ldisc(struct tty_struct *tty, int ldisc) | 470 | static int tty_set_ldisc(struct tty_struct *tty, int ldisc) |
| 471 | { | 471 | { |
| 472 | int retval = 0; | 472 | int retval = 0; |
| 473 | struct tty_ldisc o_ldisc; | 473 | struct tty_ldisc o_ldisc; |
| 474 | char buf[64]; | 474 | char buf[64]; |
| 475 | int work; | 475 | int work; |
| 476 | unsigned long flags; | 476 | unsigned long flags; |
| 477 | struct tty_ldisc *ld; | 477 | struct tty_ldisc *ld; |
| 478 | struct tty_struct *o_tty; | ||
| 478 | 479 | ||
| 479 | if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS)) | 480 | if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS)) |
| 480 | return -EINVAL; | 481 | return -EINVAL; |
| 481 | 482 | ||
| 482 | restart: | 483 | restart: |
| 483 | 484 | ||
| 484 | if (tty->ldisc.num == ldisc) | ||
| 485 | return 0; /* We are already in the desired discipline */ | ||
| 486 | |||
| 487 | ld = tty_ldisc_get(ldisc); | 485 | ld = tty_ldisc_get(ldisc); |
| 488 | /* Eduardo Blanco <ejbs@cs.cs.com.uy> */ | 486 | /* Eduardo Blanco <ejbs@cs.cs.com.uy> */ |
| 489 | /* Cyrus Durgin <cider@speakeasy.org> */ | 487 | /* Cyrus Durgin <cider@speakeasy.org> */ |
| @@ -494,45 +492,74 @@ restart: | |||
| 494 | if (ld == NULL) | 492 | if (ld == NULL) |
| 495 | return -EINVAL; | 493 | return -EINVAL; |
| 496 | 494 | ||
| 497 | o_ldisc = tty->ldisc; | ||
| 498 | |||
| 499 | tty_wait_until_sent(tty, 0); | 495 | tty_wait_until_sent(tty, 0); |
| 500 | 496 | ||
| 497 | if (tty->ldisc.num == ldisc) { | ||
| 498 | tty_ldisc_put(ldisc); | ||
| 499 | return 0; | ||
| 500 | } | ||
| 501 | |||
| 502 | o_ldisc = tty->ldisc; | ||
| 503 | o_tty = tty->link; | ||
| 504 | |||
| 501 | /* | 505 | /* |
| 502 | * Make sure we don't change while someone holds a | 506 | * Make sure we don't change while someone holds a |
| 503 | * reference to the line discipline. The TTY_LDISC bit | 507 | * reference to the line discipline. The TTY_LDISC bit |
| 504 | * prevents anyone taking a reference once it is clear. | 508 | * prevents anyone taking a reference once it is clear. |
| 505 | * We need the lock to avoid racing reference takers. | 509 | * We need the lock to avoid racing reference takers. |
| 506 | */ | 510 | */ |
| 507 | 511 | ||
| 508 | spin_lock_irqsave(&tty_ldisc_lock, flags); | 512 | spin_lock_irqsave(&tty_ldisc_lock, flags); |
| 509 | if(tty->ldisc.refcount) | 513 | if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) { |
| 510 | { | 514 | if(tty->ldisc.refcount) { |
| 511 | /* Free the new ldisc we grabbed. Must drop the lock | 515 | /* Free the new ldisc we grabbed. Must drop the lock |
| 512 | first. */ | 516 | first. */ |
| 517 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
| 518 | tty_ldisc_put(ldisc); | ||
| 519 | /* | ||
| 520 | * There are several reasons we may be busy, including | ||
| 521 | * random momentary I/O traffic. We must therefore | ||
| 522 | * retry. We could distinguish between blocking ops | ||
| 523 | * and retries if we made tty_ldisc_wait() smarter. That | ||
| 524 | * is up for discussion. | ||
| 525 | */ | ||
| 526 | if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0) | ||
| 527 | return -ERESTARTSYS; | ||
| 528 | goto restart; | ||
| 529 | } | ||
| 530 | if(o_tty && o_tty->ldisc.refcount) { | ||
| 531 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
| 532 | tty_ldisc_put(ldisc); | ||
| 533 | if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0) | ||
| 534 | return -ERESTARTSYS; | ||
| 535 | goto restart; | ||
| 536 | } | ||
| 537 | } | ||
| 538 | |||
| 539 | /* if the TTY_LDISC bit is set, then we are racing against another ldisc change */ | ||
| 540 | |||
| 541 | if (!test_bit(TTY_LDISC, &tty->flags)) { | ||
| 513 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | 542 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); |
| 514 | tty_ldisc_put(ldisc); | 543 | tty_ldisc_put(ldisc); |
| 515 | /* | 544 | ld = tty_ldisc_ref_wait(tty); |
| 516 | * There are several reasons we may be busy, including | 545 | tty_ldisc_deref(ld); |
| 517 | * random momentary I/O traffic. We must therefore | ||
| 518 | * retry. We could distinguish between blocking ops | ||
| 519 | * and retries if we made tty_ldisc_wait() smarter. That | ||
| 520 | * is up for discussion. | ||
| 521 | */ | ||
| 522 | if(wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0) | ||
| 523 | return -ERESTARTSYS; | ||
| 524 | goto restart; | 546 | goto restart; |
| 525 | } | 547 | } |
| 526 | clear_bit(TTY_LDISC, &tty->flags); | 548 | |
| 549 | clear_bit(TTY_LDISC, &tty->flags); | ||
| 527 | clear_bit(TTY_DONT_FLIP, &tty->flags); | 550 | clear_bit(TTY_DONT_FLIP, &tty->flags); |
| 551 | if (o_tty) { | ||
| 552 | clear_bit(TTY_LDISC, &o_tty->flags); | ||
| 553 | clear_bit(TTY_DONT_FLIP, &o_tty->flags); | ||
| 554 | } | ||
| 528 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | 555 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); |
| 529 | 556 | ||
| 530 | /* | 557 | /* |
| 531 | * From this point on we know nobody has an ldisc | 558 | * From this point on we know nobody has an ldisc |
| 532 | * usage reference, nor can they obtain one until | 559 | * usage reference, nor can they obtain one until |
| 533 | * we say so later on. | 560 | * we say so later on. |
| 534 | */ | 561 | */ |
| 535 | 562 | ||
| 536 | work = cancel_delayed_work(&tty->flip.work); | 563 | work = cancel_delayed_work(&tty->flip.work); |
| 537 | /* | 564 | /* |
| 538 | * Wait for ->hangup_work and ->flip.work handlers to terminate | 565 | * Wait for ->hangup_work and ->flip.work handlers to terminate |
| @@ -583,10 +610,12 @@ restart: | |||
| 583 | */ | 610 | */ |
| 584 | 611 | ||
| 585 | tty_ldisc_enable(tty); | 612 | tty_ldisc_enable(tty); |
| 613 | if (o_tty) | ||
| 614 | tty_ldisc_enable(o_tty); | ||
| 586 | 615 | ||
| 587 | /* Restart it in case no characters kick it off. Safe if | 616 | /* Restart it in case no characters kick it off. Safe if |
| 588 | already running */ | 617 | already running */ |
| 589 | if(work) | 618 | if (work) |
| 590 | schedule_delayed_work(&tty->flip.work, 1); | 619 | schedule_delayed_work(&tty->flip.work, 1); |
| 591 | return retval; | 620 | return retval; |
| 592 | } | 621 | } |
| @@ -2425,6 +2454,7 @@ static void __do_SAK(void *arg) | |||
| 2425 | int i; | 2454 | int i; |
| 2426 | struct file *filp; | 2455 | struct file *filp; |
| 2427 | struct tty_ldisc *disc; | 2456 | struct tty_ldisc *disc; |
| 2457 | struct fdtable *fdt; | ||
| 2428 | 2458 | ||
| 2429 | if (!tty) | 2459 | if (!tty) |
| 2430 | return; | 2460 | return; |
| @@ -2450,8 +2480,9 @@ static void __do_SAK(void *arg) | |||
| 2450 | } | 2480 | } |
| 2451 | task_lock(p); | 2481 | task_lock(p); |
| 2452 | if (p->files) { | 2482 | if (p->files) { |
| 2453 | spin_lock(&p->files->file_lock); | 2483 | rcu_read_lock(); |
| 2454 | for (i=0; i < p->files->max_fds; i++) { | 2484 | fdt = files_fdtable(p->files); |
| 2485 | for (i=0; i < fdt->max_fds; i++) { | ||
| 2455 | filp = fcheck_files(p->files, i); | 2486 | filp = fcheck_files(p->files, i); |
| 2456 | if (!filp) | 2487 | if (!filp) |
| 2457 | continue; | 2488 | continue; |
| @@ -2464,7 +2495,7 @@ static void __do_SAK(void *arg) | |||
| 2464 | break; | 2495 | break; |
| 2465 | } | 2496 | } |
| 2466 | } | 2497 | } |
| 2467 | spin_unlock(&p->files->file_lock); | 2498 | rcu_read_unlock(); |
| 2468 | } | 2499 | } |
| 2469 | task_unlock(p); | 2500 | task_unlock(p); |
| 2470 | } while_each_task_pid(session, PIDTYPE_SID, p); | 2501 | } while_each_task_pid(session, PIDTYPE_SID, p); |
