Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[platform/kernel/linux-starfive.git] / drivers / net / ethernet / ibm / ibmvnic.c
index 1c57249..374a75d 100644 (file)
@@ -95,7 +95,7 @@ static union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *,
                                        struct ibmvnic_sub_crq_queue *);
 static int ibmvnic_poll(struct napi_struct *napi, int data);
 static void send_query_map(struct ibmvnic_adapter *adapter);
-static int send_request_map(struct ibmvnic_adapter *, dma_addr_t, __be32, u8);
+static int send_request_map(struct ibmvnic_adapter *, dma_addr_t, u32, u8);
 static int send_request_unmap(struct ibmvnic_adapter *, u8);
 static int send_login(struct ibmvnic_adapter *adapter);
 static void send_query_cap(struct ibmvnic_adapter *adapter);
@@ -143,6 +143,29 @@ static const struct ibmvnic_stat ibmvnic_stats[] = {
        {"internal_mac_rx_errors", IBMVNIC_STAT_OFF(internal_mac_rx_errors)},
 };
 
+static int send_crq_init_complete(struct ibmvnic_adapter *adapter)
+{
+       union ibmvnic_crq crq;
+
+       memset(&crq, 0, sizeof(crq));
+       crq.generic.first = IBMVNIC_CRQ_INIT_CMD;
+       crq.generic.cmd = IBMVNIC_CRQ_INIT_COMPLETE;
+
+       return ibmvnic_send_crq(adapter, &crq);
+}
+
+static int send_version_xchg(struct ibmvnic_adapter *adapter)
+{
+       union ibmvnic_crq crq;
+
+       memset(&crq, 0, sizeof(crq));
+       crq.version_exchange.first = IBMVNIC_CRQ_CMD;
+       crq.version_exchange.cmd = VERSION_EXCHANGE;
+       crq.version_exchange.version = cpu_to_be16(ibmvnic_version);
+
+       return ibmvnic_send_crq(adapter, &crq);
+}
+
 static long h_reg_sub_crq(unsigned long unit_address, unsigned long token,
                          unsigned long length, unsigned long *number,
                          unsigned long *irq)
@@ -893,9 +916,10 @@ static const char *adapter_state_to_string(enum vnic_state state)
                return "REMOVING";
        case VNIC_REMOVED:
                return "REMOVED";
-       default:
-               return "UNKNOWN";
+       case VNIC_DOWN:
+               return "DOWN";
        }
+       return "UNKNOWN";
 }
 
 static int ibmvnic_login(struct net_device *netdev)
@@ -1554,7 +1578,8 @@ static int create_hdr_descs(u8 hdr_field, u8 *hdr_data, int len, int *hdr_len,
 
 /**
  * build_hdr_descs_arr - build a header descriptor array
- * @txbuff: tx buffer
+ * @skb: tx socket buffer
+ * @indir_arr: indirect array
  * @num_entries: number of descriptors to be sent
  * @hdr_field: bit field determining which headers will be sent
  *
@@ -1998,9 +2023,10 @@ static const char *reset_reason_to_string(enum ibmvnic_reset_reason reason)
                return "TIMEOUT";
        case VNIC_RESET_CHANGE_PARAM:
                return "CHANGE_PARAM";
-       default:
-               return "UNKNOWN";
+       case VNIC_RESET_PASSIVE_INIT:
+               return "PASSIVE_INIT";
        }
+       return "UNKNOWN";
 }
 
 /*
@@ -2137,10 +2163,10 @@ static int do_reset(struct ibmvnic_adapter *adapter,
                        goto out;
                }
 
-               /* If the adapter was in PROBE state prior to the reset,
+               /* If the adapter was in PROBE or DOWN state prior to the reset,
                 * exit here.
                 */
-               if (reset_state == VNIC_PROBED) {
+               if (reset_state == VNIC_PROBED || reset_state == VNIC_DOWN) {
                        rc = 0;
                        goto out;
                }
@@ -2266,10 +2292,10 @@ static int do_hard_reset(struct ibmvnic_adapter *adapter,
        if (rc)
                goto out;
 
-       /* If the adapter was in PROBE state prior to the reset,
+       /* If the adapter was in PROBE or DOWN state prior to the reset,
         * exit here.
         */
-       if (reset_state == VNIC_PROBED)
+       if (reset_state == VNIC_PROBED || reset_state == VNIC_DOWN)
                goto out;
 
        rc = ibmvnic_login(netdev);
@@ -2322,6 +2348,76 @@ static struct ibmvnic_rwi *get_next_rwi(struct ibmvnic_adapter *adapter)
        return rwi;
 }
 
+/**
+ * do_passive_init - complete probing when partner device is detected.
+ * @adapter: ibmvnic_adapter struct
+ *
+ * If the ibmvnic device does not have a partner device to communicate with at boot
+ * and that partner device comes online at a later time, this function is called
+ * to complete the initialization process of ibmvnic device.
+ * Caller is expected to hold rtnl_lock().
+ *
+ * Returns non-zero if sub-CRQs are not initialized properly leaving the device
+ * in the down state.
+ * Returns 0 upon success and the device is in PROBED state.
+ */
+
+static int do_passive_init(struct ibmvnic_adapter *adapter)
+{
+       unsigned long timeout = msecs_to_jiffies(30000);
+       struct net_device *netdev = adapter->netdev;
+       struct device *dev = &adapter->vdev->dev;
+       int rc;
+
+       netdev_dbg(netdev, "Partner device found, probing.\n");
+
+       adapter->state = VNIC_PROBING;
+       reinit_completion(&adapter->init_done);
+       adapter->init_done_rc = 0;
+       adapter->crq.active = true;
+
+       rc = send_crq_init_complete(adapter);
+       if (rc)
+               goto out;
+
+       rc = send_version_xchg(adapter);
+       if (rc)
+               netdev_dbg(adapter->netdev, "send_version_xchg failed, rc=%d\n", rc);
+
+       if (!wait_for_completion_timeout(&adapter->init_done, timeout)) {
+               dev_err(dev, "Initialization sequence timed out\n");
+               rc = -ETIMEDOUT;
+               goto out;
+       }
+
+       rc = init_sub_crqs(adapter);
+       if (rc) {
+               dev_err(dev, "Initialization of sub crqs failed, rc=%d\n", rc);
+               goto out;
+       }
+
+       rc = init_sub_crq_irqs(adapter);
+       if (rc) {
+               dev_err(dev, "Failed to initialize sub crq irqs\n, rc=%d", rc);
+               goto init_failed;
+       }
+
+       netdev->mtu = adapter->req_mtu - ETH_HLEN;
+       netdev->min_mtu = adapter->min_mtu - ETH_HLEN;
+       netdev->max_mtu = adapter->max_mtu - ETH_HLEN;
+
+       adapter->state = VNIC_PROBED;
+       netdev_dbg(netdev, "Probed successfully. Waiting for signal from partner device.\n");
+
+       return 0;
+
+init_failed:
+       release_sub_crqs(adapter, 1);
+out:
+       adapter->state = VNIC_DOWN;
+       return rc;
+}
+
 static void __ibmvnic_reset(struct work_struct *work)
 {
        struct ibmvnic_rwi *rwi;
@@ -2358,7 +2454,13 @@ static void __ibmvnic_reset(struct work_struct *work)
                }
                spin_unlock_irqrestore(&adapter->state_lock, flags);
 
-               if (adapter->force_reset_recovery) {
+               if (rwi->reset_reason == VNIC_RESET_PASSIVE_INIT) {
+                       rtnl_lock();
+                       rc = do_passive_init(adapter);
+                       rtnl_unlock();
+                       if (!rc)
+                               netif_carrier_on(adapter->netdev);
+               } else if (adapter->force_reset_recovery) {
                        /* Since we are doing a hard reset now, clear the
                         * failover_pending flag so we don't ignore any
                         * future MOBILITY or other resets.
@@ -2454,8 +2556,7 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter,
                goto err;
        }
 
-       list_for_each(entry, &adapter->rwi_list) {
-               tmp = list_entry(entry, struct ibmvnic_rwi, list);
+       list_for_each_entry(tmp, &adapter->rwi_list, list) {
                if (tmp->reset_reason == reason) {
                        netdev_dbg(netdev, "Skipping matching reset, reason=%s\n",
                                   reset_reason_to_string(reason));
@@ -3829,18 +3930,6 @@ static int ibmvnic_send_crq_init(struct ibmvnic_adapter *adapter)
        return 0;
 }
 
-static int send_version_xchg(struct ibmvnic_adapter *adapter)
-{
-       union ibmvnic_crq crq;
-
-       memset(&crq, 0, sizeof(crq));
-       crq.version_exchange.first = IBMVNIC_CRQ_CMD;
-       crq.version_exchange.cmd = VERSION_EXCHANGE;
-       crq.version_exchange.version = cpu_to_be16(ibmvnic_version);
-
-       return ibmvnic_send_crq(adapter, &crq);
-}
-
 struct vnic_login_client_data {
        u8      type;
        __be16  len;
@@ -3873,21 +3962,21 @@ static void vnic_add_client_data(struct ibmvnic_adapter *adapter,
        vlcd->type = 1;
        len = strlen(os_name) + 1;
        vlcd->len = cpu_to_be16(len);
-       strncpy(vlcd->name, os_name, len);
+       strscpy(vlcd->name, os_name, len);
        vlcd = (struct vnic_login_client_data *)(vlcd->name + len);
 
        /* Type 2 - LPAR name */
        vlcd->type = 2;
        len = strlen(utsname()->nodename) + 1;
        vlcd->len = cpu_to_be16(len);
-       strncpy(vlcd->name, utsname()->nodename, len);
+       strscpy(vlcd->name, utsname()->nodename, len);
        vlcd = (struct vnic_login_client_data *)(vlcd->name + len);
 
        /* Type 3 - device name */
        vlcd->type = 3;
        len = strlen(adapter->netdev->name) + 1;
        vlcd->len = cpu_to_be16(len);
-       strncpy(vlcd->name, adapter->netdev->name, len);
+       strscpy(vlcd->name, adapter->netdev->name, len);
 }
 
 static int send_login(struct ibmvnic_adapter *adapter)
@@ -4354,7 +4443,7 @@ static void handle_vpd_rsp(union ibmvnic_crq *crq,
 
 complete:
        if (adapter->fw_version[0] == '\0')
-               strncpy((char *)adapter->fw_version, "N/A", 3 * sizeof(char));
+               strscpy((char *)adapter->fw_version, "N/A", sizeof(adapter->fw_version));
        complete(&adapter->fw_done);
 }
 
@@ -4960,7 +5049,12 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq,
                                complete(&adapter->init_done);
                                adapter->init_done_rc = -EIO;
                        }
-                       rc = ibmvnic_reset(adapter, VNIC_RESET_FAILOVER);
+
+                       if (adapter->state == VNIC_DOWN)
+                               rc = ibmvnic_reset(adapter, VNIC_RESET_PASSIVE_INIT);
+                       else
+                               rc = ibmvnic_reset(adapter, VNIC_RESET_FAILOVER);
+
                        if (rc && rc != -EBUSY) {
                                /* We were unable to schedule the failover
                                 * reset either because the adapter was still
@@ -5383,6 +5477,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
        struct ibmvnic_adapter *adapter;
        struct net_device *netdev;
        unsigned char *mac_addr_p;
+       bool init_success;
        int rc;
 
        dev_dbg(&dev->dev, "entering ibmvnic_probe for UA 0x%x\n",
@@ -5429,6 +5524,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
        init_completion(&adapter->stats_done);
        clear_bit(0, &adapter->resetting);
 
+       init_success = false;
        do {
                rc = init_crq_queue(adapter);
                if (rc) {
@@ -5438,10 +5534,16 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
                }
 
                rc = ibmvnic_reset_init(adapter, false);
-               if (rc && rc != EAGAIN)
-                       goto ibmvnic_init_fail;
        } while (rc == EAGAIN);
 
+       /* We are ignoring the error from ibmvnic_reset_init() assuming that the
+        * partner is not ready. CRQ is not active. When the partner becomes
+        * ready, we will do the passive init reset.
+        */
+
+       if (!rc)
+               init_success = true;
+
        rc = init_stats_buffers(adapter);
        if (rc)
                goto ibmvnic_init_fail;
@@ -5450,10 +5552,6 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
        if (rc)
                goto ibmvnic_stats_fail;
 
-       netdev->mtu = adapter->req_mtu - ETH_HLEN;
-       netdev->min_mtu = adapter->min_mtu - ETH_HLEN;
-       netdev->max_mtu = adapter->max_mtu - ETH_HLEN;
-
        rc = device_create_file(&dev->dev, &dev_attr_failover);
        if (rc)
                goto ibmvnic_dev_file_err;
@@ -5466,7 +5564,14 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
        }
        dev_info(&dev->dev, "ibmvnic registered\n");
 
-       adapter->state = VNIC_PROBED;
+       if (init_success) {
+               adapter->state = VNIC_PROBED;
+               netdev->mtu = adapter->req_mtu - ETH_HLEN;
+               netdev->min_mtu = adapter->min_mtu - ETH_HLEN;
+               netdev->max_mtu = adapter->max_mtu - ETH_HLEN;
+       } else {
+               adapter->state = VNIC_DOWN;
+       }
 
        adapter->wait_for_reset = false;
        adapter->last_reset_time = jiffies;