mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 03:35:32 -04:00
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:
committed by
Jakub Kicinski
parent
1f0c856b59
commit
06bdfb1621
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
¬if, 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)
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
Reference in New Issue
Block a user