Merge tag 'tty-4.11-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty fix from Greg KH:
"Here is a single tty core revert for a patch that was reported to
cause problems.

The original issue is one that we have lived with for decades, so
trying to scramble to fix the fix in time for 4.11-final does not make
sense due to the fragility of the tty ldisc layer. Just reverting it
makes sense for now"

* tag 'tty-4.11-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
Revert "tty: don't panic on OOM in tty_set_ldisc()"

Changed files
+69 -16
drivers
+69 -16
drivers/tty/tty_ldisc.c
··· 492 492 } 493 493 494 494 /** 495 + * tty_ldisc_restore - helper for tty ldisc change 496 + * @tty: tty to recover 497 + * @old: previous ldisc 498 + * 499 + * Restore the previous line discipline or N_TTY when a line discipline 500 + * change fails due to an open error 501 + */ 502 + 503 + static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) 504 + { 505 + struct tty_ldisc *new_ldisc; 506 + int r; 507 + 508 + /* There is an outstanding reference here so this is safe */ 509 + old = tty_ldisc_get(tty, old->ops->num); 510 + WARN_ON(IS_ERR(old)); 511 + tty->ldisc = old; 512 + tty_set_termios_ldisc(tty, old->ops->num); 513 + if (tty_ldisc_open(tty, old) < 0) { 514 + tty_ldisc_put(old); 515 + /* This driver is always present */ 516 + new_ldisc = tty_ldisc_get(tty, N_TTY); 517 + if (IS_ERR(new_ldisc)) 518 + panic("n_tty: get"); 519 + tty->ldisc = new_ldisc; 520 + tty_set_termios_ldisc(tty, N_TTY); 521 + r = tty_ldisc_open(tty, new_ldisc); 522 + if (r < 0) 523 + panic("Couldn't open N_TTY ldisc for " 524 + "%s --- error %d.", 525 + tty_name(tty), r); 526 + } 527 + } 528 + 529 + /** 495 530 * tty_set_ldisc - set line discipline 496 531 * @tty: the terminal to set 497 532 * @ldisc: the line discipline ··· 539 504 540 505 int tty_set_ldisc(struct tty_struct *tty, int disc) 541 506 { 542 - int retval, old_disc; 507 + int retval; 508 + struct tty_ldisc *old_ldisc, *new_ldisc; 509 + 510 + new_ldisc = tty_ldisc_get(tty, disc); 511 + if (IS_ERR(new_ldisc)) 512 + return PTR_ERR(new_ldisc); 543 513 544 514 tty_lock(tty); 545 515 retval = tty_ldisc_lock(tty, 5 * HZ); ··· 557 517 } 558 518 559 519 /* Check the no-op case */ 560 - old_disc = tty->ldisc->ops->num; 561 - if (old_disc == disc) 520 + if (tty->ldisc->ops->num == disc) 562 521 goto out; 563 522 564 523 if (test_bit(TTY_HUPPED, &tty->flags)) { ··· 566 527 goto out; 567 528 } 568 529 569 - retval = tty_ldisc_reinit(tty, disc); 530 + old_ldisc = tty->ldisc; 531 + 532 + /* Shutdown the old discipline. */ 533 + tty_ldisc_close(tty, old_ldisc); 534 + 535 + /* Now set up the new line discipline. */ 536 + tty->ldisc = new_ldisc; 537 + tty_set_termios_ldisc(tty, disc); 538 + 539 + retval = tty_ldisc_open(tty, new_ldisc); 570 540 if (retval < 0) { 571 541 /* Back to the old one or N_TTY if we can't */ 572 - if (tty_ldisc_reinit(tty, old_disc) < 0) { 573 - pr_err("tty: TIOCSETD failed, reinitializing N_TTY\n"); 574 - if (tty_ldisc_reinit(tty, N_TTY) < 0) { 575 - /* At this point we have tty->ldisc == NULL. */ 576 - pr_err("tty: reinitializing N_TTY failed\n"); 577 - } 578 - } 542 + tty_ldisc_put(new_ldisc); 543 + tty_ldisc_restore(tty, old_ldisc); 579 544 } 580 545 581 - if (tty->ldisc && tty->ldisc->ops->num != old_disc && 582 - tty->ops->set_ldisc) { 546 + if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) { 583 547 down_read(&tty->termios_rwsem); 584 548 tty->ops->set_ldisc(tty); 585 549 up_read(&tty->termios_rwsem); 586 550 } 587 551 552 + /* At this point we hold a reference to the new ldisc and a 553 + reference to the old ldisc, or we hold two references to 554 + the old ldisc (if it was restored as part of error cleanup 555 + above). In either case, releasing a single reference from 556 + the old ldisc is correct. */ 557 + new_ldisc = old_ldisc; 588 558 out: 589 559 tty_ldisc_unlock(tty); 590 560 ··· 601 553 already running */ 602 554 tty_buffer_restart_work(tty->port); 603 555 err: 556 + tty_ldisc_put(new_ldisc); /* drop the extra reference */ 604 557 tty_unlock(tty); 605 558 return retval; 606 559 } ··· 662 613 int retval; 663 614 664 615 ld = tty_ldisc_get(tty, disc); 665 - if (IS_ERR(ld)) 616 + if (IS_ERR(ld)) { 617 + BUG_ON(disc == N_TTY); 666 618 return PTR_ERR(ld); 619 + } 667 620 668 621 if (tty->ldisc) { 669 622 tty_ldisc_close(tty, tty->ldisc); ··· 677 626 tty_set_termios_ldisc(tty, disc); 678 627 retval = tty_ldisc_open(tty, tty->ldisc); 679 628 if (retval) { 680 - tty_ldisc_put(tty->ldisc); 681 - tty->ldisc = NULL; 629 + if (!WARN_ON(disc == N_TTY)) { 630 + tty_ldisc_put(tty->ldisc); 631 + tty->ldisc = NULL; 632 + } 682 633 } 683 634 return retval; 684 635 }