octeontx2-pf: Add support to sync link state between representor and VFs
authorGeetha sowjanya <gakula@marvell.com>
Thu, 7 Nov 2024 16:08:33 +0000 (21:38 +0530)
committerDavid S. Miller <davem@davemloft.net>
Wed, 13 Nov 2024 11:57:11 +0000 (11:57 +0000)
Implements the below requirement mentioned
in the representors documentation.

"
The representee's link state is controlled through the
representor. Setting the representor administratively UP
or DOWN should cause carrier ON or OFF at the representee.
"

This patch enables
- Reflecting the link state of representor based on the VF state and
 link state of VF based on representor.
- On VF interface up/down a notification is sent via mbox to representor
  to update the link state.
  eg: ip link set eth0 up/down  will disable carrier on/off
       of the corresponding representor(r0p1) interface.
- On representor interface up/down will cause the link state update of VF.
  eg: ip link set r0p1 up/down  will disable carrier on/off
       of the corresponding representee(eth0) interface.

Signed-off-by: Harman Kalra <hkalra@marvell.com>
Signed-off-by: Geetha sowjanya <gakula@marvell.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/marvell/octeontx2/af/mbox.h
drivers/net/ethernet/marvell/octeontx2/af/rvu.h
drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
drivers/net/ethernet/marvell/octeontx2/nic/rep.c
drivers/net/ethernet/marvell/octeontx2/nic/rep.h

index 8fd4b58..b583c96 100644 (file)
@@ -146,6 +146,7 @@ M(SET_VF_PERM,              0x00b, set_vf_perm, set_vf_perm, msg_rsp)       \
 M(PTP_GET_CAP,         0x00c, ptp_get_cap, msg_req, ptp_get_cap_rsp)   \
 M(GET_REP_CNT,         0x00d, get_rep_cnt, msg_req, get_rep_cnt_rsp)   \
 M(ESW_CFG,             0x00e, esw_cfg, esw_cfg_req, msg_rsp)   \
+M(REP_EVENT_NOTIFY,     0x00f, rep_event_notify, rep_event, msg_rsp) \
 /* CGX mbox IDs (range 0x200 - 0x3FF) */                               \
 M(CGX_START_RXTX,      0x200, cgx_start_rxtx, msg_req, msg_rsp)        \
 M(CGX_STOP_RXTX,       0x201, cgx_stop_rxtx, msg_req, msg_rsp)         \
@@ -383,12 +384,16 @@ M(CPT_INST_LMTST, 0xD00, cpt_inst_lmtst, cpt_inst_lmtst_req, msg_rsp)
 #define MBOX_UP_MCS_MESSAGES                                           \
 M(MCS_INTR_NOTIFY,     0xE00, mcs_intr_notify, mcs_intr_info, msg_rsp)
 
+#define MBOX_UP_REP_MESSAGES                                           \
+M(REP_EVENT_UP_NOTIFY, 0xEF0, rep_event_up_notify, rep_event, msg_rsp) \
+
 enum {
 #define M(_name, _id, _1, _2, _3) MBOX_MSG_ ## _name = _id,
 MBOX_MESSAGES
 MBOX_UP_CGX_MESSAGES
 MBOX_UP_CPT_MESSAGES
 MBOX_UP_MCS_MESSAGES
+MBOX_UP_REP_MESSAGES
 #undef M
 };
 
@@ -1572,6 +1577,26 @@ struct esw_cfg_req {
        u64 rsvd;
 };
 
+struct rep_evt_data {
+       u8 port_state;
+       u8 vf_state;
+       u16 rx_mode;
+       u16 rx_flags;
+       u16 mtu;
+       u64 rsvd[5];
+};
+
+struct rep_event {
+       struct mbox_msghdr hdr;
+       u16 pcifunc;
+#define RVU_EVENT_PORT_STATE           BIT_ULL(0)
+#define RVU_EVENT_PFVF_STATE           BIT_ULL(1)
+#define RVU_EVENT_MTU_CHANGE           BIT_ULL(2)
+#define RVU_EVENT_RX_MODE_CHANGE       BIT_ULL(3)
+       u16 event;
+       struct rep_evt_data evt_data;
+};
+
 struct flow_msg {
        unsigned char dmac[6];
        unsigned char smac[6];
index a59f757..b897845 100644 (file)
@@ -513,6 +513,11 @@ struct rvu_switch {
        u16 start_entry;
 };
 
+struct rep_evtq_ent {
+       struct list_head node;
+       struct rep_event event;
+};
+
 struct rvu {
        void __iomem            *afreg_base;
        void __iomem            *pfreg_base;
@@ -599,6 +604,11 @@ struct rvu {
        int                     rep_cnt;
        u16                     *rep2pfvf_map;
        u8                      rep_mode;
+       struct                  work_struct rep_evt_work;
+       struct                  workqueue_struct *rep_evt_wq;
+       struct list_head        rep_evtq_head;
+       /* Representor event lock */
+       spinlock_t              rep_evtq_lock;
 };
 
 static inline void rvu_write64(struct rvu *rvu, u64 block, u64 offset, u64 val)
@@ -1081,4 +1091,5 @@ void rvu_mcs_exit(struct rvu *rvu);
 int rvu_rep_pf_init(struct rvu *rvu);
 int rvu_rep_install_mcam_rules(struct rvu *rvu);
 void rvu_rep_update_rules(struct rvu *rvu, u16 pcifunc, bool ena);
+int rvu_rep_notify_pfvf_state(struct rvu *rvu, u16 pcifunc, bool enable);
 #endif /* RVU_H */
index 673e499..26cf42a 100644 (file)
@@ -363,7 +363,6 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf,
 
                cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind);
                rvu_npc_set_pkind(rvu, pkind, pfvf);
-
                break;
        case NIX_INTF_TYPE_LBK:
                vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1;
@@ -5180,7 +5179,7 @@ int rvu_mbox_handler_nix_lf_start_rx(struct rvu *rvu, struct msg_req *req,
 {
        u16 pcifunc = req->hdr.pcifunc;
        struct rvu_pfvf *pfvf;
-       int nixlf, err;
+       int nixlf, err, pf;
 
        err = nix_get_nixlf(rvu, pcifunc, &nixlf, NULL);
        if (err)
@@ -5198,6 +5197,10 @@ int rvu_mbox_handler_nix_lf_start_rx(struct rvu *rvu, struct msg_req *req,
 
        rvu_switch_update_rules(rvu, pcifunc, true);
 
+       pf = rvu_get_pf(pcifunc);
+       if (is_pf_cgxmapped(rvu, pf) && rvu->rep_mode)
+               rvu_rep_notify_pfvf_state(rvu, pcifunc, true);
+
        return rvu_cgx_start_stop_io(rvu, pcifunc, true);
 }
 
@@ -5206,7 +5209,7 @@ int rvu_mbox_handler_nix_lf_stop_rx(struct rvu *rvu, struct msg_req *req,
 {
        u16 pcifunc = req->hdr.pcifunc;
        struct rvu_pfvf *pfvf;
-       int nixlf, err;
+       int nixlf, err, pf;
 
        err = nix_get_nixlf(rvu, pcifunc, &nixlf, NULL);
        if (err)
@@ -5227,6 +5230,9 @@ int rvu_mbox_handler_nix_lf_stop_rx(struct rvu *rvu, struct msg_req *req,
        rvu_switch_update_rules(rvu, pcifunc, false);
        rvu_cgx_tx_enable(rvu, pcifunc, true);
 
+       pf = rvu_get_pf(pcifunc);
+       if (is_pf_cgxmapped(rvu, pf) && rvu->rep_mode)
+               rvu_rep_notify_pfvf_state(rvu, pcifunc, false);
        return 0;
 }
 
@@ -5254,6 +5260,9 @@ void rvu_nix_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int nixlf)
 
        clear_bit(NIXLF_INITIALIZED, &pfvf->flags);
 
+       if (is_pf_cgxmapped(rvu, pf) && rvu->rep_mode)
+               rvu_rep_notify_pfvf_state(rvu, pcifunc, false);
+
        rvu_cgx_start_stop_io(rvu, pcifunc, false);
 
        if (pfvf->sq_ctx) {
index 9ac663e..80947fa 100644 (file)
 #include "rvu.h"
 #include "rvu_reg.h"
 
+#define M(_name, _id, _fn_name, _req_type, _rsp_type)                  \
+static struct _req_type __maybe_unused                                 \
+*otx2_mbox_alloc_msg_ ## _fn_name(struct rvu *rvu, int devid)          \
+{                                                                      \
+       struct _req_type *req;                                          \
+                                                                       \
+       req = (struct _req_type *)otx2_mbox_alloc_msg_rsp(              \
+               &rvu->afpf_wq_info.mbox_up, devid, sizeof(struct _req_type), \
+               sizeof(struct _rsp_type));                              \
+       if (!req)                                                       \
+               return NULL;                                            \
+       req->hdr.sig = OTX2_MBOX_REQ_SIG;                               \
+       req->hdr.id = _id;                                              \
+       return req;                                                     \
+}
+
+MBOX_UP_REP_MESSAGES
+#undef M
+
+static int rvu_rep_up_notify(struct rvu *rvu, struct rep_event *event)
+{
+       struct rep_event *msg;
+       int pf;
+
+       pf = rvu_get_pf(event->pcifunc);
+
+       mutex_lock(&rvu->mbox_lock);
+       msg = otx2_mbox_alloc_msg_rep_event_up_notify(rvu, pf);
+       if (!msg) {
+               mutex_unlock(&rvu->mbox_lock);
+               return -ENOMEM;
+       }
+
+       msg->hdr.pcifunc = event->pcifunc;
+       msg->event = event->event;
+
+       memcpy(&msg->evt_data, &event->evt_data, sizeof(struct rep_evt_data));
+
+       otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, pf);
+
+       otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, pf);
+
+       mutex_unlock(&rvu->mbox_lock);
+       return 0;
+}
+
+static void rvu_rep_wq_handler(struct work_struct *work)
+{
+       struct rvu *rvu = container_of(work, struct rvu, rep_evt_work);
+       struct rep_evtq_ent *qentry;
+       struct rep_event *event;
+       unsigned long flags;
+
+       do {
+               spin_lock_irqsave(&rvu->rep_evtq_lock, flags);
+               qentry = list_first_entry_or_null(&rvu->rep_evtq_head,
+                                                 struct rep_evtq_ent,
+                                                 node);
+               if (qentry)
+                       list_del(&qentry->node);
+
+               spin_unlock_irqrestore(&rvu->rep_evtq_lock, flags);
+               if (!qentry)
+                       break; /* nothing more to process */
+
+               event = &qentry->event;
+
+               rvu_rep_up_notify(rvu, event);
+               kfree(qentry);
+       } while (1);
+}
+
+int rvu_mbox_handler_rep_event_notify(struct rvu *rvu, struct rep_event *req,
+                                     struct msg_rsp *rsp)
+{
+       struct rep_evtq_ent *qentry;
+
+       qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC);
+       if (!qentry)
+               return -ENOMEM;
+
+       qentry->event = *req;
+       spin_lock(&rvu->rep_evtq_lock);
+       list_add_tail(&qentry->node, &rvu->rep_evtq_head);
+       spin_unlock(&rvu->rep_evtq_lock);
+       queue_work(rvu->rep_evt_wq, &rvu->rep_evt_work);
+       return 0;
+}
+
+int rvu_rep_notify_pfvf_state(struct rvu *rvu, u16 pcifunc, bool enable)
+{
+       struct rep_event *req;
+       int pf;
+
+       if (!is_pf_cgxmapped(rvu, rvu_get_pf(pcifunc)))
+               return 0;
+
+       pf = rvu_get_pf(rvu->rep_pcifunc);
+
+       mutex_lock(&rvu->mbox_lock);
+       req = otx2_mbox_alloc_msg_rep_event_up_notify(rvu, pf);
+       if (!req) {
+               mutex_unlock(&rvu->mbox_lock);
+               return -ENOMEM;
+       }
+
+       req->hdr.pcifunc = rvu->rep_pcifunc;
+       req->event |= RVU_EVENT_PFVF_STATE;
+       req->pcifunc = pcifunc;
+       req->evt_data.vf_state = enable;
+
+       otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, pf);
+       otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, pf);
+
+       mutex_unlock(&rvu->mbox_lock);
+       return 0;
+}
+
 #define RVU_LF_RX_STATS(reg) \
                rvu_read64(rvu, blkaddr, NIX_AF_LFX_RX_STATX(nixlf, reg))
 
@@ -248,6 +366,16 @@ int rvu_rep_install_mcam_rules(struct rvu *rvu)
                        }
                }
        }
+
+       /* Initialize the wq for handling REP events */
+       spin_lock_init(&rvu->rep_evtq_lock);
+       INIT_LIST_HEAD(&rvu->rep_evtq_head);
+       INIT_WORK(&rvu->rep_evt_work, rvu_rep_wq_handler);
+       rvu->rep_evt_wq = alloc_workqueue("rep_evt_wq", 0, 0);
+       if (!rvu->rep_evt_wq) {
+               dev_err(rvu->dev, "REP workqueue allocation failed\n");
+               return -ENOMEM;
+       }
        return 0;
 }
 
index ae0eb08..962b10f 100644 (file)
@@ -441,6 +441,7 @@ struct otx2_nic {
 #define OTX2_FLAG_ADPTV_INT_COAL_ENABLED BIT_ULL(16)
 #define OTX2_FLAG_TC_MARK_ENABLED              BIT_ULL(17)
 #define OTX2_FLAG_REP_MODE_ENABLED              BIT_ULL(18)
+#define OTX2_FLAG_PORT_UP                      BIT_ULL(19)
        u64                     flags;
        u64                     *cq_op_addr;
 
@@ -1125,4 +1126,5 @@ u16 otx2_select_queue(struct net_device *netdev, struct sk_buff *skb,
 int otx2_get_txq_by_classid(struct otx2_nic *pfvf, u16 classid);
 void otx2_qos_config_txschq(struct otx2_nic *pfvf);
 void otx2_clean_qos_queues(struct otx2_nic *pfvf);
+int rvu_event_up_notify(struct otx2_nic *pf, struct rep_event *info);
 #endif /* OTX2_COMMON_H */
index 41480e2..ea5183f 100644 (file)
@@ -519,6 +519,7 @@ static void otx2_pfvf_mbox_up_handler(struct work_struct *work)
 
                switch (msg->id) {
                case MBOX_MSG_CGX_LINK_EVENT:
+               case MBOX_MSG_REP_EVENT_UP_NOTIFY:
                        break;
                default:
                        if (msg->rc)
@@ -832,6 +833,9 @@ static void otx2_handle_link_event(struct otx2_nic *pf)
        struct cgx_link_user_info *linfo = &pf->linfo;
        struct net_device *netdev = pf->netdev;
 
+       if (pf->flags & OTX2_FLAG_PORT_UP)
+               return;
+
        pr_info("%s NIC Link is %s %d Mbps %s duplex\n", netdev->name,
                linfo->link_up ? "UP" : "DOWN", linfo->speed,
                linfo->full_duplex ? "Full" : "Half");
@@ -844,6 +848,30 @@ static void otx2_handle_link_event(struct otx2_nic *pf)
        }
 }
 
+static int otx2_mbox_up_handler_rep_event_up_notify(struct otx2_nic *pf,
+                                                   struct rep_event *info,
+                                                   struct msg_rsp *rsp)
+{
+       struct net_device *netdev = pf->netdev;
+
+       if (info->event == RVU_EVENT_PORT_STATE) {
+               if (info->evt_data.port_state) {
+                       pf->flags |= OTX2_FLAG_PORT_UP;
+                       netif_carrier_on(netdev);
+                       netif_tx_start_all_queues(netdev);
+               } else {
+                       pf->flags &= ~OTX2_FLAG_PORT_UP;
+                       netif_tx_stop_all_queues(netdev);
+                       netif_carrier_off(netdev);
+               }
+               return 0;
+       }
+#ifdef CONFIG_RVU_ESWITCH
+       rvu_event_up_notify(pf, info);
+#endif
+       return 0;
+}
+
 int otx2_mbox_up_handler_mcs_intr_notify(struct otx2_nic *pf,
                                         struct mcs_intr_info *event,
                                         struct msg_rsp *rsp)
@@ -913,6 +941,7 @@ static int otx2_process_mbox_msg_up(struct otx2_nic *pf,
        }
 MBOX_UP_CGX_MESSAGES
 MBOX_UP_MCS_MESSAGES
+MBOX_UP_REP_MESSAGES
 #undef M
                break;
        default:
@@ -1974,6 +2003,7 @@ int otx2_open(struct net_device *netdev)
        }
 
        pf->flags &= ~OTX2_FLAG_INTF_DOWN;
+       pf->flags &= ~OTX2_FLAG_PORT_UP;
        /* 'intf_down' may be checked on any cpu */
        smp_wmb();
 
index 197aa21..eec3e0d 100644 (file)
@@ -28,6 +28,57 @@ MODULE_DESCRIPTION(DRV_STRING);
 MODULE_LICENSE("GPL");
 MODULE_DEVICE_TABLE(pci, rvu_rep_id_table);
 
+static int rvu_rep_get_repid(struct otx2_nic *priv, u16 pcifunc)
+{
+       int rep_id;
+
+       for (rep_id = 0; rep_id < priv->rep_cnt; rep_id++)
+               if (priv->rep_pf_map[rep_id] == pcifunc)
+                       return rep_id;
+       return -EINVAL;
+}
+
+static int rvu_rep_notify_pfvf(struct otx2_nic *priv, u16 event,
+                              struct rep_event *data)
+{
+       struct rep_event *req;
+
+       mutex_lock(&priv->mbox.lock);
+       req = otx2_mbox_alloc_msg_rep_event_notify(&priv->mbox);
+       if (!req) {
+               mutex_unlock(&priv->mbox.lock);
+               return -ENOMEM;
+       }
+       req->event = event;
+       req->pcifunc = data->pcifunc;
+
+       memcpy(&req->evt_data, &data->evt_data, sizeof(struct rep_evt_data));
+       otx2_sync_mbox_msg(&priv->mbox);
+       mutex_unlock(&priv->mbox.lock);
+       return 0;
+}
+
+static void rvu_rep_state_evt_handler(struct otx2_nic *priv,
+                                     struct rep_event *info)
+{
+       struct rep_dev *rep;
+       int rep_id;
+
+       rep_id = rvu_rep_get_repid(priv, info->pcifunc);
+       rep = priv->reps[rep_id];
+       if (info->evt_data.vf_state)
+               rep->flags |= RVU_REP_VF_INITIALIZED;
+       else
+               rep->flags &= ~RVU_REP_VF_INITIALIZED;
+}
+
+int rvu_event_up_notify(struct otx2_nic *pf, struct rep_event *info)
+{
+       if (info->event & RVU_EVENT_PFVF_STATE)
+               rvu_rep_state_evt_handler(pf, info);
+       return 0;
+}
+
 static void rvu_rep_get_stats(struct work_struct *work)
 {
        struct delayed_work *del_work = to_delayed_work(work);
@@ -78,6 +129,9 @@ static void rvu_rep_get_stats64(struct net_device *dev,
 {
        struct rep_dev *rep = netdev_priv(dev);
 
+       if (!(rep->flags & RVU_REP_VF_INITIALIZED))
+               return;
+
        stats->rx_packets = rep->stats.rx_frames;
        stats->rx_bytes = rep->stats.rx_bytes;
        stats->rx_dropped = rep->stats.rx_drops;
@@ -132,16 +186,38 @@ static netdev_tx_t rvu_rep_xmit(struct sk_buff *skb, struct net_device *dev)
 
 static int rvu_rep_open(struct net_device *dev)
 {
+       struct rep_dev *rep = netdev_priv(dev);
+       struct otx2_nic *priv = rep->mdev;
+       struct rep_event evt = {0};
+
+       if (!(rep->flags & RVU_REP_VF_INITIALIZED))
+               return 0;
+
        netif_carrier_on(dev);
        netif_tx_start_all_queues(dev);
+
+       evt.event = RVU_EVENT_PORT_STATE;
+       evt.evt_data.port_state = 1;
+       evt.pcifunc = rep->pcifunc;
+       rvu_rep_notify_pfvf(priv, RVU_EVENT_PORT_STATE, &evt);
        return 0;
 }
 
 static int rvu_rep_stop(struct net_device *dev)
 {
+       struct rep_dev *rep = netdev_priv(dev);
+       struct otx2_nic *priv = rep->mdev;
+       struct rep_event evt = {0};
+
+       if (!(rep->flags & RVU_REP_VF_INITIALIZED))
+               return 0;
+
        netif_carrier_off(dev);
        netif_tx_disable(dev);
 
+       evt.event = RVU_EVENT_PORT_STATE;
+       evt.pcifunc = rep->pcifunc;
+       rvu_rep_notify_pfvf(priv, RVU_EVENT_PORT_STATE, &evt);
        return 0;
 }
 
index 5d39bf6..7230061 100644 (file)
@@ -34,6 +34,8 @@ struct rep_dev {
        struct net_device *netdev;
        struct rep_stats stats;
        struct delayed_work stats_wrk;
+#define RVU_REP_VF_INITIALIZED         BIT_ULL(0)
+       u64 flags;
        u16 rep_id;
        u16 pcifunc;
 };
@@ -45,4 +47,5 @@ static inline bool otx2_rep_dev(struct pci_dev *pdev)
 
 int rvu_rep_create(struct otx2_nic *priv, struct netlink_ext_ack *extack);
 void rvu_rep_destroy(struct otx2_nic *priv);
+int rvu_event_up_notify(struct otx2_nic *pf, struct rep_event *info);
 #endif /* REP_H */