]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
sparc64: fix intermittent LDom hang waiting for vdc_port_up
authorThomas Tai <thomas.tai@oracle.com>
Thu, 6 Apr 2017 17:29:03 +0000 (10:29 -0700)
committerAllen Pais <allen.pais@oracle.com>
Mon, 15 May 2017 10:56:10 +0000 (16:26 +0530)
When LDom boots, sunvdc probes the disk using the LDC channel.
If the channel was previously configured, we need to wait until
we have received a packet from the other end to ensure the
the LDC reset was fully completed; otherwise, the primary domain
will send out NACK when vdc tries to connect. The NACK causes
ldc_reset and flushes the receive queue which may contain the
version info. The unexpected loss of version info causes
the vdc handshake to wait forever.

This patch is to workaround the ldc_reset not handling
reconnection after receiving a NACK. This patch can be
reverted after ldc_reset is properly implemented.

orabug: 25814698

Signed-off-by: Thomas Tai <thomas.tai@oracle.com>
Reviewed-by: Tom Saeger <tom.saeger@oracle.com>
Reviewed-by: Shannon Nelson <shannon.nelson@oracle.com>
Reviewed-by: Babu Moger <babu.moger@oracle.com>
Reviewed-by: Alexandre Chartre <alexandre.chartre@oracle.com>
Signed-off-by: Allen Pais <allen.pais@oracle.com>
arch/sparc/kernel/ldc.c

index da6a02d176c372e410799d4fde3901c11137b1fc..87be2d13532cf714a5aee84ff4f53602e2bad6bd 100644 (file)
@@ -1359,7 +1359,10 @@ EXPORT_SYMBOL(ldc_free);
 int ldc_bind(struct ldc_channel *lp)
 {
        unsigned long hv_err, flags;
+       unsigned long head, tail, chan_state;
+       bool wait_for_channel_reset = false;
        int err = -EINVAL;
+       int limit = 1000;
 
        if (lp->state != LDC_STATE_INIT)
                return -EINVAL;
@@ -1372,6 +1375,19 @@ int ldc_bind(struct ldc_channel *lp)
        lp->flags |= LDC_FLAG_REGISTERED_IRQS;
 
        err = -ENODEV;
+
+       /* sun4v_ldc_rx_get_state() will return 0 if this ldc channel
+        * was previously configured. If so, after setting the new
+        * configuration, we need to wait for the channel to reset
+        * so that the seqid is reset to 0.
+        */
+       hv_err = sun4v_ldc_rx_get_state(lp->id,
+                                       &head,
+                                       &tail,
+                                       &chan_state);
+       if (hv_err == 0)
+               wait_for_channel_reset = true;
+
        hv_err = sun4v_ldc_tx_qconf(lp->id, 0, 0);
        if (hv_err)
                goto out_free_irqs;
@@ -1410,6 +1426,31 @@ int ldc_bind(struct ldc_channel *lp)
        ldc_rx_reset(lp);
        lp->rx_head = lp->rx_tail;
 
+       /* If the channel was previously configured, wait until we
+        * have received a packet from the other end to ensure the
+        * the LDC reset was fully completed.
+        *
+        * Following loop is necessary because ldc_reset does not
+        * handle reconnection if a NACK is received.
+        */
+       do {
+               hv_err = sun4v_ldc_rx_get_state(lp->id,
+                                       &lp->rx_head,
+                                       &lp->rx_tail,
+                                       &lp->chan_state);
+               if (hv_err)
+                       goto out_unmap_rx;
+
+               if (!wait_for_channel_reset)
+                       break;
+               if (lp->rx_head != lp->rx_tail)
+                       break;
+               udelay(10);
+
+       } while (limit-- > 0);
+       if (limit < 0)
+               pr_warn("ldc: channel %lu failed to reset\n", lp->id);
+
        lp->hs_state = LDC_HS_OPEN;
        ldc_set_state(lp, LDC_STATE_BOUND);