Skip to content

Commit 682a612

Browse files
ahduyckPaolo Abeni
authored andcommitted
fbnic: Add additional handling of IRQs
We have two issues that need to be addressed in our IRQ handling. One is the fact that we can end up double-freeing IRQs in the event of an exception handling error such as a PCIe reset/recovery that fails. To prevent that from becoming an issue we can use the msix_vector values to indicate that we have successfully requested/freed the IRQ by only setting or clearing them when we have completed the given action. The other issue is that we have several potential races in our IRQ path due to us manipulating the mask before the vector has been truly disabled. In order to handle that in the case of the FW mailbox we need to not auto-enable the IRQ and instead will be enabling/disabling it separately. In the case of the PCS vector we can mitigate this by unmapping it and synchronizing the IRQ before we clear the mask. The general order of operations after this change is now to request the interrupt, poll the FW mailbox to ready, and then enable the interrupt. For the shutdown we do the reverse where we disable the interrupt, flush any pending Tx, and then free the IRQ. I am renaming the enable/disable to request/free to be equivilent with the IRQ calls being used. We may see additions in the future to enable/disable the IRQs versus request/free them for certain use cases. Fixes: da3cde0 ("eth: fbnic: Add FW communication mechanism") Fixes: 6968437 ("eth: fbnic: Add link detection") Signed-off-by: Alexander Duyck <alexanderduyck@fb.com> Reviewed-by: Simon Horman <horms@kernel.org> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com> Link: https://patch.msgid.link/174654719271.499179.3634535105127848325.stgit@ahduyck-xeon-server.home.arpa Reviewed-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
1 parent 3b12f00 commit 682a612

File tree

4 files changed

+110
-59
lines changed

4 files changed

+110
-59
lines changed

drivers/net/ethernet/meta/fbnic/fbnic.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,14 @@ struct fbnic_dev *fbnic_devlink_alloc(struct pci_dev *pdev);
154154
void fbnic_devlink_register(struct fbnic_dev *fbd);
155155
void fbnic_devlink_unregister(struct fbnic_dev *fbd);
156156

157-
int fbnic_fw_enable_mbx(struct fbnic_dev *fbd);
158-
void fbnic_fw_disable_mbx(struct fbnic_dev *fbd);
157+
int fbnic_fw_request_mbx(struct fbnic_dev *fbd);
158+
void fbnic_fw_free_mbx(struct fbnic_dev *fbd);
159159

160160
void fbnic_hwmon_register(struct fbnic_dev *fbd);
161161
void fbnic_hwmon_unregister(struct fbnic_dev *fbd);
162162

163-
int fbnic_pcs_irq_enable(struct fbnic_dev *fbd);
164-
void fbnic_pcs_irq_disable(struct fbnic_dev *fbd);
163+
int fbnic_pcs_request_irq(struct fbnic_dev *fbd);
164+
void fbnic_pcs_free_irq(struct fbnic_dev *fbd);
165165

166166
void fbnic_napi_name_irqs(struct fbnic_dev *fbd);
167167
int fbnic_napi_request_irq(struct fbnic_dev *fbd,

drivers/net/ethernet/meta/fbnic/fbnic_irq.c

Lines changed: 96 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,69 +19,105 @@ static irqreturn_t fbnic_fw_msix_intr(int __always_unused irq, void *data)
1919
return IRQ_HANDLED;
2020
}
2121

22+
static int __fbnic_fw_enable_mbx(struct fbnic_dev *fbd, int vector)
23+
{
24+
int err;
25+
26+
/* Initialize mailbox and attempt to poll it into ready state */
27+
fbnic_mbx_init(fbd);
28+
err = fbnic_mbx_poll_tx_ready(fbd);
29+
if (err) {
30+
dev_warn(fbd->dev, "FW mailbox did not enter ready state\n");
31+
return err;
32+
}
33+
34+
/* Enable interrupt and unmask the vector */
35+
enable_irq(vector);
36+
fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_FW_MSIX_ENTRY);
37+
38+
return 0;
39+
}
40+
2241
/**
23-
* fbnic_fw_enable_mbx - Configure and initialize Firmware Mailbox
42+
* fbnic_fw_request_mbx - Configure and initialize Firmware Mailbox
2443
* @fbd: Pointer to device to initialize
2544
*
26-
* This function will initialize the firmware mailbox rings, enable the IRQ
27-
* and initialize the communication between the Firmware and the host. The
28-
* firmware is expected to respond to the initialization by sending an
29-
* interrupt essentially notifying the host that it has seen the
30-
* initialization and is now synced up.
45+
* This function will allocate the IRQ and then reinitialize the mailbox
46+
* starting communication between the host and firmware.
3147
*
3248
* Return: non-zero on failure.
3349
**/
34-
int fbnic_fw_enable_mbx(struct fbnic_dev *fbd)
50+
int fbnic_fw_request_mbx(struct fbnic_dev *fbd)
3551
{
36-
u32 vector = fbd->fw_msix_vector;
37-
int err;
52+
struct pci_dev *pdev = to_pci_dev(fbd->dev);
53+
int vector, err;
54+
55+
WARN_ON(fbd->fw_msix_vector);
56+
57+
vector = pci_irq_vector(pdev, FBNIC_FW_MSIX_ENTRY);
58+
if (vector < 0)
59+
return vector;
3860

3961
/* Request the IRQ for FW Mailbox vector. */
4062
err = request_threaded_irq(vector, NULL, &fbnic_fw_msix_intr,
41-
IRQF_ONESHOT, dev_name(fbd->dev), fbd);
63+
IRQF_ONESHOT | IRQF_NO_AUTOEN,
64+
dev_name(fbd->dev), fbd);
4265
if (err)
4366
return err;
4467

4568
/* Initialize mailbox and attempt to poll it into ready state */
46-
fbnic_mbx_init(fbd);
47-
err = fbnic_mbx_poll_tx_ready(fbd);
48-
if (err) {
49-
dev_warn(fbd->dev, "FW mailbox did not enter ready state\n");
69+
err = __fbnic_fw_enable_mbx(fbd, vector);
70+
if (err)
5071
free_irq(vector, fbd);
51-
return err;
52-
}
5372

54-
/* Enable interrupts */
55-
fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_FW_MSIX_ENTRY);
73+
fbd->fw_msix_vector = vector;
5674

57-
return 0;
75+
return err;
5876
}
5977

6078
/**
61-
* fbnic_fw_disable_mbx - Disable mailbox and place it in standby state
62-
* @fbd: Pointer to device to disable
79+
* fbnic_fw_disable_mbx - Temporarily place mailbox in standby state
80+
* @fbd: Pointer to device
6381
*
64-
* This function will disable the mailbox interrupt, free any messages still
65-
* in the mailbox and place it into a standby state. The firmware is
66-
* expected to see the update and assume that the host is in the reset state.
82+
* Shutdown the mailbox by notifying the firmware to stop sending us logs, mask
83+
* and synchronize the IRQ, and then clean up the rings.
6784
**/
68-
void fbnic_fw_disable_mbx(struct fbnic_dev *fbd)
85+
static void fbnic_fw_disable_mbx(struct fbnic_dev *fbd)
6986
{
70-
/* Disable interrupt and free vector */
71-
fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_FW_MSIX_ENTRY);
87+
/* Disable interrupt and synchronize the IRQ */
88+
disable_irq(fbd->fw_msix_vector);
7289

73-
/* Free the vector */
74-
free_irq(fbd->fw_msix_vector, fbd);
90+
/* Mask the vector */
91+
fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_FW_MSIX_ENTRY);
7592

7693
/* Make sure disabling logs message is sent, must be done here to
7794
* avoid risk of completing without a running interrupt.
7895
*/
7996
fbnic_mbx_flush_tx(fbd);
80-
81-
/* Reset the mailboxes to the initialized state */
8297
fbnic_mbx_clean(fbd);
8398
}
8499

100+
/**
101+
* fbnic_fw_free_mbx - Disable mailbox and place it in standby state
102+
* @fbd: Pointer to device to disable
103+
*
104+
* This function will disable the mailbox interrupt, free any messages still
105+
* in the mailbox and place it into a disabled state. The firmware is
106+
* expected to see the update and assume that the host is in the reset state.
107+
**/
108+
void fbnic_fw_free_mbx(struct fbnic_dev *fbd)
109+
{
110+
/* Vector has already been freed */
111+
if (!fbd->fw_msix_vector)
112+
return;
113+
114+
fbnic_fw_disable_mbx(fbd);
115+
116+
/* Free the vector */
117+
free_irq(fbd->fw_msix_vector, fbd);
118+
fbd->fw_msix_vector = 0;
119+
}
120+
85121
static irqreturn_t fbnic_pcs_msix_intr(int __always_unused irq, void *data)
86122
{
87123
struct fbnic_dev *fbd = data;
@@ -101,49 +137,69 @@ static irqreturn_t fbnic_pcs_msix_intr(int __always_unused irq, void *data)
101137
}
102138

103139
/**
104-
* fbnic_pcs_irq_enable - Configure the MAC to enable it to advertise link
140+
* fbnic_pcs_request_irq - Configure the PCS to enable it to advertise link
105141
* @fbd: Pointer to device to initialize
106142
*
107143
* This function provides basic bringup for the MAC/PCS IRQ. For now the IRQ
108144
* will remain disabled until we start the MAC/PCS/PHY logic via phylink.
109145
*
110146
* Return: non-zero on failure.
111147
**/
112-
int fbnic_pcs_irq_enable(struct fbnic_dev *fbd)
148+
int fbnic_pcs_request_irq(struct fbnic_dev *fbd)
113149
{
114-
u32 vector = fbd->pcs_msix_vector;
115-
int err;
150+
struct pci_dev *pdev = to_pci_dev(fbd->dev);
151+
int vector, err;
116152

117-
/* Request the IRQ for MAC link vector.
118-
* Map MAC cause to it, and unmask it
153+
WARN_ON(fbd->pcs_msix_vector);
154+
155+
vector = pci_irq_vector(pdev, FBNIC_PCS_MSIX_ENTRY);
156+
if (vector < 0)
157+
return vector;
158+
159+
/* Request the IRQ for PCS link vector.
160+
* Map PCS cause to it, and unmask it
119161
*/
120162
err = request_irq(vector, &fbnic_pcs_msix_intr, 0,
121163
fbd->netdev->name, fbd);
122164
if (err)
123165
return err;
124166

167+
/* Map and enable interrupt, unmask vector after link is configured */
125168
fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
126169
FBNIC_PCS_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE);
127170

171+
fbd->pcs_msix_vector = vector;
172+
128173
return 0;
129174
}
130175

131176
/**
132-
* fbnic_pcs_irq_disable - Teardown the MAC IRQ to prepare for stopping
177+
* fbnic_pcs_free_irq - Teardown the PCS IRQ to prepare for stopping
133178
* @fbd: Pointer to device that is stopping
134179
*
135-
* This function undoes the work done in fbnic_pcs_irq_enable and prepares
180+
* This function undoes the work done in fbnic_pcs_request_irq and prepares
136181
* the device to no longer receive traffic on the host interface.
137182
**/
138-
void fbnic_pcs_irq_disable(struct fbnic_dev *fbd)
183+
void fbnic_pcs_free_irq(struct fbnic_dev *fbd)
139184
{
185+
/* Vector has already been freed */
186+
if (!fbd->pcs_msix_vector)
187+
return;
188+
140189
/* Disable interrupt */
141190
fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
142191
FBNIC_PCS_MSIX_ENTRY);
192+
fbnic_wrfl(fbd);
193+
194+
/* Synchronize IRQ to prevent race that would unmask vector */
195+
synchronize_irq(fbd->pcs_msix_vector);
196+
197+
/* Mask the vector */
143198
fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_PCS_MSIX_ENTRY);
144199

145200
/* Free the vector */
146201
free_irq(fbd->pcs_msix_vector, fbd);
202+
fbd->pcs_msix_vector = 0;
147203
}
148204

149205
void fbnic_synchronize_irq(struct fbnic_dev *fbd, int nr)
@@ -226,9 +282,6 @@ void fbnic_free_irqs(struct fbnic_dev *fbd)
226282
{
227283
struct pci_dev *pdev = to_pci_dev(fbd->dev);
228284

229-
fbd->pcs_msix_vector = 0;
230-
fbd->fw_msix_vector = 0;
231-
232285
fbd->num_irqs = 0;
233286

234287
pci_free_irq_vectors(pdev);
@@ -254,8 +307,5 @@ int fbnic_alloc_irqs(struct fbnic_dev *fbd)
254307

255308
fbd->num_irqs = num_irqs;
256309

257-
fbd->pcs_msix_vector = pci_irq_vector(pdev, FBNIC_PCS_MSIX_ENTRY);
258-
fbd->fw_msix_vector = pci_irq_vector(pdev, FBNIC_FW_MSIX_ENTRY);
259-
260310
return 0;
261311
}

drivers/net/ethernet/meta/fbnic/fbnic_netdev.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ int __fbnic_open(struct fbnic_net *fbn)
4444
if (err)
4545
goto time_stop;
4646

47-
err = fbnic_pcs_irq_enable(fbd);
47+
err = fbnic_pcs_request_irq(fbd);
4848
if (err)
4949
goto time_stop;
50+
5051
/* Pull the BMC config and initialize the RPC */
5152
fbnic_bmc_rpc_init(fbd);
5253
fbnic_rss_reinit(fbd, fbn);
@@ -82,7 +83,7 @@ static int fbnic_stop(struct net_device *netdev)
8283
struct fbnic_net *fbn = netdev_priv(netdev);
8384

8485
fbnic_down(fbn);
85-
fbnic_pcs_irq_disable(fbn->fbd);
86+
fbnic_pcs_free_irq(fbn->fbd);
8687

8788
fbnic_time_stop(fbn);
8889
fbnic_fw_xmit_ownership_msg(fbn->fbd, false);

drivers/net/ethernet/meta/fbnic/fbnic_pci.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
283283
goto free_irqs;
284284
}
285285

286-
err = fbnic_fw_enable_mbx(fbd);
286+
err = fbnic_fw_request_mbx(fbd);
287287
if (err) {
288288
dev_err(&pdev->dev,
289289
"Firmware mailbox initialization failure\n");
@@ -363,7 +363,7 @@ static void fbnic_remove(struct pci_dev *pdev)
363363
fbnic_hwmon_unregister(fbd);
364364
fbnic_dbg_fbd_exit(fbd);
365365
fbnic_devlink_unregister(fbd);
366-
fbnic_fw_disable_mbx(fbd);
366+
fbnic_fw_free_mbx(fbd);
367367
fbnic_free_irqs(fbd);
368368

369369
fbnic_devlink_free(fbd);
@@ -387,7 +387,7 @@ static int fbnic_pm_suspend(struct device *dev)
387387
rtnl_unlock();
388388

389389
null_uc_addr:
390-
fbnic_fw_disable_mbx(fbd);
390+
fbnic_fw_free_mbx(fbd);
391391

392392
/* Free the IRQs so they aren't trying to occupy sleeping CPUs */
393393
fbnic_free_irqs(fbd);
@@ -420,7 +420,7 @@ static int __fbnic_pm_resume(struct device *dev)
420420
fbd->mac->init_regs(fbd);
421421

422422
/* Re-enable mailbox */
423-
err = fbnic_fw_enable_mbx(fbd);
423+
err = fbnic_fw_request_mbx(fbd);
424424
if (err)
425425
goto err_free_irqs;
426426

@@ -438,15 +438,15 @@ static int __fbnic_pm_resume(struct device *dev)
438438
if (netif_running(netdev)) {
439439
err = __fbnic_open(fbn);
440440
if (err)
441-
goto err_disable_mbx;
441+
goto err_free_mbx;
442442
}
443443

444444
rtnl_unlock();
445445

446446
return 0;
447-
err_disable_mbx:
447+
err_free_mbx:
448448
rtnl_unlock();
449-
fbnic_fw_disable_mbx(fbd);
449+
fbnic_fw_free_mbx(fbd);
450450
err_free_irqs:
451451
fbnic_free_irqs(fbd);
452452
err_invalidate_uc_addr:

0 commit comments

Comments
 (0)