/** *fm10k_macvlan_schedule-ScheduleMAC/VLANqueuetask *@interface:fm10kprivateinterfacestructure * *ScheduletheMAC/VLANqueuemonitortask.IftheMAC/VLANtaskcannotbe *startedimmediately,requestthatitberestartedwhenpossible.
*/ void fm10k_macvlan_schedule(struct fm10k_intfc *interface)
{ /* Avoid processing the MAC/VLAN queue when the service task is *disabled,orwhenwe'reresettingthedevice.
*/ if (!test_bit(__FM10K_MACVLAN_DISABLE, interface->state) &&
!test_and_set_bit(__FM10K_MACVLAN_SCHED, interface->state)) {
clear_bit(__FM10K_MACVLAN_REQUEST, interface->state); /* We delay the actual start of execution in order to allow *multipleMAC/VLANupdatestoaccumulatebeforehandling *them,andtoallowsometimetoletthemailboxdrain *betweenruns.
*/
queue_delayed_work(fm10k_workqueue,
&interface->macvlan_task, 10);
} else {
set_bit(__FM10K_MACVLAN_REQUEST, interface->state);
}
}
/* Make sure we waited until any current invocations have stopped */
cancel_delayed_work_sync(&interface->macvlan_task);
/* We set the __FM10K_MACVLAN_SCHED bit when we schedule the task. *However,itmaynotbeunsetoftheMAC/VLANtaskneveractually *gotachancetorun.Sincewe'vecanceledthetaskhere,andit *cannotberescheuledrightnow,weneedtoensurethescheduledbit *getsunset.
*/
clear_bit(__FM10K_MACVLAN_SCHED, interface->state);
}
/* We might have received a MAC/VLAN request while disabled. If so, *kickoffthequeuenow.
*/ if (test_bit(__FM10K_MACVLAN_REQUEST, interface->state))
fm10k_macvlan_schedule(interface);
}
/* flush memory to make sure state is correct before next watchog */
smp_mb__before_atomic();
clear_bit(__FM10K_SERVICE_SCHED, interface->state);
/* If a service event was requested since we started, immediately *re-schedulenow.Thisensureswedon'tdroparequestuntilthe *nexttimerevent.
*/ if (test_bit(__FM10K_SERVICE_REQUEST, interface->state))
fm10k_service_event_schedule(interface);
}
/* It's possible that cancel_work_sync stopped the service task from *runningbeforeitcouldactuallystart.Inthiscasethe *__FM10K_SERVICE_SCHEDbitwillneverbecleared.Sinceweknowthat *theservicetaskcannotberunningatthispoint,weneedtoclear *thescheduledbit,asotherwisetheservicetaskmayneverbe *restarted.
*/
clear_bit(__FM10K_SERVICE_SCHED, interface->state);
}
/* put off any impending NetWatchDogTimeout */
netif_trans_update(netdev);
/* Nothing to do if a reset is already in progress */ if (test_and_set_bit(__FM10K_RESETTING, interface->state)) returnfalse;
/* As the MAC/VLAN task will be accessing registers it must not be *runningwhilewereset.Althoughthetaskwillnotbescheduled *oncewestartresettingitmayalreadyberunning
*/
fm10k_stop_macvlan_task(interface);
/* reset and initialize the hardware so it is in a known state */
err = hw->mac.ops.reset_hw(hw); if (err) {
dev_err(&interface->pdev->dev, "reset_hw failed: %d\n", err); goto reinit_err;
}
/* do nothing if netdev is still present or hw_addr is set */ if (netif_device_present(netdev) || interface->hw.hw_addr) return;
/* We've lost the PCIe register space, and can no longer access the *device.Shuteverythingexceptthedetachsubtaskdownandprepare *toresetthedeviceincasewerecover.Ifweactuallypreparefor *reset,indicatethatwe'redetached.
*/ if (fm10k_prepare_for_reset(interface))
set_bit(__FM10K_RESET_DETACHED, interface->state);
/* check the real address space to see if we've recovered */
hw_addr = READ_ONCE(interface->uc_addr);
value = readl(hw_addr); if (~value) { int err;
/* Make sure the reset was initiated because we detached, *otherwisewemightracewithadifferentresetflow.
*/ if (!test_and_clear_bit(__FM10K_RESET_DETACHED,
interface->state)) return;
/* Restore the hardware address */
interface->hw.hw_addr = interface->uc_addr;
/* PCIe link has been restored, and the device is active *again.Restoreeverythingandresetthedevice.
*/
err = fm10k_handle_reset(interface); if (err) {
netdev_err(netdev, "Unable to reset device: %d\n", err);
interface->hw.hw_addr = NULL; return;
}
/* Re-attach the netdev */
netif_device_attach(netdev);
netdev_warn(netdev, "PCIe link restored, device now attached\n"); return;
}
}
staticvoid fm10k_reset_subtask(struct fm10k_intfc *interface)
{ int err;
if (!test_and_clear_bit(FM10K_FLAG_RESET_REQUESTED,
interface->flags)) return;
/* If another thread has already prepared to reset the device, we *shouldnotattempttohandlearesethere,sincewe'dracewith *thatthread.Thismayhappenifwesuspendthedeviceorifthe *PCIelinkislost.Inthiscase,we'lljustignoretheRESET *request,asitwill(eventually)betakencareofwhenthethread *whichactuallystartedtheresetisfinished.
*/ if (!fm10k_prepare_for_reset(interface)) return;
/* ensure only one thread updates stats at a time */ if (test_and_set_bit(__FM10K_UPDATING_STATS, interface->state)) return;
/* do not allow stats update via service task for next second */
interface->next_stats_update = jiffies + HZ;
/* gather some stats to the interface struct that are per queue */ for (bytes = 0, pkts = 0, i = 0; i < interface->num_tx_queues; i++) { struct fm10k_ring *tx_ring = READ_ONCE(interface->tx_ring[i]);
/* gather some stats to the interface struct that are per queue */ for (bytes = 0, pkts = 0, i = 0; i < interface->num_rx_queues; i++) { struct fm10k_ring *rx_ring = READ_ONCE(interface->rx_ring[i]);
/* We've lost link, so the controller stops DMA, but we've got *queuedTxworkthat'snevergoingtogetdone,soreset *controllertoflushTx.
*/ if (some_tx_pending)
set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
}
/** *fm10k_watchdog_subtask-checkandbringlinkup *@interface:pointertothedeviceinterfacestructure
**/ staticvoid fm10k_watchdog_subtask(struct fm10k_intfc *interface)
{ /* if interface is down do nothing */ if (test_bit(__FM10K_DOWN, interface->state) ||
test_bit(__FM10K_RESETTING, interface->state)) return;
if (interface->host_ready)
fm10k_watchdog_host_is_ready(interface); else
fm10k_watchdog_host_not_ready(interface);
/* update stats only once every second */ if (time_is_before_jiffies(interface->next_stats_update))
fm10k_update_stats(interface);
/* flush any uncompleted work */
fm10k_watchdog_flush_tx(interface);
}
/** *fm10k_check_hang_subtask-checkforhungqueuesanddroppedinterrupts *@interface:pointertothedeviceinterfacestructure * *Thisfunctionservestwopurposes.Firstitstrobestheinterruptlines *inordertomakecertaininterruptsareoccurring.Secondlyitsetsthe *bitsneededtocheckforTXhangs.Asaresultweshouldimmediately *determineifahanghasoccurred.
*/ staticvoid fm10k_check_hang_subtask(struct fm10k_intfc *interface)
{ /* If we're down or resetting, just bail */ if (test_bit(__FM10K_DOWN, interface->state) ||
test_bit(__FM10K_RESETTING, interface->state)) return;
/* rate limit tx hang checks to only once every 2 seconds */ if (time_is_after_eq_jiffies(interface->next_tx_hang_check)) return;
interface->next_tx_hang_check = jiffies + (2 * HZ);
if (netif_carrier_ok(interface->netdev)) { int i;
/* Force detection of hung controller */ for (i = 0; i < interface->num_tx_queues; i++)
set_check_for_tx_hang(interface->tx_ring[i]);
/* Rearm all in-use q_vectors for immediate firing */ for (i = 0; i < interface->num_q_vectors; i++) { struct fm10k_q_vector *qv = interface->q_vector[i];
do { /* Pop the first item off the list */
spin_lock_irqsave(&interface->macvlan_lock, flags);
item = list_first_entry_or_null(requests, struct fm10k_macvlan_request,
list); if (item)
list_del_init(&item->list);
/* We have no more items to process */ if (!item) goto done;
fm10k_mbx_lock(interface);
/* Check that we have plenty of space to send the message. We *wanttoensurethatthemailboxstayslowenoughtoavoida *changeinthehoststate,otherwisewemayseespurious *linkup/linkdownnotifications.
*/ if (!hw->mbx.ops.tx_ready(&hw->mbx, FM10K_VFMBX_MSG_MTU + 5)) {
hw->mbx.ops.process(hw, &hw->mbx);
set_bit(__FM10K_MACVLAN_REQUEST, interface->state);
fm10k_mbx_unlock(interface);
/* Put the request back on the list */
spin_lock_irqsave(&interface->macvlan_lock, flags);
list_add(&item->list, requests);
spin_unlock_irqrestore(&interface->macvlan_lock, flags); break;
}
switch (item->type) { case FM10K_MC_MAC_REQUEST:
hw->mac.ops.update_mc_addr(hw,
item->mac.glort,
item->mac.addr,
item->mac.vid,
item->set); break; case FM10K_UC_MAC_REQUEST:
hw->mac.ops.update_uc_addr(hw,
item->mac.glort,
item->mac.addr,
item->mac.vid,
item->set, 0); break; case FM10K_VLAN_REQUEST:
hw->mac.ops.update_vlan(hw,
item->vlan.vid,
item->vlan.vsi,
item->set); break; default: break;
}
fm10k_mbx_unlock(interface);
/* Free the item now that we've sent the update */
kfree(item);
} while (true);
/* flush memory to make sure state is correct */
smp_mb__before_atomic();
clear_bit(__FM10K_MACVLAN_SCHED, interface->state);
/* If a MAC/VLAN request was scheduled since we started, we should *re-schedule.However,thereisnoreasontore-scheduleifthereis *noworktodo.
*/ if (test_bit(__FM10K_MACVLAN_REQUEST, interface->state))
fm10k_macvlan_schedule(interface);
}
/* Setup the HW Tx Head and Tail descriptor pointers */ for (i = 0; i < interface->num_tx_queues; i++)
fm10k_configure_tx_ring(interface, interface->tx_ring[i]);
/* poll here to verify that Tx rings are now enabled */ for (i = 0; i < interface->num_tx_queues; i++)
fm10k_enable_tx_ring(interface, interface->tx_ring[i]);
}
/* Configure SWPRI to PC map */
fm10k_configure_swpri_map(interface);
/* Configure RSS and DGLORT map */
fm10k_configure_dglort(interface);
/* Setup the HW Rx Head and Tail descriptor pointers */ for (i = 0; i < interface->num_rx_queues; i++)
fm10k_configure_rx_ring(interface, interface->rx_ring[i]);
/* possible poll here to verify that Rx rings are now enabled */
}
staticvoid fm10k_napi_enable_all(struct fm10k_intfc *interface)
{ struct fm10k_q_vector *q_vector; int q_idx;
for (eicr &= FM10K_EICR_FAULT_MASK, type = FM10K_PCA_FAULT;
eicr;
eicr >>= 1, type += FM10K_FAULT_SIZE) { /* only check if there is an error reported */ if (!(eicr & 0x1)) continue;
/* retrieve fault info */
err = hw->mac.ops.get_fault(hw, type, &fault); if (err) {
dev_err(&interface->pdev->dev, "error reading fault\n"); continue;
}
/* unmask any set bits related to this interrupt */
eicr = fm10k_read_reg(hw, FM10K_EICR);
fm10k_write_reg(hw, FM10K_EICR, eicr & (FM10K_EICR_MAILBOX |
FM10K_EICR_SWITCHREADY |
FM10K_EICR_SWITCHNOTREADY));
/* report any faults found to the message log */
fm10k_report_fault(interface, eicr);
/* reset any queues disabled due to receiver overrun */
fm10k_reset_drop_on_empty(interface, eicr);
/* service mailboxes */ if (fm10k_mbx_trylock(interface)) {
s32 err = mbx->ops.process(hw, mbx);
if (err == FM10K_ERR_RESET_REQUESTED)
set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
/* if switch toggled state we should reset GLORTs */ if (eicr & FM10K_EICR_SWITCHNOTREADY) { /* force link down for at least 4 seconds */
interface->link_down_event = jiffies + (4 * HZ);
set_bit(__FM10K_LINK_DOWN, interface->state);
/* reset dglort_map back to no config */
hw->mac.dglort_map = FM10K_DGLORTMAP_NONE;
}
/* we should validate host state after interrupt event */
hw->mac.get_host_state = true;
/* validate host state, and handle VF mailboxes in the service task */
fm10k_service_event_schedule(interface);
/* MAC was changed so we need reset */ if (is_valid_ether_addr(hw->mac.perm_addr) &&
!ether_addr_equal(hw->mac.perm_addr, hw->mac.addr))
set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
/* VLAN override was changed, or default VLAN changed */ if ((vlan_override != hw->mac.vlan_override) ||
(default_vid != hw->mac.default_vid))
set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
err = fm10k_msg_err_pf(hw, results, mbx); if (!err && hw->swapi.status) { /* force link down for a reasonable delay */
interface->link_down_event = jiffies + (2 * HZ);
set_bit(__FM10K_LINK_DOWN, interface->state);
/* reset dglort_map back to no config */
hw->mac.dglort_map = FM10K_DGLORTMAP_NONE;
fm10k_service_event_schedule(interface);
/* prevent overloading kernel message buffer */ if (interface->lport_map_failed) return0;
interface->lport_map_failed = true;
if (hw->swapi.status == FM10K_MSG_ERR_PEP_NOT_SCHEDULED)
dev_warn(&interface->pdev->dev, "cannot obtain link because the host interface is configured for a PCIe host interface bandwidth of zero\n");
dev_warn(&interface->pdev->dev, "request logical port map failed: %d\n",
hw->swapi.status);
return0;
}
err = fm10k_msg_lport_map_pf(hw, results, mbx); if (err) return err;
interface->lport_map_failed = false;
/* we need to reset if port count was just updated */ if (dglort_map != hw->mac.dglort_map)
set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags);
/* capture stats one last time before stopping interface */
fm10k_update_stats(interface);
/* prevent updating statistics while we're down */ while (test_and_set_bit(__FM10K_UPDATING_STATS, interface->state))
usleep_range(1000, 2000);
/* skip waiting for TX DMA if we lost PCIe link */ if (FM10K_REMOVED(hw->hw_addr)) goto skip_tx_dma_drain;
/* In some rare circumstances it can take a while for Tx queues to *quiesceandbefullydisabled.Attemptto.stop_hw()first,and *thenifwegetERR_REQUESTS_PENDING,goaheadandwaitinaloop *untiltheTxqueueshaveemptied,oruntilanumberofretries.If *wefailtoclearwithintheretryloop,wewillissueawarning *indicatingthatTxDMAisprobablyhung.Notethismeanswecall *.stop_hw()twicebutthisshouldn'tcauseanyproblems.
*/
err = hw->mac.ops.stop_hw(hw); if (err != FM10K_ERR_REQUESTS_PENDING) goto skip_tx_dma_drain;
/* start checking at the last ring to have pending Tx */ for (; i < interface->num_tx_queues; i++) if (fm10k_get_tx_pending(interface->tx_ring[i], false)) break;
/* if all the queues are drained, we can break now */ if (i == interface->num_tx_queues) break;
}
if (count >= TX_DMA_DRAIN_RETRIES)
dev_err(&interface->pdev->dev, "Tx queues failed to drain after %d tries. Tx DMA is probably hung.\n",
count);
skip_tx_dma_drain: /* Disable DMA engine for Tx/Rx */
err = hw->mac.ops.stop_hw(hw); if (err == FM10K_ERR_REQUESTS_PENDING)
dev_err(&interface->pdev->dev, "due to pending requests hw was not shut down gracefully\n"); elseif (err)
dev_err(&interface->pdev->dev, "stop_hw failed: %d\n", err);
/* free any buffers still on the rings */
fm10k_clean_all_tx_rings(interface);
fm10k_clean_all_rx_rings(interface);
}
/* Setup IOV handlers */ if (fi->iov_ops)
memcpy(&hw->iov.ops, fi->iov_ops, sizeof(hw->iov.ops));
/* Set common capability flags and settings */
rss = min_t(int, FM10K_MAX_RSS_INDICES, num_online_cpus());
interface->ring_feature[RING_F_RSS].limit = rss;
fi->get_invariants(hw);
/* pick up the PCIe bus settings for reporting later */ if (hw->mac.ops.get_bus_info)
hw->mac.ops.get_bus_info(hw);
/* limit the usable DMA range */ if (hw->mac.ops.set_dma_mask)
hw->mac.ops.set_dma_mask(hw, dma_get_mask(&pdev->dev));
/* update netdev with DMA restrictions */ if (dma_get_mask(&pdev->dev) > DMA_BIT_MASK(32)) {
netdev->features |= NETIF_F_HIGHDMA;
netdev->vlan_features |= NETIF_F_HIGHDMA;
}
/* reset and initialize the hardware so it is in a known state */
err = hw->mac.ops.reset_hw(hw); if (err) {
dev_err(&pdev->dev, "reset_hw failed: %d\n", err); return err;
}
/* Set upper limit on IOV VFs that can be allocated */
pci_sriov_set_totalvfs(pdev, hw->iov.total_vfs);
/* Start with random Ethernet address */
eth_random_addr(hw->mac.addr);
/* Initialize MAC address from hardware */
err = hw->mac.ops.read_mac_addr(hw); if (err) {
dev_warn(&pdev->dev, "Failed to obtain MAC address defaulting to random\n"); /* tag address assignment as random */
netdev->addr_assign_type |= NET_ADDR_RANDOM;
}
err = fm10k_sw_init(interface, ent); if (err) goto err_sw_init;
/* enable debugfs support */
fm10k_dbg_intfc_init(interface);
err = fm10k_init_queueing_scheme(interface); if (err) goto err_sw_init;
/* the mbx interrupt might attempt to schedule the service task, so we *mustensureitisdisabledsincewehaven'tyetrequestedthetimer *orworkitem.
*/
set_bit(__FM10K_SERVICE_DISABLE, interface->state);
err = fm10k_mbx_request_irq(interface); if (err) goto err_mbx_interrupt;
/* final check of hardware state before registering the interface */
err = fm10k_hw_ready(interface); if (err) goto err_register;
err = register_netdev(netdev); if (err) goto err_register;
/* carrier off reporting is important to ethtool even BEFORE open */
netif_carrier_off(netdev);
/* stop all the transmit queues from transmitting until link is up */
netif_tx_stop_all_queues(netdev);
/* Initialize service timer and service task late in order to avoid *cleanupissues.
*/
timer_setup(&interface->service_timer, fm10k_service_timer, 0);
INIT_WORK(&interface->service_task, fm10k_service_task);
/* Setup the MAC/VLAN queue */
INIT_DELAYED_WORK(&interface->macvlan_task, fm10k_macvlan_task);
/* kick off service timer now, even when interface is down */
mod_timer(&interface->service_timer, (HZ * 2) + jiffies);
/* print warning for non-optimal configurations */
pcie_print_link_status(interface->pdev);
/* report MAC address for logging */
dev_info(&pdev->dev, "%pM\n", netdev->dev_addr);
/* enable SR-IOV after registering netdev to enforce PF/VF ordering */
fm10k_iov_configure(pdev, 0);
/* clear the service task disable bit and kick off service task */
clear_bit(__FM10K_SERVICE_DISABLE, interface->state);
fm10k_service_event_schedule(interface);
/* Even if we didn't properly prepare for reset in *fm10k_prepare_suspend,we'llattempttoresumeanyways.
*/ if (!test_and_clear_bit(__FM10K_RESET_SUSPENDED, interface->state))
dev_warn(&interface->pdev->dev, "Device was shut down as part of suspend... Attempting to recover\n");
err = fm10k_handle_reset(interface); if (err) return err;
/* assume host is not ready, to prevent race with watchdog in case we *actuallydon'thaveconnectiontotheswitch
*/
interface->host_ready = false;
fm10k_watchdog_host_not_ready(interface);
/* force link to stay down for a second to prevent link flutter */
interface->link_down_event = jiffies + (HZ);
set_bit(__FM10K_LINK_DOWN, interface->state);
/* restart the service task */
fm10k_start_service_event(interface);
/* Restart the MAC/VLAN request queue in-case of outstanding events */
fm10k_macvlan_schedule(interface);
/** *fm10k_io_reset_prepare-calledwhenPCIfunctionisabouttobereset *@pdev:PointertoPCIdevice * *ThiscallbackiscalledwhenthePCIfunctionisabouttobereset, *allowingthedevicedrivertoprepareforit.
*/ staticvoid fm10k_io_reset_prepare(struct pci_dev *pdev)
{ /* warn incase we have any active VF devices */ if (pci_num_vf(pdev))
dev_warn(&pdev->dev, "PCIe FLR may cause issues for any active VF devices\n");
fm10k_prepare_suspend(pci_get_drvdata(pdev));
}
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.