]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/tty/tty_ldisc.c
i2c: Add drivers for the AMD PCIe MP2 I2C controller
[mirror_ubuntu-bionic-kernel.git] / drivers / tty / tty_ldisc.c
index 24ec5c7e6b2091141a7ea113518ba48ea4b6575d..48fe8d9b6df98eeb755eebda6ec813657457d699 100644 (file)
@@ -176,12 +176,11 @@ static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
                        return ERR_CAST(ldops);
        }
 
-       ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);
-       if (ld == NULL) {
-               put_ldops(ldops);
-               return ERR_PTR(-ENOMEM);
-       }
-
+       /*
+        * There is no way to handle allocation failure of only 16 bytes.
+        * Let's simplify error handling and save more memory.
+        */
+       ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL | __GFP_NOFAIL);
        ld->ops = ldops;
        ld->tty = tty;
 
@@ -337,10 +336,15 @@ static inline void __tty_ldisc_unlock(struct tty_struct *tty)
        ldsem_up_write(&tty->ldisc_sem);
 }
 
-static int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
+int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
 {
        int ret;
 
+       /* Kindly asking blocked readers to release the read side */
+       set_bit(TTY_LDISC_CHANGING, &tty->flags);
+       wake_up_interruptible_all(&tty->read_wait);
+       wake_up_interruptible_all(&tty->write_wait);
+
        ret = __tty_ldisc_lock(tty, timeout);
        if (!ret)
                return -EBUSY;
@@ -348,9 +352,11 @@ static int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
        return 0;
 }
 
-static void tty_ldisc_unlock(struct tty_struct *tty)
+void tty_ldisc_unlock(struct tty_struct *tty)
 {
        clear_bit(TTY_LDISC_HALTED, &tty->flags);
+       /* Can be cleared here - ldisc_unlock will wake up writers firstly */
+       clear_bit(TTY_LDISC_CHANGING, &tty->flags);
        __tty_ldisc_unlock(tty);
 }
 
@@ -527,19 +533,16 @@ static int tty_ldisc_failto(struct tty_struct *tty, int ld)
 static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
 {
        /* There is an outstanding reference here so this is safe */
-       old = tty_ldisc_get(tty, old->ops->num);
-       WARN_ON(IS_ERR(old));
-       tty->ldisc = old;
-       tty_set_termios_ldisc(tty, old->ops->num);
-       if (tty_ldisc_open(tty, old) < 0) {
-               tty_ldisc_put(old);
+       if (tty_ldisc_failto(tty, old->ops->num) < 0) {
+               const char *name = tty_name(tty);
+
+               pr_warn("Falling back ldisc for %s.\n", name);
                /* The traditional behaviour is to fall back to N_TTY, we
                   want to avoid falling back to N_NULL unless we have no
                   choice to avoid the risk of breaking anything */
                if (tty_ldisc_failto(tty, N_TTY) < 0 &&
                    tty_ldisc_failto(tty, N_NULL) < 0)
-                       panic("Couldn't open N_NULL ldisc for %s.",
-                             tty_name(tty));
+                       panic("Couldn't open N_NULL ldisc for %s.", name);
        }
 }
 
@@ -824,12 +827,13 @@ EXPORT_SYMBOL_GPL(tty_ldisc_release);
  *     the tty structure is not completely set up when this call is made.
  */
 
-void tty_ldisc_init(struct tty_struct *tty)
+int tty_ldisc_init(struct tty_struct *tty)
 {
        struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
        if (IS_ERR(ld))
-               panic("n_tty: init_tty");
+               return PTR_ERR(ld);
        tty->ldisc = ld;
+       return 0;
 }
 
 /**