enic: add MBOX PF handlers for VF register and capability

Implement PF-side mailbox message processing for SR-IOV V2
admin channel communication.

When the PF receives messages from VFs, the dispatch routes
them to type-specific handlers:
  - VF_CAPABILITY_REQUEST: reply with protocol version 1
  - VF_REGISTER_REQUEST: send the register reply, mark the
    VF registered on success, then send PF_LINK_STATE_NOTIF
    reflecting the PF's current carrier state
  - VF_UNREGISTER_REQUEST: mark VF unregistered, send reply
  - PF_LINK_STATE_ACK: log errors from VF acknowledgment

Per-VF state (struct enic_vf_state) is tracked via enic->vf_state
which will be allocated when SRIOV V2 is enabled.

Remove the CONFIG_PCI_IOV guard from num_vfs in struct enic. The
PF handlers reference enic->num_vfs for VF ID bounds checking in
enic_mbox.c, which is compiled unconditionally. The field must be
visible regardless of CONFIG_PCI_IOV to avoid build failures.

Add enic_mbox_send_link_state() helper for PF-initiated link
state notifications.

Signed-off-by: Satish Kharat <satishkh@cisco.com>
Link: https://patch.msgid.link/20260812-enic-sriov-v2-admin-channel-v2-v13-7-b3809e448aba@cisco.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Satish Kharat
2026-08-12 05:48:07 -07:00
committed by Jakub Kicinski
parent 1f0c856b59
commit 06bdfb1621
3 changed files with 194 additions and 4 deletions

View File

@@ -256,9 +256,7 @@ struct enic {
struct enic_rx_coal rx_coalesce_setting;
u32 rx_coalesce_usecs;
u32 tx_coalesce_usecs;
#ifdef CONFIG_PCI_IOV
u16 num_vfs;
#endif
enum enic_vf_type vf_type;
unsigned int enable_count;
spinlock_t enic_api_lock;
@@ -315,6 +313,11 @@ struct enic {
/* MBOX protocol state — mbox_lock serializes admin WQ sends */
struct mutex mbox_lock;
u64 mbox_msg_num;
/* PF: per-VF MBOX state, allocated when SRIOV V2 is enabled */
struct enic_vf_state {
bool registered;
} *vf_state;
};
static inline struct net_device *vnic_get_netdev(struct vnic_dev *vdev)

View File

@@ -142,10 +142,183 @@ int enic_mbox_send_msg(struct enic *enic, u8 msg_type, u16 dst_vnic_id,
return err;
}
int enic_mbox_send_link_state(struct enic *enic, u16 vf_id, u32 link_state)
{
struct enic_mbox_pf_link_state_notif_msg notif = {};
if (!enic->vf_state || vf_id >= enic->num_vfs ||
!enic->vf_state[vf_id].registered) {
netdev_dbg(enic->netdev,
"MBOX: skip link state to unregistered VF %u\n",
vf_id);
return 0;
}
notif.link_state = cpu_to_le32(link_state);
return enic_mbox_send_msg(enic, ENIC_MBOX_PF_LINK_STATE_NOTIF, vf_id,
&notif, sizeof(notif));
}
static int enic_mbox_pf_handle_capability(struct enic *enic, void *msg,
u16 vf_id, u64 msg_num)
{
struct enic_mbox_vf_capability_reply_msg reply = {};
reply.reply.ret_major = cpu_to_le16(0);
reply.version = cpu_to_le32(ENIC_MBOX_CAP_VERSION_1);
return enic_mbox_send_msg(enic, ENIC_MBOX_VF_CAPABILITY_REPLY, vf_id,
&reply, sizeof(reply));
}
static int enic_mbox_pf_handle_register(struct enic *enic, void *msg,
u16 vf_id, u64 msg_num)
{
struct enic_mbox_vf_register_reply_msg reply = {};
u32 link_state;
int err;
if (!enic->vf_state || vf_id >= enic->num_vfs) {
if (net_ratelimit())
netdev_warn(enic->netdev,
"MBOX: register from invalid VF %u\n",
vf_id);
return -EINVAL;
}
/* VF re-registering (e.g. guest reboot without clean unregister):
* mark the previous registration inactive before accepting the new one.
*/
if (enic->vf_state[vf_id].registered) {
netdev_dbg(enic->netdev,
"MBOX: VF %u re-register, cleaning previous state\n",
vf_id);
enic->vf_state[vf_id].registered = false;
}
reply.reply.ret_major = cpu_to_le16(0);
err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_REGISTER_REPLY, vf_id,
&reply, sizeof(reply));
if (err)
return err;
enic->vf_state[vf_id].registered = true;
if (net_ratelimit())
netdev_info(enic->netdev, "VF %u registered via MBOX\n", vf_id);
link_state = netif_carrier_ok(enic->netdev) ?
ENIC_MBOX_LINK_STATE_ENABLE :
ENIC_MBOX_LINK_STATE_DISABLE;
err = enic_mbox_send_link_state(enic, vf_id, link_state);
if (err && net_ratelimit())
netdev_warn(enic->netdev,
"VF %u: failed to send initial link state: %d\n",
vf_id, err);
/* Registration succeeded; initial link state notification attempted
* above. Subsequent link state changes are sent from the PF
* when enic_link_check() detects carrier changes.
*/
return 0;
}
static int enic_mbox_pf_handle_unregister(struct enic *enic, void *msg,
u16 vf_id, u64 msg_num)
{
struct enic_mbox_vf_register_reply_msg reply = {};
int err;
if (!enic->vf_state || vf_id >= enic->num_vfs) {
if (net_ratelimit())
netdev_warn(enic->netdev,
"MBOX: unregister from invalid VF %u\n",
vf_id);
return -EINVAL;
}
/* VF is unloading; clear local state regardless of whether
* the reply is successfully delivered to avoid the PF treating
* a dead VF as still registered.
*/
enic->vf_state[vf_id].registered = false;
reply.reply.ret_major = cpu_to_le16(0);
err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_UNREGISTER_REPLY, vf_id,
&reply, sizeof(reply));
if (net_ratelimit())
netdev_info(enic->netdev,
"VF %u unregistered via MBOX\n", vf_id);
return err;
}
static void enic_mbox_pf_process_msg(struct enic *enic,
struct enic_mbox_hdr *hdr, void *payload)
{
u16 vf_id = le16_to_cpu(hdr->src_vnic_id);
u16 msg_len = le16_to_cpu(hdr->msg_len);
int err = 0;
if (!enic->vf_state) {
netdev_dbg(enic->netdev,
"MBOX: PF received msg but SRIOV not active\n");
return;
}
if (vf_id >= enic->num_vfs) {
if (net_ratelimit())
netdev_warn(enic->netdev,
"MBOX: PF received msg from invalid VF %u\n",
vf_id);
return;
}
switch (hdr->msg_type) {
case ENIC_MBOX_VF_CAPABILITY_REQUEST:
err = enic_mbox_pf_handle_capability(enic, payload, vf_id,
le64_to_cpu(hdr->msg_num));
break;
case ENIC_MBOX_VF_REGISTER_REQUEST:
err = enic_mbox_pf_handle_register(enic, payload, vf_id,
le64_to_cpu(hdr->msg_num));
break;
case ENIC_MBOX_VF_UNREGISTER_REQUEST:
err = enic_mbox_pf_handle_unregister(enic, payload, vf_id,
le64_to_cpu(hdr->msg_num));
break;
case ENIC_MBOX_PF_LINK_STATE_ACK: {
struct enic_mbox_pf_link_state_ack_msg *ack = payload;
if (msg_len < sizeof(*hdr) + sizeof(*ack))
break;
if (le16_to_cpu(ack->ack.ret_major) && net_ratelimit())
netdev_warn(enic->netdev,
"MBOX: VF %u link state ACK error %u/%u\n",
vf_id,
le16_to_cpu(ack->ack.ret_major),
le16_to_cpu(ack->ack.ret_minor));
break;
}
default:
netdev_dbg(enic->netdev,
"MBOX: PF unhandled msg type %u from VF %u\n",
hdr->msg_type, vf_id);
err = -EOPNOTSUPP;
break;
}
if (err && net_ratelimit())
netdev_warn(enic->netdev,
"MBOX: PF handler for msg type %u from VF %u failed: %d\n",
hdr->msg_type, vf_id, err);
}
static void enic_mbox_recv_handler(struct enic *enic, void *buf,
unsigned int len)
{
struct enic_mbox_hdr *hdr = buf;
void *payload;
u16 msg_len;
if (len < sizeof(*hdr)) {
if (net_ratelimit())
@@ -163,10 +336,23 @@ static void enic_mbox_recv_handler(struct enic *enic, void *buf,
return;
}
msg_len = le16_to_cpu(hdr->msg_len);
if (msg_len < sizeof(*hdr) || msg_len > len) {
if (net_ratelimit())
netdev_warn(enic->netdev,
"MBOX: invalid msg_len %u (buf len %u)\n",
msg_len, len);
return;
}
netdev_dbg(enic->netdev,
"MBOX recv: type %u from vnic %u len %u\n",
hdr->msg_type, le16_to_cpu(hdr->src_vnic_id),
le16_to_cpu(hdr->msg_len));
hdr->msg_type, le16_to_cpu(hdr->src_vnic_id), msg_len);
payload = buf + sizeof(*hdr);
if (enic->vf_state)
enic_mbox_pf_process_msg(enic, hdr, payload);
}
void enic_mbox_init(struct enic *enic)

View File

@@ -87,5 +87,6 @@ struct enic;
void enic_mbox_init(struct enic *enic);
int enic_mbox_send_msg(struct enic *enic, u8 msg_type, u16 dst_vnic_id,
void *payload, u16 payload_len);
int enic_mbox_send_link_state(struct enic *enic, u16 vf_id, u32 link_state);
#endif /* _ENIC_MBOX_H_ */