hil/parisc: Disable HIL driver when it gets stuck
authorHelge Deller <deller@gmx.de>
Mon, 19 Oct 2020 14:57:50 +0000 (16:57 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 5 Nov 2020 10:07:05 +0000 (11:07 +0100)
commit 879bc2d27904354b98ca295b6168718e045c4aa2 upstream.

When starting a HP machine with HIL driver but without an HIL keyboard
or HIL mouse attached, it may happen that data written to the HIL loop
gets stuck (e.g. because the transaction queue is full).  Usually one
will then have to reboot the machine because all you see is and endless
output of:
 Transaction add failed: transaction already queued?

In the higher layers hp_sdc_enqueue_transaction() is called to queued up
a HIL packet. This function returns an error code, and this patch adds
the necessary checks for this return code and disables the HIL driver if
further packets can't be sent.

Tested on a HP 730 and a HP 715/64 machine.

Signed-off-by: Helge Deller <deller@gmx.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/input/serio/hil_mlc.c
drivers/input/serio/hp_sdc_mlc.c
include/linux/hil_mlc.h

index 65605e4ef3cf6f7b085816ffeb8b2c11af0f4fc4..1491a9a5c6b06d82046721eaa203e883ff69cc9b 100644 (file)
@@ -74,7 +74,7 @@ EXPORT_SYMBOL(hil_mlc_unregister);
 static LIST_HEAD(hil_mlcs);
 static DEFINE_RWLOCK(hil_mlcs_lock);
 static struct timer_list       hil_mlcs_kicker;
-static int                     hil_mlcs_probe;
+static int                     hil_mlcs_probe, hil_mlc_stop;
 
 static void hil_mlcs_process(unsigned long unused);
 static DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0);
@@ -704,9 +704,13 @@ static int hilse_donode(hil_mlc *mlc)
                if (!mlc->ostarted) {
                        mlc->ostarted = 1;
                        mlc->opacket = pack;
-                       mlc->out(mlc);
+                       rc = mlc->out(mlc);
                        nextidx = HILSEN_DOZE;
                        write_unlock_irqrestore(&mlc->lock, flags);
+                       if (rc) {
+                               hil_mlc_stop = 1;
+                               return 1;
+                       }
                        break;
                }
                mlc->ostarted = 0;
@@ -717,8 +721,13 @@ static int hilse_donode(hil_mlc *mlc)
 
        case HILSE_CTS:
                write_lock_irqsave(&mlc->lock, flags);
-               nextidx = mlc->cts(mlc) ? node->bad : node->good;
+               rc = mlc->cts(mlc);
+               nextidx = rc ? node->bad : node->good;
                write_unlock_irqrestore(&mlc->lock, flags);
+               if (rc) {
+                       hil_mlc_stop = 1;
+                       return 1;
+               }
                break;
 
        default:
@@ -786,6 +795,12 @@ static void hil_mlcs_process(unsigned long unused)
 
 static void hil_mlcs_timer(unsigned long data)
 {
+       if (hil_mlc_stop) {
+               /* could not send packet - stop immediately. */
+               pr_warn(PREFIX "HIL seems stuck - Disabling HIL MLC.\n");
+               return;
+       }
+
        hil_mlcs_probe = 1;
        tasklet_schedule(&hil_mlcs_tasklet);
        /* Re-insert the periodic task. */
index d50f0678bf47a607c09c8346dd73958a14df4322..078cbe6522a2a776bf8db7f6b1f63380529a411e 100644 (file)
@@ -213,7 +213,7 @@ static int hp_sdc_mlc_cts(hil_mlc *mlc)
        priv->tseq[2] = 1;
        priv->tseq[3] = 0;
        priv->tseq[4] = 0;
-       __hp_sdc_enqueue_transaction(&priv->trans);
+       return __hp_sdc_enqueue_transaction(&priv->trans);
  busy:
        return 1;
  done:
@@ -222,7 +222,7 @@ static int hp_sdc_mlc_cts(hil_mlc *mlc)
        return 0;
 }
 
-static void hp_sdc_mlc_out(hil_mlc *mlc)
+static int hp_sdc_mlc_out(hil_mlc *mlc)
 {
        struct hp_sdc_mlc_priv_s *priv;
 
@@ -237,7 +237,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc)
  do_data:
        if (priv->emtestmode) {
                up(&mlc->osem);
-               return;
+               return 0;
        }
        /* Shouldn't be sending commands when loop may be busy */
        BUG_ON(down_trylock(&mlc->csem));
@@ -299,7 +299,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc)
                BUG_ON(down_trylock(&mlc->csem));
        }
  enqueue:
-       hp_sdc_enqueue_transaction(&priv->trans);
+       return hp_sdc_enqueue_transaction(&priv->trans);
 }
 
 static int __init hp_sdc_mlc_init(void)
index 394a8405dd74d96c1c09ec269de52457bd685bb0..e0521a1d9325025941a7257aaae785573b5e1e04 100644 (file)
@@ -103,7 +103,7 @@ struct hilse_node {
 
 /* Methods for back-end drivers, e.g. hp_sdc_mlc */
 typedef int    (hil_mlc_cts) (hil_mlc *mlc);
-typedef void   (hil_mlc_out) (hil_mlc *mlc);
+typedef int    (hil_mlc_out) (hil_mlc *mlc);
 typedef int    (hil_mlc_in)  (hil_mlc *mlc, suseconds_t timeout);
 
 struct hil_mlc_devinfo {