// SPDX-License-Identifier: GPL-2.0-or-later /**************************************************************************/ /* */ /* IBM System i and System p Virtual NIC Device Driver */ /* Copyright (C) 2014 IBM Corp. */ /* Santiago Leon (santi_leon@yahoo.com) */ /* Thomas Falcon (tlfalcon@linux.vnet.ibm.com) */ /* John Allen (jallen@linux.vnet.ibm.com) */ /* */ /* */ /* This module contains the implementation of a virtual ethernet device */ /* for use with IBM i/p Series LPAR Linux. It utilizes the logical LAN */ /* option of the RS/6000 Platform Architecture to interface with virtual */ /* ethernet NICs that are presented to the partition by the hypervisor. */ /* */ /* Messages are passed between the VNIC driver and the VNIC server using */ /* Command/Response Queues (CRQs) and sub CRQs (sCRQs). CRQs are used to */ /* issue and receive commands that initiate communication with the server */ /* on driver initialization. Sub CRQs (sCRQs) are similar to CRQs, but */ /* are used by the driver to notify the server that a packet is */ /* ready for transmission or that a buffer has been added to receive a */ /* packet. Subsequently, sCRQs are used by the server to notify the */ /* driver that a packet transmission has been completed or that a packet */ /* has been received and placed in a waiting buffer. */ /* */ /* In lieu of a more conventional "on-the-fly" DMA mapping strategy in */ /* which skbs are DMA mapped and immediately unmapped when the transmit */ /* or receive has been completed, the VNIC driver is required to use */ /* "long term mapping". This entails that large, continuous DMA mapped */ /* buffers are allocated on driver initialization and these buffers are */ /* then continuously reused to pass skbs to and from the VNIC server. */ /* */ /**************************************************************************/
staticconstchar ibmvnic_driver_name[] = "ibmvnic"; staticconstchar ibmvnic_driver_string[] = "IBM System i/p Virtual NIC Driver";
MODULE_AUTHOR("Santiago Leon");
MODULE_DESCRIPTION("IBM System i/p Virtual NIC Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(IBMVNIC_DRIVER_VERSION);
netdev_dbg(adapter->netdev, "%s: Cleaning irq affinity hints", __func__); if (txqs) { for (i = 0; i < num_txqs; i++)
ibmvnic_clean_queue_affinity(adapter, txqs[i]);
} if (rxqs) { for (i = 0; i < num_rxqs; i++)
ibmvnic_clean_queue_affinity(adapter, rxqs[i]);
}
}
staticint ibmvnic_set_queue_affinity(struct ibmvnic_sub_crq_queue *queue, unsignedint *cpu, int *stragglers, int stride)
{
cpumask_var_t mask; int i; int rc = 0;
if (!(queue && queue->irq)) return rc;
/* cpumask_var_t is either a pointer or array, allocation works here */ if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) return -ENOMEM;
/* while we have extra cpu give one extra to this irq */ if (*stragglers) {
stride++;
(*stragglers)--;
} /* atomic write is safer than writing bit by bit directly */
for_each_online_cpu_wrap(i, *cpu) { if (!stride--) { /* For the next queue we start from the first *unusedCPUinthisqueue
*/
*cpu = i; break;
}
cpumask_set_cpu(i, mask);
}
nltbs = tot_size / ltb_size; if (tot_size % ltb_size)
nltbs++;
old_set = *ltb_set;
if (old_set.num_ltbs == nltbs) {
new_set = old_set;
} else { int tmp = nltbs * sizeof(struct ibmvnic_long_term_buff);
new_set.ltbs = kzalloc(tmp, GFP_KERNEL); if (!new_set.ltbs) return -ENOMEM;
new_set.num_ltbs = nltbs;
/* Free any excess ltbs in old set */ for (i = new_set.num_ltbs; i < old_set.num_ltbs; i++)
free_long_term_buff(adapter, &old_set.ltbs[i]);
/* Copy remaining ltbs to new set. All LTBs except the *lastoneareofthesamesize.alloc_long_term_buff() *willreallocifthesizechanges.
*/
n = min(old_set.num_ltbs, new_set.num_ltbs); for (i = 0; i < n; i++)
new_set.ltbs[i] = old_set.ltbs[i];
/* Any additional ltbs in new set will have NULL ltbs for *nowandwillbeallocatedinalloc_long_term_buff().
*/
/* We no longer need the old_set so free it. Note that we *mayhavereusedsomeltbsfromoldsetandfreedexcess *ltbsabove.Soweonlyneedtofreethecontainernow *nottheLTBsthemselves.(i.e.dontfree_ltb_set()!)
*/
kfree(old_set.ltbs);
old_set.ltbs = NULL;
old_set.num_ltbs = 0;
/* Install the new set. If allocations fail below, we will *retrylaterandknowwhatsizeLTBsweneed.
*/
*ltb_set = new_set;
}
i = 0;
rem_size = tot_size; while (rem_size) { if (ltb_size > rem_size)
ltb_size = rem_size;
/* netdev_skb_alloc() could have failed after we saved a few skbs *intheindir_bufandwewouldnothavesentthemtoVIOSyet. *Toaccountforthem,starttheloopatind_bufp->indexrather *than0.IfwepushedalltheskbstoVIOS,ind_bufp->indexwill *be0.
*/ for (i = ind_bufp->index; i < count; ++i) {
bufidx = pool->free_map[pool->next_free];
/* We maybe reusing the skb from earlier resets. Allocate *onlyifnecessary.ButsincetheLTBmayhavechanged *duringreset(seeinit_rx_pools()),updateLTBbelow *evenifreusingskb.
*/
skb = pool->rx_buff[bufidx].skb; if (!skb) {
skb = netdev_alloc_skb(adapter->netdev,
pool->buff_size); if (!skb) {
dev_err(dev, "Couldn't replenish rx buff\n");
adapter->replenish_no_mem++; break;
}
}
/* Copy the skb to the long term mapped DMA buffer */
map_rxpool_buf_to_ltb(pool, bufidx, <b, &offset);
dst = ltb->buff + offset;
memset(dst, 0, pool->buff_size);
dma_addr = ltb->addr + offset;
/* add the skb to an rx_buff in the pool */
pool->rx_buff[bufidx].data = dst;
pool->rx_buff[bufidx].dma = dma_addr;
pool->rx_buff[bufidx].skb = skb;
pool->rx_buff[bufidx].pool_index = pool->index;
pool->rx_buff[bufidx].size = pool->buff_size;
/* queue the rx_buff for the next send_subcrq_indirect */
sub_crq = &ind_bufp->indir_arr[ind_bufp->index++];
memset(sub_crq, 0, sizeof(*sub_crq));
sub_crq->rx_add.first = IBMVNIC_CRQ_CMD;
sub_crq->rx_add.correlator =
cpu_to_be64((u64)&pool->rx_buff[bufidx]);
sub_crq->rx_add.ioba = cpu_to_be32(dma_addr);
sub_crq->rx_add.map_id = ltb->map_id;
/* The length field of the sCRQ is defined to be 24 bits so the *buffersizeneedstobeleftshiftedbyabytebeforeitis *convertedtobigendiantopreventthelastbytefrombeing *truncated.
*/ #ifdef __LITTLE_ENDIAN__
shift = 8; #endif
sub_crq->rx_add.len = cpu_to_be32(pool->buff_size << shift);
/* if send_subcrq_indirect queue is full, flush to VIOS */ if (ind_bufp->index == IBMVNIC_MAX_IND_DESCS ||
i == count - 1) {
lpar_rc =
send_subcrq_indirect(adapter, handle,
(u64)ind_bufp->indir_dma,
(u64)ind_bufp->index); if (lpar_rc != H_SUCCESS) goto failure;
buffers_added += ind_bufp->index;
adapter->replenish_add_buff_success += ind_bufp->index;
ind_bufp->index = 0;
}
}
atomic_add(buffers_added, &pool->available); return;
failure: if (lpar_rc != H_PARAMETER && lpar_rc != H_CLOSED)
dev_err_ratelimited(dev, "rx: replenish packet buffer failed\n"); for (i = ind_bufp->index - 1; i >= 0; --i) { struct ibmvnic_rx_buff *rx_buff;
staticvoid replenish_pools(struct ibmvnic_adapter *adapter)
{ int i;
adapter->replenish_task_cycles++; for (i = 0; i < adapter->num_active_rx_pools; i++) { if (adapter->rx_pool[i].active)
replenish_rx_pool(adapter, &adapter->rx_pool[i]);
}
if (reuse_rx_pools(adapter)) {
dev_dbg(dev, "Reusing rx pools\n"); goto update_ltb;
}
/* Allocate/populate the pools. */
release_rx_pools(adapter);
adapter->rx_pool = kcalloc(num_pools, sizeof(struct ibmvnic_rx_pool),
GFP_KERNEL); if (!adapter->rx_pool) {
dev_err(dev, "Failed to allocate rx pools\n"); return -ENOMEM;
}
/* Set num_active_rx_pools early. If we fail below after partial *allocation,release_rx_pools()willknowhowmanytolookfor.
*/
adapter->num_active_rx_pools = num_pools;
for (i = 0; i < num_pools; i++) {
rx_pool = &adapter->rx_pool[i];
update_ltb: for (i = 0; i < num_pools; i++) {
rx_pool = &adapter->rx_pool[i];
dev_dbg(dev, "Updating LTB for rx pool %d [%d, %d]\n",
i, rx_pool->size, rx_pool->buff_size);
rc = alloc_ltb_set(adapter, &rx_pool->ltb_set,
rx_pool->size, rx_pool->buff_size); if (rc) goto out;
/* Mark pool "empty" so replenish_rx_pools() will *updatetheLTBinfoforeachbuffer
*/
atomic_set(&rx_pool->available, 0);
rx_pool->next_alloc = 0;
rx_pool->next_free = 0; /* replenish_rx_pool() may have called deactivate_rx_pools() *onfailover.Ensurepoolisactivenow.
*/
rx_pool->active = 1;
} return0;
out_release:
release_rx_pools(adapter);
out: /* We failed to allocate one or more LTBs or map them on the VIOS. *HoldontothepoolsandanyLTBsthatwedidallocate/map.
*/ return rc;
}
staticvoid release_vpd_data(struct ibmvnic_adapter *adapter)
{ if (!adapter->vpd) return;
/* init_tx_pools() ensures that ->tx_pool and ->tso_pool are *bothNULLorbothnon-NULL.Soweonlyneedtocheckone.
*/ if (!adapter->tx_pool) return;
for (i = 0; i < adapter->num_active_tx_pools; i++) {
release_one_tx_pool(adapter, &adapter->tx_pool[i]);
release_one_tx_pool(adapter, &adapter->tso_pool[i]);
}
/** *init_tx_pools():Initializethesetoftransmitpoolsintheadapter. *@netdev:netdeviceassociatedwiththevnicinterface * *Initializethesetoftransmitpoolsintheibmvnicadapterassociated *withthenet_device@netdev.Ifpossible,reusetheexistingtxpools. *Otherwisefreeanyexistingpoolsandallocateanewsetofpools *beforeinitializingthem. * *Return:0onsuccessandnegativevalueonerror.
*/ staticint init_tx_pools(struct net_device *netdev)
{ struct ibmvnic_adapter *adapter = netdev_priv(netdev); struct device *dev = &adapter->vdev->dev; int num_pools;
u64 pool_size; /* # of buffers in pool */
u64 buff_size; int i, j, rc;
num_pools = adapter->req_tx_queues;
/* We must notify the VIOS about the LTB on all resets - but we only *needtoalloc/populatepoolsifeitherthenumberofbuffersor *sizeofeachbufferinthepoolhaschanged.
*/ if (reuse_tx_pools(adapter)) {
netdev_dbg(netdev, "Reusing tx pools\n"); goto update_ltb;
}
/* Allocate/populate the pools. */
release_tx_pools(adapter);
adapter->tx_pool = kcalloc(num_pools, sizeof(struct ibmvnic_tx_pool), GFP_KERNEL); if (!adapter->tx_pool) return -ENOMEM;
adapter->tso_pool = kcalloc(num_pools, sizeof(struct ibmvnic_tx_pool), GFP_KERNEL); /* To simplify release_tx_pools() ensure that ->tx_pool and *->tso_poolareeitherbothNULLorbothnon-NULL.
*/ if (!adapter->tso_pool) {
kfree(adapter->tx_pool);
adapter->tx_pool = NULL; return -ENOMEM;
}
/* Set num_active_tx_pools early. If we fail below after partial *allocation,release_tx_pools()willknowhowmanytolookfor.
*/
adapter->num_active_tx_pools = num_pools;
update_ltb: /* NOTE: All tx_pools have the same number of buffers (which is *sameaspool_size).Alltso_poolshaveIBMVNIC_TSO_BUFS *buffers(seecallsinit_one_tx_pool()forthese). *Forconsistency,weusetx_pool->num_buffersand *tso_pool->num_buffersbelow.
*/
rc = -1; for (i = 0; i < num_pools; i++) { struct ibmvnic_tx_pool *tso_pool; struct ibmvnic_tx_pool *tx_pool;
tx_pool = &adapter->tx_pool[i];
dev_dbg(dev, "Updating LTB for tx pool %d [%d, %d]\n",
i, tx_pool->num_buffers, tx_pool->buf_size);
rc = alloc_ltb_set(adapter, &tx_pool->ltb_set,
tx_pool->num_buffers, tx_pool->buf_size); if (rc) goto out;
return0;
out_release:
release_tx_pools(adapter);
out: /* We failed to allocate one or more LTBs or map them on the VIOS. *HoldontothepoolsandanyLTBsthatwedidallocate/map.
*/ return rc;
}
staticvoid ibmvnic_napi_enable(struct ibmvnic_adapter *adapter)
{ int i;
if (adapter->napi_enabled) return;
for (i = 0; i < adapter->req_rx_queues; i++)
napi_enable(&adapter->napi[i]);
adapter->napi_enabled = true;
}
staticvoid ibmvnic_napi_disable(struct ibmvnic_adapter *adapter)
{ int i;
if (!adapter->napi_enabled) return;
for (i = 0; i < adapter->req_rx_queues; i++) {
netdev_dbg(adapter->netdev, "Disabling napi[%d]\n", i);
napi_disable(&adapter->napi[i]);
}
adapter->napi_enabled = false;
}
staticint init_napi(struct ibmvnic_adapter *adapter)
{ int i;
adapter->napi = kcalloc(adapter->req_rx_queues, sizeof(struct napi_struct), GFP_KERNEL); if (!adapter->napi) return -ENOMEM;
for (i = 0; i < adapter->req_rx_queues; i++) {
netdev_dbg(adapter->netdev, "Adding napi[%d]\n", i);
netif_napi_add(adapter->netdev, &adapter->napi[i],
ibmvnic_poll);
}
staticconstchar *adapter_state_to_string(enum vnic_state state)
{ switch (state) { case VNIC_PROBING: return"PROBING"; case VNIC_PROBED: return"PROBED"; case VNIC_OPENING: return"OPENING"; case VNIC_OPEN: return"OPEN"; case VNIC_CLOSING: return"CLOSING"; case VNIC_CLOSED: return"CLOSED"; case VNIC_REMOVING: return"REMOVING"; case VNIC_REMOVED: return"REMOVED"; case VNIC_DOWN: return"DOWN";
} return"UNKNOWN";
}
staticint ibmvnic_login(struct net_device *netdev)
{ unsignedlong flags, timeout = msecs_to_jiffies(20000); struct ibmvnic_adapter *adapter = netdev_priv(netdev); int retry_count = 0; int retries = 10; bool retry; int rc;
do {
retry = false; if (retry_count > retries) {
netdev_warn(netdev, "Login attempts exceeded\n"); return -EACCES;
}
partial_reset: /* adapter login failed, so free any CRQs or sub-CRQs *andregisteragainbeforeattemptingtologinagain. *Ifwedon'tdothisthentheVIOSmaythinkthat *wearealreadyloggedinandrejectanysubsequent *attempts
*/
netdev_warn(netdev, "Freeing and re-registering CRQs before attempting to login again\n");
retry = true;
adapter->init_done_rc = 0;
release_sub_crqs(adapter, true); /* Much of this is similar logic as ibmvnic_probe(), *weareessentiallyre-initializingcommunication *withtheserver.Wereallyshouldnotrunany *resets/failoversherebecausethisisalreadyaform *ofresetandwedonotwantparallelresetsoccurring
*/ do {
reinit_init_done(adapter); /* Clear any failovers we got in the previous *passsincewearere-initializingtheCRQ
*/
adapter->failover_pending = false;
release_crq_queue(adapter); /* If we don't sleep here then we risk an *unnecessaryfailovereventfromtheVIOS. *ThisisaknownVIOSissuecausedbyavnic *devicefreeingandregisteringaCRQtoo *quickly.
*/
msleep(1500); /* Avoid any resets, since we are currently *resetting.
*/
spin_lock_irqsave(&adapter->rwi_lock, flags);
flush_reset_queue(adapter);
spin_unlock_irqrestore(&adapter->rwi_lock,
flags);
netdev_dbg(netdev, "Setting real tx/rx queues (%llx/%llx)\n",
adapter->req_tx_queues, adapter->req_rx_queues);
rc = netif_set_real_num_tx_queues(netdev, adapter->req_tx_queues); if (rc) {
netdev_err(netdev, "failed to set the number of tx queues\n"); return rc;
}
rc = netif_set_real_num_rx_queues(netdev, adapter->req_rx_queues); if (rc)
netdev_err(netdev, "failed to set the number of rx queues\n");
return rc;
}
staticint ibmvnic_get_vpd(struct ibmvnic_adapter *adapter)
{ struct device *dev = &adapter->vdev->dev; union ibmvnic_crq crq; int len = 0; int rc;
/* We're ready to receive frames, enable the sub-crq interrupts and *setthelogicallinkstatetoup
*/ for (i = 0; i < adapter->req_rx_queues; i++) {
netdev_dbg(netdev, "Enabling rx_scrq[%d] irq\n", i); if (prev_state == VNIC_CLOSED)
enable_irq(adapter->rx_scrq[i]->irq);
enable_scrq_irq(adapter, adapter->rx_scrq[i]);
}
for (i = 0; i < adapter->req_tx_queues; i++) {
netdev_dbg(netdev, "Enabling tx_scrq[%d] irq\n", i); if (prev_state == VNIC_CLOSED)
enable_irq(adapter->tx_scrq[i]->irq);
enable_scrq_irq(adapter, adapter->tx_scrq[i]); /* netdev_tx_reset_queue will reset dql stats. During NON_FATAL *resets,don'tresetthestatsbecausetherecouldbebatched *skb'swaitingtobesent.Ifweresetdqlstats,werisk *num_completedbeinggreaterthannum_queued.Thiswillcause *aBUG_ONindql_completed().
*/ if (adapter->reset_reason != VNIC_RESET_NON_FATAL)
netdev_tx_reset_queue(netdev_get_tx_queue(netdev, i));
}
/* Since queues were stopped until now, there shouldn't be any *oneinibmvnic_complete_tx()oribmvnic_xmit()somaybewe *don'tneedthesynchronize_rcu()?Leavingitforconsistency *withsetting->tx_queues_active=false.
*/
synchronize_rcu();
netif_tx_start_all_queues(netdev);
if (prev_state == VNIC_CLOSED) { for (i = 0; i < adapter->req_rx_queues; i++)
napi_schedule(&adapter->napi[i]);
}
/* If device failover is pending or we are about to reset, just set *devicestateandreturn.Deviceoperationwillbehandledbyreset *routine. * *Itshouldbesafetooverwritetheadapter->statehere.Since *weholdthertnl,eithertheresethasnotactuallystartedor *thertnlgotdroppedduringtheset_link_state()indo_reset(). *Intheformercase,nooneelseischangingthestate(againwe *havethertnl)andinthelattercase,do_reset()willdetectand *honoroursettingbelow.
*/ if (adapter->failover_pending || (test_bit(0, &adapter->resetting))) {
netdev_dbg(netdev, "[S:%s FOP:%d] Resetting, deferring open\n",
adapter_state_to_string(adapter->state),
adapter->failover_pending);
adapter->state = VNIC_OPEN;
rc = 0; goto out;
}
if (adapter->state != VNIC_CLOSED) {
rc = ibmvnic_login(netdev); if (rc) goto out;
rc = init_resources(adapter); if (rc) {
netdev_err(netdev, "failed to initialize resources\n"); goto out;
}
}
rc = __ibmvnic_open(netdev);
out: /* If open failed and there is a pending failover or in-progress reset, *setdevicestateandreturn.Deviceoperationwillbehandledby *resetroutine.Seealsocommentsaboveregardingrtnl.
*/ if (rc &&
(adapter->failover_pending || (test_bit(0, &adapter->resetting)))) {
adapter->state = VNIC_OPEN;
rc = 0;
}
if (rc) {
release_resources(adapter);
release_rx_pools(adapter);
release_tx_pools(adapter);
}
return rc;
}
staticvoid clean_rx_pools(struct ibmvnic_adapter *adapter)
{ struct ibmvnic_rx_pool *rx_pool; struct ibmvnic_rx_buff *rx_buff;
u64 rx_entries; int rx_scrqs; int i, j;
/* Free any remaining skbs in the rx buffer pools */ for (i = 0; i < rx_scrqs; i++) {
rx_pool = &adapter->rx_pool[i]; if (!rx_pool || !rx_pool->rx_buff) continue;
for (i = 0; i < tx_entries; i++) {
tx_buff = &tx_pool->tx_buff[i]; if (tx_buff && tx_buff->skb) {
dev_kfree_skb_any(tx_buff->skb);
tx_buff->skb = NULL;
}
}
}
staticvoid clean_tx_pools(struct ibmvnic_adapter *adapter)
{ int tx_scrqs; int i;
if (!adapter->tx_pool || !adapter->tso_pool) return;
tx_scrqs = adapter->num_active_tx_pools;
/* Free any remaining skbs in the tx buffer pools */ for (i = 0; i < tx_scrqs; i++) {
netdev_dbg(adapter->netdev, "Cleaning tx_pool[%d]\n", i);
clean_one_tx_pool(adapter, &adapter->tx_pool[i]);
clean_one_tx_pool(adapter, &adapter->tso_pool[i]);
}
}
if (adapter->tx_scrq) { for (i = 0; i < adapter->req_tx_queues; i++) if (adapter->tx_scrq[i]->irq) {
netdev_dbg(netdev, "Disabling tx_scrq[%d] irq\n", i);
disable_scrq_irq(adapter, adapter->tx_scrq[i]);
disable_irq(adapter->tx_scrq[i]->irq);
}
}
if (adapter->rx_scrq) { for (i = 0; i < adapter->req_rx_queues; i++) { if (adapter->rx_scrq[i]->irq) {
netdev_dbg(netdev, "Disabling rx_scrq[%d] irq\n", i);
disable_scrq_irq(adapter, adapter->rx_scrq[i]);
disable_irq(adapter->rx_scrq[i]->irq);
}
}
}
}
/* If device failover is pending, just set device state and return. *Deviceoperationwillbehandledbyresetroutine.
*/ if (adapter->failover_pending) {
adapter->state = VNIC_CLOSED; return0;
}
staticint create_hdr_descs(u8 hdr_field, u8 *hdr_data, int len, int *hdr_len, union sub_crq *scrq_arr)
{ union sub_crq *hdr_desc; int tmp_len = len; int num_descs = 0;
u8 *data, *cur; int tmp;
while (tmp_len > 0) {
cur = hdr_data + len - tmp_len;
staticvoid build_hdr_descs_arr(struct sk_buff *skb, union sub_crq *indir_arr, int *num_entries, u8 hdr_field)
{ int hdr_len[3] = {0, 0, 0}; int tot_len;
staticint send_subcrq_direct(struct ibmvnic_adapter *adapter,
u64 remote_handle, u64 *entry)
{ unsignedint ua = adapter->vdev->unit_address; struct device *dev = &adapter->vdev->dev; int rc;
/* Make sure the hypervisor sees the complete request */
dma_wmb();
rc = plpar_hcall_norets(H_SEND_SUB_CRQ, ua,
cpu_to_be64(remote_handle),
cpu_to_be64(entry[0]), cpu_to_be64(entry[1]),
cpu_to_be64(entry[2]), cpu_to_be64(entry[3]));
/* If a reset is in progress, drop the packet since *thescrqsmaygettorndown.Otherwiseusethe *rcutoensureresetwaitsforustocomplete.
*/
rcu_read_lock(); if (!adapter->tx_queues_active) {
dev_kfree_skb_any(skb);
tx_send_failed++;
tx_dropped++;
ret = NETDEV_TX_OK; goto out;
}
/* if we are going to send_subcrq_direct this then we need to *updatethechecksumbeforecopyingthedataintoltb.Essentially *thesepacketsforcedisableCSOsothatwecanguaranteethat *FWdoesnotneedheaderinfoandwecansenddirect.Also,vnic *servermustbeabletoxmitstandardpacketswithoutheaderdata
*/ if (*hdrs == 0 && !skb_is_gso(skb) &&
!ind_bufp->index && !netdev_xmit_more()) {
use_scrq_send_direct = true; if (skb->ip_summed == CHECKSUM_PARTIAL &&
skb_checksum_help(skb))
use_scrq_send_direct = false;
}
if (skb_shinfo(skb)->nr_frags) { int cur, i;
/* Copy the head */
skb_copy_from_linear_data(skb, dst, skb_headlen(skb));
cur = skb_headlen(skb);
/* Copy the frags */ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
/* Sanity checks on our free map to make sure it points to an index *thatisnotbeingoccupiedbyanotherskb.Ifskbmemoryis *notfreedthenweseecongestioncontrolkickinandhalttx.
*/ if (unlikely(tx_buff->skb)) {
dev_warn_ratelimited(dev, "TX free map points to untracked skb (%s %d idx=%d)\n",
skb_is_gso(skb) ? "tso_pool" : "tx_pool",
queue_num, bufidx);
dev_kfree_skb_any(tx_buff->skb);
}
if ((*hdrs >> 7) & 1)
build_hdr_descs_arr(skb, indir_arr, &num_entries, *hdrs);
tx_crq.v1.n_crq_elem = num_entries;
tx_buff->num_entries = num_entries; /* flush buffer if current entry can not fit */ if (num_entries + ind_bufp->index > IBMVNIC_MAX_IND_DESCS) {
lpar_rc = ibmvnic_tx_scrq_flush(adapter, tx_scrq, true); if (lpar_rc != H_SUCCESS) goto tx_flush_err;
}
rc = 0; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL;
ether_addr_copy(adapter->mac_addr, addr->sa_data); if (adapter->state != VNIC_PROBED)
rc = __ibmvnic_set_mac(netdev, addr->sa_data);
return rc;
}
staticconstchar *reset_reason_to_string(enum ibmvnic_reset_reason reason)
{ switch (reason) { case VNIC_RESET_FAILOVER: return"FAILOVER"; case VNIC_RESET_MOBILITY: return"MOBILITY"; case VNIC_RESET_FATAL: return"FATAL"; case VNIC_RESET_NON_FATAL: return"NON_FATAL"; case VNIC_RESET_TIMEOUT: return"TIMEOUT"; case VNIC_RESET_CHANGE_PARAM: return"CHANGE_PARAM"; case VNIC_RESET_PASSIVE_INIT: return"PASSIVE_INIT";
} return"UNKNOWN";
}
adapter->reset_reason = rwi->reset_reason; /* requestor of VNIC_RESET_CHANGE_PARAM already has the rtnl lock */ if (!(adapter->reset_reason == VNIC_RESET_CHANGE_PARAM))
rtnl_lock();
/* Now that we have the rtnl lock, clear any pending failover. *Thiswillensureibmvnic_open()haseithercompletedorwill *blockuntilfailoveriscomplete.
*/ if (rwi->reset_reason == VNIC_RESET_FAILOVER)
adapter->failover_pending = false;
/* read the state and check (again) after getting rtnl */
reset_state = adapter->state;
/* Release the RTNL lock before link state change and *re-acquireafterthelinkstatechangetoallow *linkwatch_eventtograbtheRTNLlockandrunduring *areset.
*/
rtnl_unlock();
rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN);
rtnl_lock(); if (rc) goto out;
if (adapter->state == VNIC_OPEN) { /* When we dropped rtnl, ibmvnic_open() got *itandnoticedthatweareresettingand *settheadapterstatetoOPEN.Updateour *new"target"state,andresumethereset *fromVNIC_CLOSINGstate.
*/
netdev_dbg(netdev, "Open changed state from %s, updating.\n",
adapter_state_to_string(reset_state));
reset_state = VNIC_OPEN;
adapter->state = VNIC_CLOSING;
}
if (adapter->state != VNIC_CLOSING) { /* If someone else changed the adapter state *whenwedroppedthertnl,failthereset
*/
rc = -EAGAIN; goto out;
}
adapter->state = VNIC_CLOSED;
}
}
if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) {
release_resources(adapter);
release_sub_crqs(adapter, 1);
release_crq_queue(adapter);
}
if (adapter->reset_reason != VNIC_RESET_NON_FATAL) { /* remove the closed state so when we call open it appears *wearecomingfromtheprobedstate.
*/
adapter->state = VNIC_PROBED;
rc = ibmvnic_reset_init(adapter, true); if (rc) goto out;
/* If the adapter was in PROBE or DOWN state prior to the reset, *exithere.
*/ if (reset_state == VNIC_PROBED || reset_state == VNIC_DOWN) {
rc = 0; goto out;
}
/* refresh device's multicast list */
ibmvnic_set_multi(netdev);
if (adapter->reset_reason == VNIC_RESET_FAILOVER ||
adapter->reset_reason == VNIC_RESET_MOBILITY)
__netdev_notify_peers(netdev);
rc = 0;
out: /* restore the adapter state if reset failed */ if (rc)
adapter->state = reset_state; /* requestor of VNIC_RESET_CHANGE_PARAM should still hold the rtnl lock */ if (!(adapter->reset_reason == VNIC_RESET_CHANGE_PARAM))
rtnl_unlock();
adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset);
dev = &adapter->vdev->dev;
/* Wait for ibmvnic_probe() to complete. If probe is taking too long *orifanotherresetisinprogress,deferworkfornow.Ifprobe *eventuallyfailsitwillflushandterminateourwork. * *Threepossibilitieshere: *1.Adpaterbeingremoved-justreturn *2.Timedoutonprobeoranotherresetinprogress-delaythework *3.Completedprobe-performanyresetsinqueue
*/ if (adapter->state == VNIC_PROBING &&
!wait_for_completion_timeout(&adapter->probe_done, timeout)) {
dev_err(dev, "Reset thread timed out on probe");
queue_delayed_work(system_long_wq,
&adapter->ibmvnic_delayed_reset,
IBMVNIC_RESET_DELAY); return;
}
/* adapter is done with probe (i.e state is never VNIC_PROBING now) */ if (adapter->state == VNIC_REMOVING) return;
/* ->rwi_list is stable now (no one else is removing entries) */
/* ibmvnic_probe() may have purged the reset queue after we were *scheduledtoprocessaresetsotheremaybenoresetstoprocess. *Beforesettingthe->resettingbitthough,wehavetomakesure *thatthereisinfactaresettoprocess.Otherwisewemayrace *withibmvnic_open()andendupleavingthevnicdown: * *__ibmvnic_reset()ibmvnic_open() *------------------------------- * *set->resettingbit *find->resettingbitisset *set->statetoIBMVNIC_OPEN(i.e *assumeresetwillopendevice) *return *findresetqueueempty *return * *Neitherperformedvniclogin/openandvnicstaysdown * *Ifweholdthelockandconditionallysetthebit,eitherwe *oribmvnic_open()willcompletetheopen.
*/
need_reset = false;
spin_lock(&adapter->rwi_lock); if (!list_empty(&adapter->rwi_list)) { if (test_and_set_bit_lock(0, &adapter->resetting)) {
queue_delayed_work(system_long_wq,
&adapter->ibmvnic_delayed_reset,
IBMVNIC_RESET_DELAY);
} else {
need_reset = true;
}
}
spin_unlock(&adapter->rwi_lock);
if (!need_reset) return;
rwi = get_next_rwi(adapter); while (rwi) {
spin_lock_irqsave(&adapter->state_lock, flags);
if (rwi->reset_reason == VNIC_RESET_PASSIVE_INIT) {
rtnl_lock();
rc = do_passive_init(adapter);
rtnl_unlock(); if (!rc)
netif_carrier_on(adapter->netdev);
} elseif (adapter->force_reset_recovery) { /* Since we are doing a hard reset now, clear the *failover_pendingflagsowedon'tignoreany *futureMOBILITYorotherresets.
*/
adapter->failover_pending = false;
/* Transport event occurred during previous reset */ if (adapter->wait_for_reset) { /* Previous was CHANGE_PARAM; caller locked */
adapter->force_reset_recovery = false;
rc = do_hard_reset(adapter, rwi, reset_state);
} else {
rtnl_lock();
adapter->force_reset_recovery = false;
rc = do_hard_reset(adapter, rwi, reset_state);
rtnl_unlock();
} if (rc)
num_fails++; else
num_fails = 0;
/* If auto-priority-failover is enabled we can get *backtobackfailoversduringresets,resulting *inatleasttwofailedresets(fromhigh-priority *backingdevicetolow-priorityoneandthenback) *Ifresetscontinuetofailbeyondthat,givethe *adaptersometimetosettledownbeforeretrying.
*/ if (num_fails >= 3) {
netdev_dbg(adapter->netdev, "[S:%s] Hard reset failed %d times, waiting 60 secs\n",
adapter_state_to_string(adapter->state),
num_fails);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(60 * HZ);
}
} else {
rc = do_reset(adapter, rwi, reset_state);
}
tmprwi = rwi;
adapter->last_reset_time = jiffies;
if (rc)
netdev_dbg(adapter->netdev, "Reset failed, rc=%d\n", rc);
rwi = kzalloc(sizeof(*rwi), GFP_ATOMIC); if (!rwi) {
ret = ENOMEM; goto err;
} /* if we just received a transport event, *flushresetqueueandprocessthisreset
*/ if (adapter->force_reset_recovery)
flush_reset_queue(adapter);
if (test_bit(0, &adapter->resetting)) {
netdev_err(adapter->netdev, "Adapter is resetting, skip timeout reset\n"); return;
} /* No queuing up reset until at least 5 seconds (default watchdog val) *afterlastreset
*/ if (time_before(jiffies, (adapter->last_reset_time + dev->watchdog_timeo))) {
netdev_dbg(dev, "Not yet time to tx timeout.\n"); return;
}
ibmvnic_reset(adapter, VNIC_RESET_TIMEOUT);
}
/* VLAN Header has been stripped by the system firmware and *needstobeinsertedbythedriver
*/ if (adapter->rx_vlan_header_insertion &&
(flags & IBMVNIC_VLAN_STRIPPED))
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
ntohs(next->rx_comp.vlan_tci));
/* free the entry */
next->rx_comp.first = 0;
remove_buff_from_pool(adapter, rx_buff);
/* Don't need to send a query because we request a logical link up at *initandthenwewaitforlinkstateindications
*/ return adapter->logical_link_state;
}
/* Wait for data to be written */
reinit_completion(&adapter->stats_done);
rc = ibmvnic_send_crq(adapter, &crq); if (rc) return;
rc = ibmvnic_wait_for_completion(adapter, &adapter->stats_done, 10000); if (rc) return;
for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++)
data[i] = be64_to_cpu(IBMVNIC_GET_STAT
(adapter, ibmvnic_stats[i].offset));
if (do_h_free) { /* Close the sub-crqs */ do {
rc = plpar_hcall_norets(H_FREE_SUB_CRQ,
adapter->vdev->unit_address,
scrq->crq_num);
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
if (rc) {
netdev_err(adapter->netdev, "Failed to release sub-CRQ %16lx, rc = %ld\n",
scrq->crq_num, rc);
}
}
/* We can not use the IRQ chip EOI handler because that has the *unintendedeffectofchangingtheinterruptpriority.
*/ staticvoid ibmvnic_xics_eoi(struct device *dev, struct ibmvnic_sub_crq_queue *scrq)
{
u64 val = 0xff000000 | scrq->hw_irq; unsignedlong rc;
/* Due to a firmware bug, the hypervisor can send an interrupt to a *transmitorreceivequeuejustpriortoapartitionmigration. *ForceanEOIaftermigration.
*/ staticvoid ibmvnic_clear_pending_interrupt(struct device *dev, struct ibmvnic_sub_crq_queue *scrq)
{ if (!xive_enabled())
ibmvnic_xics_eoi(dev, scrq);
}
restart_loop: while (pending_scrq(adapter, scrq)) { unsignedint pool = scrq->pool_index; int num_entries = 0;
next = ibmvnic_next_scrq(adapter, scrq); for (i = 0; i < next->tx_comp.num_comps; i++) {
index = be32_to_cpu(next->tx_comp.correlators[i]); if (index & IBMVNIC_TSO_POOL_MASK) {
tx_pool = &adapter->tso_pool[pool];
index &= ~IBMVNIC_TSO_POOL_MASK;
} else {
tx_pool = &adapter->tx_pool[pool];
}
/* When booting a kdump kernel we can hit pending interrupts *priortocompletingdriverinitialization.
*/ if (unlikely(adapter->state != VNIC_OPEN)) return IRQ_NONE;
staticint init_sub_crqs(struct ibmvnic_adapter *adapter)
{ struct device *dev = &adapter->vdev->dev; struct ibmvnic_sub_crq_queue **allqueues; int registered_queues = 0; int total_queues; int more = 0; int i;
allqueues = kcalloc(total_queues, sizeof(*allqueues), GFP_KERNEL); if (!allqueues) return -ENOMEM;
for (i = 0; i < total_queues; i++) {
allqueues[i] = init_sub_crq_queue(adapter); if (!allqueues[i]) {
dev_warn(dev, "Couldn't allocate all sub-crqs\n"); break;
}
registered_queues++;
}
/* Make sure we were able to register the minimum number of queues */ if (registered_queues <
adapter->min_tx_queues + adapter->min_rx_queues) {
dev_err(dev, "Fatal: Couldn't init min number of sub-crqs\n"); goto tx_failed;
}
/* Distribute the failed allocated queues*/ for (i = 0; i < total_queues - registered_queues + more ; i++) {
netdev_dbg(adapter->netdev, "Reducing number of queues\n"); switch (i % 3) { case0: if (adapter->req_rx_queues > adapter->min_rx_queues)
adapter->req_rx_queues--; else
more++; break; case1: if (adapter->req_tx_queues > adapter->min_tx_queues)
adapter->req_tx_queues--; else
more++; break;
}
}
adapter->tx_scrq = kcalloc(adapter->req_tx_queues, sizeof(*adapter->tx_scrq), GFP_KERNEL); if (!adapter->tx_scrq) goto tx_failed;
for (i = 0; i < adapter->req_tx_queues; i++) {
adapter->tx_scrq[i] = allqueues[i];
adapter->tx_scrq[i]->pool_index = i;
adapter->num_active_tx_scrqs++;
}
adapter->rx_scrq = kcalloc(adapter->req_rx_queues, sizeof(*adapter->rx_scrq), GFP_KERNEL); if (!adapter->rx_scrq) goto rx_failed;
for (i = 0; i < adapter->req_rx_queues; i++) {
adapter->rx_scrq[i] = allqueues[i + adapter->req_tx_queues];
adapter->rx_scrq[i]->scrq_num = i;
adapter->num_active_rx_scrqs++;
}
kfree(allqueues); return0;
rx_failed:
kfree(adapter->tx_scrq);
adapter->tx_scrq = NULL;
tx_failed: for (i = 0; i < registered_queues; i++)
release_sub_crq_queue(adapter, allqueues[i], 1);
kfree(allqueues); return -ENOMEM;
}
staticvoid send_request_cap(struct ibmvnic_adapter *adapter, int retry)
{ struct device *dev = &adapter->vdev->dev; union ibmvnic_crq crq; int max_entries; int cap_reqs;
/* We send out 6 or 7 REQUEST_CAPABILITY CRQs below (depending on *thePROMISCflag).Initializethiscountupfront.Whenthetasklet *receivesaresponsetoallofthese,itwillsendthenextprotocol *message(QUERY_IP_OFFLOAD).
*/ if (!(adapter->netdev->flags & IFF_PROMISC) ||
adapter->promisc_supported)
cap_reqs = 7; else
cap_reqs = 6;
if (!retry) { /* Sub-CRQ entries are 32 byte long */ int entries_page = 4 * PAGE_SIZE / (sizeof(u64) * 4);
atomic_set(&adapter->running_cap_crqs, cap_reqs);
if (adapter->min_tx_entries_per_subcrq > entries_page ||
adapter->min_rx_add_entries_per_subcrq > entries_page) {
dev_err(dev, "Fatal, invalid entries per sub-crq\n"); return;
}
if (!adapter->desired.tx_entries)
adapter->desired.tx_entries =
adapter->max_tx_entries_per_subcrq; if (!adapter->desired.rx_entries)
adapter->desired.rx_entries =
adapter->max_rx_add_entries_per_subcrq;
/* Make sure the hypervisor sees the complete request */
dma_wmb();
rc = plpar_hcall_norets(H_SEND_SUB_CRQ_INDIRECT, ua,
cpu_to_be64(remote_handle),
ioba, num_entries);
if (rc)
print_subcrq_error(dev, rc, __func__);
return rc;
}
staticint ibmvnic_send_crq(struct ibmvnic_adapter *adapter, union ibmvnic_crq *crq)
{ unsignedint ua = adapter->vdev->unit_address; struct device *dev = &adapter->vdev->dev;
u64 *u64_crq = (u64 *)crq; int rc;
if (!adapter->crq.active &&
crq->generic.first != IBMVNIC_CRQ_INIT_CMD) {
dev_warn(dev, "Invalid request detected while CRQ is inactive, possible device state change during reset\n"); return -EINVAL;
}
/* Make sure the hypervisor sees the complete request */
dma_wmb();
staticint vnic_client_data_len(struct ibmvnic_adapter *adapter)
{ int len;
/* Calculate the amount of buffer space needed for the *vnicclientdataintheloginbuffer.Therearefourentries, *OSname,LPARname,devicename,andanulllastentry.
*/
len = 4 * sizeof(struct vnic_login_client_data);
len += 6; /* "Linux" plus NULL */
len += strlen(utsname()->nodename) + 1;
len += strlen(adapter->netdev->name) + 1;
for (size_t i = 0; i < len; i += 16) {
hex_dump_to_buffer((unsignedchar *)buf + i, len - i, 16, 8,
hex_str, sizeof(hex_str), false);
netdev_dbg(dev, "%s\n", hex_str);
}
}
staticint send_login(struct ibmvnic_adapter *adapter)
{ struct ibmvnic_login_rsp_buffer *login_rsp_buffer; struct ibmvnic_login_buffer *login_buffer; struct device *dev = &adapter->vdev->dev; struct vnic_login_client_data *vlcd;
dma_addr_t rsp_buffer_token;
dma_addr_t buffer_token;
size_t rsp_buffer_size; union ibmvnic_crq crq; int client_data_len;
size_t buffer_size;
__be64 *tx_list_p;
__be64 *rx_list_p; int rc; int i;
if (!adapter->tx_scrq || !adapter->rx_scrq) {
netdev_err(adapter->netdev, "RX or TX queues are not allocated, device login failed\n"); return -ENOMEM;
}
/* Send a series of CRQs requesting various capabilities of the VNIC server */ staticvoid send_query_cap(struct ibmvnic_adapter *adapter)
{ union ibmvnic_crq crq; int cap_reqs;
/* We send out 25 QUERY_CAPABILITY CRQs below. Initialize this count *upfront.Whenthetaskletreceivesaresponsetoallofthese,it *cansendoutthenextprotocolmessaage(REQUEST_CAPABILITY).
*/
cap_reqs = 25;
if (crq->get_vpd_rsp.rc.code) {
dev_err(dev, "Error retrieving VPD from device, rc=%x\n",
crq->get_vpd_rsp.rc.code); goto complete;
}
/* get the position of the firmware version info *locatedaftertheASCII'RM'substringinthebuffer
*/
substr = strnstr(adapter->vpd->buff, "RM", adapter->vpd->len); if (!substr) {
dev_info(dev, "Warning - No FW level has been provided in the VPD buffer by the VIOS Server\n"); goto complete;
}
/* get length of firmware level ASCII substring */ if ((substr + 2) < (adapter->vpd->buff + adapter->vpd->len)) {
fw_level_len = *(substr + 2);
} else {
dev_info(dev, "Length of FW substr extrapolated VDP buff\n"); goto complete;
}
/* copy firmware version string from vpd into adapter */ if ((substr + 3 + fw_level_len) <
(adapter->vpd->buff + adapter->vpd->len)) {
strscpy(adapter->fw_version, substr + 3, sizeof(adapter->fw_version));
} else {
dev_info(dev, "FW substr extrapolated VPD buff\n");
}
/* CHECK: Test/set of login_pending does not need to be atomic *becauseonlyibmvnic_tasklettests/clearsthis.
*/ if (!adapter->login_pending) {
netdev_warn(netdev, "Ignoring unexpected login response\n"); return0;
}
adapter->login_pending = false;
/* If the number of queues requested can't be allocated by the *server,theloginresponsewillreturnwithcode1.Wewillneed *toresendtheloginbufferwithfewerqueuesrequested.
*/ if (login_rsp_crq->generic.rc.code) {
adapter->init_done_rc = login_rsp_crq->generic.rc.code;
complete(&adapter->init_done); return0;
}
if (adapter->failover_pending) {
adapter->init_done_rc = -EAGAIN;
netdev_dbg(netdev, "Failover pending, ignoring login response\n");
complete(&adapter->init_done); /* login response buffer will be released on reset */ return0;
}
rsp_len = be32_to_cpu(login_rsp->len); if (be32_to_cpu(login->login_rsp_len) < rsp_len ||
rsp_len <= be32_to_cpu(login_rsp->off_txsubm_subcrqs) ||
rsp_len <= be32_to_cpu(login_rsp->off_rxadd_subcrqs) ||
rsp_len <= be32_to_cpu(login_rsp->off_rxadd_buff_size) ||
rsp_len <= be32_to_cpu(login_rsp->off_supp_tx_desc)) { /* This can happen if a login request times out and there are *2outstandingloginrequestssent,theLOGIN_RSPcrq *couldhavebeenfortheolderloginrequest.Soweare *parsingthenewerresponsebufferwhichmaybeincomplete
*/
dev_err(dev, "FATAL: Login rsp offsets/lengths invalid\n");
ibmvnic_reset(adapter, VNIC_RESET_FATAL); return -EIO;
}
size_array = (u64 *)((u8 *)(adapter->login_rsp_buf) +
be32_to_cpu(adapter->login_rsp_buf->off_rxadd_buff_size)); /* variable buffer sizes are not supported, so just read the *firstentry.
*/
adapter->cur_rx_buf_sz = be64_to_cpu(size_array[0]);
if (rc && rc != -EBUSY) { /* We were unable to schedule the failover *reseteitherbecausetheadapterwasstill *probing(eg:duringkexec)orwecouldnot *allocatememory.Clearthefailover_pending *flagsincenooneelsewill.Weignore *EBUSYbecauseitmeanseitherFAILOVERreset *isalreadyscheduledortheadapteris *beingremoved.
*/
netdev_err(netdev, "Error %ld scheduling failover reset\n",
rc);
adapter->failover_pending = false;
}
if (!completion_done(&adapter->init_done)) { if (!adapter->init_done_rc)
adapter->init_done_rc = -EAGAIN;
complete(&adapter->init_done);
}
break; case IBMVNIC_CRQ_INIT_COMPLETE:
dev_info(dev, "Partner initialization complete\n");
adapter->crq.active = true;
send_version_xchg(adapter); break; default:
dev_err(dev, "Unknown crq cmd: %d\n", gen_crq->cmd);
} return; case IBMVNIC_CRQ_XPORT_EVENT:
netif_carrier_off(netdev);
adapter->crq.active = false; /* terminate any thread waiting for a response *fromthedevice
*/ if (!completion_done(&adapter->fw_done)) {
adapter->fw_done_rc = -EIO;
complete(&adapter->fw_done);
}
/* if we got here during crq-init, retry crq-init */ if (!completion_done(&adapter->init_done)) {
adapter->init_done_rc = -EAGAIN;
complete(&adapter->init_done);
}
if (!completion_done(&adapter->stats_done))
complete(&adapter->stats_done); if (test_bit(0, &adapter->resetting))
adapter->force_reset_recovery = true; if (gen_crq->cmd == IBMVNIC_PARTITION_MIGRATED) {
dev_info(dev, "Migrated, re-enabling adapter\n");
ibmvnic_reset(adapter, VNIC_RESET_MOBILITY);
} elseif (gen_crq->cmd == IBMVNIC_DEVICE_FAILOVER) {
dev_info(dev, "Backing device failover detected\n");
adapter->failover_pending = true;
} else { /* The adapter lost the connection */
dev_err(dev, "Virtual Adapter failed (rc=%d)\n",
gen_crq->cmd);
ibmvnic_reset(adapter, VNIC_RESET_FATAL);
} return; case IBMVNIC_CRQ_CMD_RSP: break; default:
dev_err(dev, "Got an invalid msg type 0x%02x\n",
gen_crq->first); return;
}
switch (gen_crq->cmd) { case VERSION_EXCHANGE_RSP:
rc = crq->version_exchange_rsp.rc.code; if (rc) {
dev_err(dev, "Error %ld in VERSION_EXCHG_RSP\n", rc); break;
}
ibmvnic_version =
be16_to_cpu(crq->version_exchange_rsp.version);
dev_info(dev, "Partner protocol version is %d\n",
ibmvnic_version);
send_query_cap(adapter); break; case QUERY_CAPABILITY_RSP:
handle_query_cap_rsp(crq, adapter); break; case QUERY_MAP_RSP:
handle_query_map_rsp(crq, adapter); break; case REQUEST_MAP_RSP:
adapter->fw_done_rc = crq->request_map_rsp.rc.code;
complete(&adapter->fw_done); break; case REQUEST_UNMAP_RSP:
handle_request_unmap_rsp(crq, adapter); break; case REQUEST_CAPABILITY_RSP:
handle_request_cap_rsp(crq, adapter); break; case LOGIN_RSP:
netdev_dbg(netdev, "Got Login Response\n");
handle_login_rsp(crq, adapter); break; case LOGICAL_LINK_STATE_RSP:
netdev_dbg(netdev, "Got Logical Link State Response, state: %d rc: %d\n",
crq->logical_link_state_rsp.link_state,
crq->logical_link_state_rsp.rc.code);
adapter->logical_link_state =
crq->logical_link_state_rsp.link_state;
adapter->init_done_rc = crq->logical_link_state_rsp.rc.code;
complete(&adapter->init_done); break; case LINK_STATE_INDICATION:
netdev_dbg(netdev, "Got Logical Link State Indication\n");
adapter->phys_link_state =
crq->link_state_indication.phys_link_state;
adapter->logical_link_state =
crq->link_state_indication.logical_link_state; if (adapter->phys_link_state && adapter->logical_link_state)
netif_carrier_on(netdev); else
netif_carrier_off(netdev); break; case CHANGE_MAC_ADDR_RSP:
netdev_dbg(netdev, "Got MAC address change Response\n");
adapter->fw_done_rc = handle_change_mac_rsp(crq, adapter); break; case ERROR_INDICATION:
netdev_dbg(netdev, "Got Error Indication\n");
handle_error_indication(crq, adapter); break; case REQUEST_STATISTICS_RSP:
netdev_dbg(netdev, "Got Statistics Response\n");
complete(&adapter->stats_done); break; case QUERY_IP_OFFLOAD_RSP:
netdev_dbg(netdev, "Got Query IP offload Response\n");
handle_query_ip_offload_rsp(adapter); break; case MULTICAST_CTRL_RSP:
netdev_dbg(netdev, "Got multicast control Response\n"); break; case CONTROL_IP_OFFLOAD_RSP:
netdev_dbg(netdev, "Got Control IP offload Response\n");
dma_unmap_single(dev, adapter->ip_offload_ctrl_tok, sizeof(adapter->ip_offload_ctrl),
DMA_TO_DEVICE);
complete(&adapter->init_done); break; case COLLECT_FW_TRACE_RSP:
netdev_dbg(netdev, "Got Collect firmware trace Response\n");
complete(&adapter->fw_done); break; case GET_VPD_SIZE_RSP:
handle_vpd_size_rsp(crq, adapter); break; case GET_VPD_RSP:
handle_vpd_rsp(crq, adapter); break; case QUERY_PHYS_PARMS_RSP:
adapter->fw_done_rc = handle_query_phys_parms_rsp(crq, adapter);
complete(&adapter->fw_done); break; default:
netdev_err(netdev, "Got an invalid cmd type 0x%02x\n",
gen_crq->cmd);
}
}
/* Pull all the valid messages off the CRQ */ while ((crq = ibmvnic_next_crq(adapter)) != NULL) { /* This barrier makes sure ibmvnic_next_crq()'s *crq->generic.first&IBMVNIC_CRQ_CMD_RSPisloaded *beforeibmvnic_handle_crq()'s *switch(gen_crq->first)andswitch(gen_crq->cmd).
*/
dma_rmb();
ibmvnic_handle_crq(crq, adapter);
crq->generic.first = 0;
}
/* And re-open it again */
rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
crq->msg_token, PAGE_SIZE);
if (rc == H_CLOSED) /* Adapter is good, but other end is not ready */
dev_warn(dev, "Partner adapter not ready\n"); elseif (rc != 0)
dev_warn(dev, "Couldn't register crq (rc=%d)\n", rc);
init_success = false; do {
reinit_init_done(adapter);
/* clear any failovers we got in the previous pass *sincewearereinitializingtheCRQ
*/
adapter->failover_pending = false;
/* If we had already initialized CRQ, we may have one or *moreresetsqueuedalready.Discardthoseandrelease *theCRQbeforeinitializingtheCRQagain.
*/
release_crq_queue(adapter);
/* Since we are still in PROBING state, __ibmvnic_reset() *willnotaccessthe->rwi_listandsincewereleasedCRQ, *wewon'tget_new_transportevents.Buttheremaybean *ongoingibmvnic_reset()call.Soserializeaccessto *rwi_list.Ifwewintherace,ibvmnic_reset()couldadd *aresetafterwepurgedbutthatsok-wejustmayend *upwithanextrareset(i.esimilartohavingtwoormore *resetsinthequeueatonce). *CHECK.
*/
spin_lock_irqsave(&adapter->rwi_lock, flags);
flush_reset_queue(adapter);
spin_unlock_irqrestore(&adapter->rwi_lock, flags);
rc = ibmvnic_reset_init(adapter, false);
} while (rc == -EAGAIN);
/* We are ignoring the error from ibmvnic_reset_init() assuming that the *partnerisnotready.CRQisnotactive.Whenthepartnerbecomes *ready,wewilldothepassiveinitreset.
*/
if (!rc)
init_success = true;
rc = init_stats_buffers(adapter); if (rc) goto ibmvnic_init_fail;
rc = init_stats_token(adapter); if (rc) goto ibmvnic_stats_fail;
rc = device_create_file(&dev->dev, &dev_attr_failover); if (rc) goto ibmvnic_dev_file_err;
/* cleanup worker thread after releasing CRQ so we don't get *transportevents(i.enewworkitemsfortheworkerthread).
*/
adapter->state = VNIC_REMOVING;
complete(&adapter->probe_done);
flush_work(&adapter->ibmvnic_reset);
flush_delayed_work(&adapter->ibmvnic_delayed_reset);
/* If ibmvnic_reset() is scheduling a reset, wait for it to *finish.Then,setthestatetoREMOVINGtopreventitfrom *schedulinganymoreworkandtohaveresetfunctionsignore *anyresetsthathavealreadybeenscheduled.Dropthelock *aftersettingstate,so__ibmvnic_reset()whichiscalled *fromtheflush_work()below,canmakeprogress.
*/
spin_lock(&adapter->rwi_lock);
adapter->state = VNIC_REMOVING;
spin_unlock(&adapter->rwi_lock);
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.218Bemerkung:
(vorverarbeitet am 2026-10-01)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.