[PATCH net-next 2/6] enic: serialize V2 VF mailbox requests

From: Satish Kharat

Date: Mon Sep 21 2026 - 16:10:22 EST


The VF stores one expected reply and uses one completion for mailbox
requests. If two VF control paths issue requests at the same time, the
second request can replace the reply state for the first.

Add a request mutex used only by the VF. Hold it from before a request is
armed until its reply or timeout has been consumed. Recheck VF registration
after taking the mutex and clear pending state on send failure so a later
request cannot inherit it.

PF-side request processing is unchanged. Unsolicited PF notifications and
their acknowledgments remain asynchronous.

Assisted-by: LLM
Signed-off-by: Satish Kharat <satishkh@xxxxxxxxx>
---
drivers/net/ethernet/cisco/enic/enic.h | 1 +
drivers/net/ethernet/cisco/enic/enic_mbox.c | 49 +++++++++++++++++++++++++++--
2 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h
index 7a509a056990..3945fe28f199 100644
--- a/drivers/net/ethernet/cisco/enic/enic.h
+++ b/drivers/net/ethernet/cisco/enic/enic.h
@@ -335,6 +335,7 @@ struct enic {
* the requester.
*/
struct completion mbox_comp;
+ struct mutex vf_mbox_request_lock; /* serializes VF request lifetimes */
spinlock_t mbox_state_lock; /* protects expected reply state */
u64 mbox_expected_msg_num;
u8 mbox_expected_reply;
diff --git a/drivers/net/ethernet/cisco/enic/enic_mbox.c b/drivers/net/ethernet/cisco/enic/enic_mbox.c
index 5c93ca49552a..b8a18d9682b2 100644
--- a/drivers/net/ethernet/cisco/enic/enic_mbox.c
+++ b/drivers/net/ethernet/cisco/enic/enic_mbox.c
@@ -218,6 +218,32 @@ static int enic_mbox_wait_reply(struct enic *enic, unsigned long timeout_ms)
return err;
}

+static void enic_mbox_vf_request_start(struct enic *enic)
+{
+ mutex_lock(&enic->vf_mbox_request_lock);
+ reinit_completion(&enic->mbox_comp);
+ spin_lock_bh(&enic->mbox_state_lock);
+ enic->mbox_expected_msg_num = 0;
+ enic->mbox_expected_reply = 0;
+ spin_unlock_bh(&enic->mbox_state_lock);
+}
+
+static void enic_mbox_vf_request_abort(struct enic *enic)
+{
+ lockdep_assert_held(&enic->vf_mbox_request_lock);
+ spin_lock_bh(&enic->mbox_state_lock);
+ enic->mbox_expected_reply = 0;
+ enic->mbox_expected_msg_num = 0;
+ spin_unlock_bh(&enic->mbox_state_lock);
+ mutex_unlock(&enic->vf_mbox_request_lock);
+}
+
+static void enic_mbox_vf_request_finish(struct enic *enic)
+{
+ lockdep_assert_held(&enic->vf_mbox_request_lock);
+ mutex_unlock(&enic->vf_mbox_request_lock);
+}
+
int enic_mbox_send_link_state(struct enic *enic, u16 vf_id, u32 link_state)
{
struct enic_mbox_pf_link_state_notif_msg notif = {};
@@ -623,6 +649,7 @@ int enic_mbox_vf_capability_check(struct enic *enic)
u32 version;
int err;

+ enic_mbox_vf_request_start(enic);
WRITE_ONCE(enic->pf_cap_version, 0);
req.version = cpu_to_le32(ENIC_MBOX_CAP_VERSION_1);

@@ -630,11 +657,14 @@ int enic_mbox_vf_capability_check(struct enic *enic)
ENIC_MBOX_VF_CAPABILITY_REQUEST,
ENIC_MBOX_VF_CAPABILITY_REPLY,
&req, sizeof(req));
- if (err)
+ if (err) {
+ enic_mbox_vf_request_abort(enic);
return err;
+ }

err = enic_mbox_wait_reply(enic, 3000);
version = READ_ONCE(enic->pf_cap_version);
+ enic_mbox_vf_request_finish(enic);
if (err) {
netdev_warn(enic->netdev,
"MBOX: no capability reply from PF\n");
@@ -656,15 +686,19 @@ int enic_mbox_vf_register(struct enic *enic)
bool registered;
int err;

+ enic_mbox_vf_request_start(enic);
WRITE_ONCE(enic->vf_registered, false);

err = enic_mbox_vf_send_request(enic, ENIC_MBOX_VF_REGISTER_REQUEST,
ENIC_MBOX_VF_REGISTER_REPLY, NULL, 0);
- if (err)
+ if (err) {
+ enic_mbox_vf_request_abort(enic);
return err;
+ }

err = enic_mbox_wait_reply(enic, 3000);
registered = READ_ONCE(enic->vf_registered);
+ enic_mbox_vf_request_finish(enic);
if (err) {
netdev_warn(enic->netdev,
"MBOX: VF registration with PF timed out\n");
@@ -684,16 +718,24 @@ int enic_mbox_vf_unregister(struct enic *enic)

if (!READ_ONCE(enic->vf_registered))
return 0;
+ enic_mbox_vf_request_start(enic);
+ if (!READ_ONCE(enic->vf_registered)) {
+ enic_mbox_vf_request_finish(enic);
+ return 0;
+ }

err = enic_mbox_vf_send_request(enic,
ENIC_MBOX_VF_UNREGISTER_REQUEST,
ENIC_MBOX_VF_UNREGISTER_REPLY,
NULL, 0);
- if (err)
+ if (err) {
+ enic_mbox_vf_request_abort(enic);
return err;
+ }

err = enic_mbox_wait_reply(enic, 3000);
registered = READ_ONCE(enic->vf_registered);
+ enic_mbox_vf_request_finish(enic);
if (err)
return err;
if (registered)
@@ -711,6 +753,7 @@ void enic_mbox_init(struct enic *enic)
*/
if (!reinit) {
mutex_init(&enic->mbox_lock);
+ mutex_init(&enic->vf_mbox_request_lock);
init_completion(&enic->mbox_comp);
spin_lock_init(&enic->mbox_state_lock);
enic->mbox_msg_num = 0;

--
2.43.0