Skip to content

Commit

Permalink
ibmvnic: Allow device probe if the device is not ready at boot
Browse files Browse the repository at this point in the history
Allow the device to be initialized at a later time if
it is not available at boot. The device will be allowed to probe but
will be given a "down" state. After completing device probe and
registering the net device, the driver will await an interrupt signal
from its partner device, indicating that it is ready for boot. The
driver will schedule a work event to perform the necessary procedure
and begin operation.

Co-developed-by: Thomas Falcon <tlfalcon@linux.ibm.com>
Signed-off-by: Thomas Falcon <tlfalcon@linux.ibm.com>
Signed-off-by: Cristobal Forno <cforno12@linux.ibm.com>
Acked-by: Lijun Pan <lijunp213@gmail.com>
Reviewed-by: Dany Madden <drt@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Cristobal Forno authored and David S. Miller committed Jun 10, 2021
1 parent 1b6c215 commit 53f8b1b
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 27 deletions.
153 changes: 128 additions & 25 deletions drivers/net/ethernet/ibm/ibmvnic.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,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)
Expand Down Expand Up @@ -2083,10 +2106,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;
}
Expand Down Expand Up @@ -2212,10 +2235,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);
Expand Down Expand Up @@ -2268,6 +2291,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;
Expand Down Expand Up @@ -2304,7 +2397,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.
Expand Down Expand Up @@ -3773,18 +3872,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;
Expand Down Expand Up @@ -4904,7 +4991,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
Expand Down Expand Up @@ -5327,6 +5419,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",
Expand Down Expand Up @@ -5373,6 +5466,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) {
Expand All @@ -5382,10 +5476,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;
Expand All @@ -5394,10 +5494,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;
Expand All @@ -5410,7 +5506,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;
Expand Down
6 changes: 4 additions & 2 deletions drivers/net/ethernet/ibm/ibmvnic.h
Original file line number Diff line number Diff line change
Expand Up @@ -851,14 +851,16 @@ enum vnic_state {VNIC_PROBING = 1,
VNIC_CLOSING,
VNIC_CLOSED,
VNIC_REMOVING,
VNIC_REMOVED};
VNIC_REMOVED,
VNIC_DOWN};

enum ibmvnic_reset_reason {VNIC_RESET_FAILOVER = 1,
VNIC_RESET_MOBILITY,
VNIC_RESET_FATAL,
VNIC_RESET_NON_FATAL,
VNIC_RESET_TIMEOUT,
VNIC_RESET_CHANGE_PARAM};
VNIC_RESET_CHANGE_PARAM,
VNIC_RESET_PASSIVE_INIT};

struct ibmvnic_rwi {
enum ibmvnic_reset_reason reset_reason;
Expand Down

0 comments on commit 53f8b1b

Please sign in to comment.