net: thunderx: Support for upto 96 queues for a VF
[deliverable/linux.git] / drivers / net / ethernet / cavium / thunder / nicvf_main.c
index 85213f38ceb0fb5f7731f4f7377bc8816bc20ff6..68b2dce9229da0949c78c5956a29ef2cfaae1d40 100644 (file)
@@ -51,6 +51,14 @@ module_param(cpi_alg, int, S_IRUGO);
 MODULE_PARM_DESC(cpi_alg,
                 "PFC algorithm (0=none, 1=VLAN, 2=VLAN16, 3=IP Diffserv)");
 
+static inline u8 nicvf_netdev_qidx(struct nicvf *nic, u8 qidx)
+{
+       if (nic->sqs_mode)
+               return qidx + ((nic->sqs_id + 1) * MAX_CMP_QUEUES_PER_QS);
+       else
+               return qidx;
+}
+
 static inline void nicvf_set_rx_frame_cnt(struct nicvf *nic,
                                          struct sk_buff *skb)
 {
@@ -193,6 +201,7 @@ static void  nicvf_handle_mbx_intr(struct nicvf *nic)
                if (!nic->set_mac_pending)
                        ether_addr_copy(nic->netdev->dev_addr,
                                        mbx.nic_cfg.mac_addr);
+               nic->sqs_mode = mbx.nic_cfg.sqs_mode;
                nic->link_up = false;
                nic->duplex = 0;
                nic->speed = 0;
@@ -230,6 +239,26 @@ static void  nicvf_handle_mbx_intr(struct nicvf *nic)
                        netif_tx_stop_all_queues(nic->netdev);
                }
                break;
+       case NIC_MBOX_MSG_ALLOC_SQS:
+               nic->sqs_count = mbx.sqs_alloc.qs_count;
+               nic->pf_acked = true;
+               break;
+       case NIC_MBOX_MSG_SNICVF_PTR:
+               /* Primary VF: make note of secondary VF's pointer
+                * to be used while packet transmission.
+                */
+               nic->snicvf[mbx.nicvf.sqs_id] =
+                       (struct nicvf *)mbx.nicvf.nicvf;
+               nic->pf_acked = true;
+               break;
+       case NIC_MBOX_MSG_PNICVF_PTR:
+               /* Secondary VF/Qset: make note of primary VF's pointer
+                * to be used while packet reception, to handover packet
+                * to primary VF's netdev.
+                */
+               nic->pnicvf = (struct nicvf *)mbx.nicvf.nicvf;
+               nic->pf_acked = true;
+               break;
        default:
                netdev_err(nic->netdev,
                           "Invalid message from PF, msg 0x%x\n", mbx.msg.msg);
@@ -338,11 +367,100 @@ static int nicvf_rss_init(struct nicvf *nic)
 
        for (idx = 0; idx < rss->rss_size; idx++)
                rss->ind_tbl[idx] = ethtool_rxfh_indir_default(idx,
-                                                              nic->qs->rq_cnt);
+                                                              nic->rx_queues);
        nicvf_config_rss(nic);
        return 1;
 }
 
+/* Request PF to allocate additional Qsets */
+static void nicvf_request_sqs(struct nicvf *nic)
+{
+       union nic_mbx mbx = {};
+       int sqs;
+       int sqs_count = nic->sqs_count;
+       int rx_queues = 0, tx_queues = 0;
+
+       /* Only primary VF should request */
+       if (nic->sqs_mode ||  !nic->sqs_count)
+               return;
+
+       mbx.sqs_alloc.msg = NIC_MBOX_MSG_ALLOC_SQS;
+       mbx.sqs_alloc.vf_id = nic->vf_id;
+       mbx.sqs_alloc.qs_count = nic->sqs_count;
+       if (nicvf_send_msg_to_pf(nic, &mbx)) {
+               /* No response from PF */
+               nic->sqs_count = 0;
+               return;
+       }
+
+       /* Return if no Secondary Qsets available */
+       if (!nic->sqs_count)
+               return;
+
+       if (nic->rx_queues > MAX_RCV_QUEUES_PER_QS)
+               rx_queues = nic->rx_queues - MAX_RCV_QUEUES_PER_QS;
+       if (nic->tx_queues > MAX_SND_QUEUES_PER_QS)
+               tx_queues = nic->tx_queues - MAX_SND_QUEUES_PER_QS;
+
+       /* Set no of Rx/Tx queues in each of the SQsets */
+       for (sqs = 0; sqs < nic->sqs_count; sqs++) {
+               mbx.nicvf.msg = NIC_MBOX_MSG_SNICVF_PTR;
+               mbx.nicvf.vf_id = nic->vf_id;
+               mbx.nicvf.sqs_id = sqs;
+               nicvf_send_msg_to_pf(nic, &mbx);
+
+               nic->snicvf[sqs]->sqs_id = sqs;
+               if (rx_queues > MAX_RCV_QUEUES_PER_QS) {
+                       nic->snicvf[sqs]->qs->rq_cnt = MAX_RCV_QUEUES_PER_QS;
+                       rx_queues -= MAX_RCV_QUEUES_PER_QS;
+               } else {
+                       nic->snicvf[sqs]->qs->rq_cnt = rx_queues;
+                       rx_queues = 0;
+               }
+
+               if (tx_queues > MAX_SND_QUEUES_PER_QS) {
+                       nic->snicvf[sqs]->qs->sq_cnt = MAX_SND_QUEUES_PER_QS;
+                       tx_queues -= MAX_SND_QUEUES_PER_QS;
+               } else {
+                       nic->snicvf[sqs]->qs->sq_cnt = tx_queues;
+                       tx_queues = 0;
+               }
+
+               nic->snicvf[sqs]->qs->cq_cnt =
+               max(nic->snicvf[sqs]->qs->rq_cnt, nic->snicvf[sqs]->qs->sq_cnt);
+
+               /* Initialize secondary Qset's queues and its interrupts */
+               nicvf_open(nic->snicvf[sqs]->netdev);
+       }
+
+       /* Update stack with actual Rx/Tx queue count allocated */
+       if (sqs_count != nic->sqs_count)
+               nicvf_set_real_num_queues(nic->netdev,
+                                         nic->tx_queues, nic->rx_queues);
+}
+
+/* Send this Qset's nicvf pointer to PF.
+ * PF inturn sends primary VF's nicvf struct to secondary Qsets/VFs
+ * so that packets received by these Qsets can use primary VF's netdev
+ */
+static void nicvf_send_vf_struct(struct nicvf *nic)
+{
+       union nic_mbx mbx = {};
+
+       mbx.nicvf.msg = NIC_MBOX_MSG_NICVF_PTR;
+       mbx.nicvf.sqs_mode = nic->sqs_mode;
+       mbx.nicvf.nicvf = (u64)nic;
+       nicvf_send_msg_to_pf(nic, &mbx);
+}
+
+static void nicvf_get_primary_vf_struct(struct nicvf *nic)
+{
+       union nic_mbx mbx = {};
+
+       mbx.nicvf.msg = NIC_MBOX_MSG_PNICVF_PTR;
+       nicvf_send_msg_to_pf(nic, &mbx);
+}
+
 int nicvf_set_real_num_queues(struct net_device *netdev,
                              int tx_queues, int rx_queues)
 {
@@ -453,6 +571,15 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev,
        struct sk_buff *skb;
        struct nicvf *nic = netdev_priv(netdev);
        int err = 0;
+       int rq_idx;
+
+       rq_idx = nicvf_netdev_qidx(nic, cqe_rx->rq_idx);
+
+       if (nic->sqs_mode) {
+               /* Use primary VF's 'nicvf' struct */
+               nic = nic->pnicvf;
+               netdev = nic->netdev;
+       }
 
        /* Check for errors */
        err = nicvf_check_cqe_rx_errs(nic, cq, cqe_rx);
@@ -482,7 +609,7 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev,
 
        nicvf_set_rxhash(netdev, cqe_rx, skb);
 
-       skb_record_rx_queue(skb, cqe_rx->rq_idx);
+       skb_record_rx_queue(skb, rq_idx);
        if (netdev->hw_features & NETIF_F_RXCSUM) {
                /* HW by default verifies TCP/UDP/SCTP checksums */
                skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -578,8 +705,11 @@ loop:
 done:
        /* Wakeup TXQ if its stopped earlier due to SQ full */
        if (tx_done) {
-               txq = netdev_get_tx_queue(netdev, cq_idx);
-               if (netif_tx_queue_stopped(txq)) {
+               netdev = nic->pnicvf->netdev;
+               txq = netdev_get_tx_queue(netdev,
+                                         nicvf_netdev_qidx(nic, cq_idx));
+               nic = nic->pnicvf;
+               if (netif_tx_queue_stopped(txq) && netif_carrier_ok(netdev)) {
                        netif_tx_start_queue(txq);
                        nic->drv_stats.txq_wake++;
                        if (netif_msg_tx_err(nic))
@@ -893,7 +1023,6 @@ static netdev_tx_t nicvf_xmit(struct sk_buff *skb, struct net_device *netdev)
                        netdev_warn(netdev,
                                    "%s: Transmit ring full, stopping SQ%d\n",
                                    netdev->name, qid);
-
                return NETDEV_TX_BUSY;
        }
 
@@ -926,6 +1055,17 @@ int nicvf_stop(struct net_device *netdev)
        nicvf_send_msg_to_pf(nic, &mbx);
 
        netif_carrier_off(netdev);
+       netif_tx_stop_all_queues(nic->netdev);
+
+       /* Teardown secondary qsets first */
+       if (!nic->sqs_mode) {
+               for (qidx = 0; qidx < nic->sqs_count; qidx++) {
+                       if (!nic->snicvf[qidx])
+                               continue;
+                       nicvf_stop(nic->snicvf[qidx]->netdev);
+                       nic->snicvf[qidx] = NULL;
+               }
+       }
 
        /* Disable RBDR & QS error interrupts */
        for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) {
@@ -973,6 +1113,10 @@ int nicvf_stop(struct net_device *netdev)
 
        nicvf_free_cq_poll(nic);
 
+       /* Clear multiqset info */
+       nic->pnicvf = nic;
+       nic->sqs_count = 0;
+
        return 0;
 }
 
@@ -1028,10 +1172,16 @@ int nicvf_open(struct net_device *netdev)
 
        /* Configure CPI alorithm */
        nic->cpi_alg = cpi_alg;
-       nicvf_config_cpi(nic);
+       if (!nic->sqs_mode)
+               nicvf_config_cpi(nic);
+
+       nicvf_request_sqs(nic);
+       if (nic->sqs_mode)
+               nicvf_get_primary_vf_struct(nic);
 
        /* Configure receive side scaling */
-       nicvf_rss_init(nic);
+       if (!nic->sqs_mode)
+               nicvf_rss_init(nic);
 
        err = nicvf_register_interrupts(nic);
        if (err)
@@ -1282,8 +1432,7 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        struct device *dev = &pdev->dev;
        struct net_device *netdev;
        struct nicvf *nic;
-       struct queue_set *qs;
-       int    err;
+       int    err, qcount;
 
        err = pci_enable_device(pdev);
        if (err) {
@@ -1309,9 +1458,17 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_release_regions;
        }
 
-       netdev = alloc_etherdev_mqs(sizeof(struct nicvf),
-                                   MAX_RCV_QUEUES_PER_QS,
-                                   MAX_SND_QUEUES_PER_QS);
+       qcount = MAX_CMP_QUEUES_PER_QS;
+
+       /* Restrict multiqset support only for host bound VFs */
+       if (pdev->is_virtfn) {
+               /* Set max number of queues per VF */
+               qcount = roundup(num_online_cpus(), MAX_CMP_QUEUES_PER_QS);
+               qcount = min(qcount,
+                            (MAX_SQS_PER_VF + 1) * MAX_CMP_QUEUES_PER_QS);
+       }
+
+       netdev = alloc_etherdev_mqs(sizeof(struct nicvf), qcount, qcount);
        if (!netdev) {
                err = -ENOMEM;
                goto err_release_regions;
@@ -1324,6 +1481,8 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        nic = netdev_priv(netdev);
        nic->netdev = netdev;
        nic->pdev = pdev;
+       nic->pnicvf = nic;
+       nic->max_queues = qcount;
 
        /* MAP VF's configuration registers */
        nic->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
@@ -1337,20 +1496,26 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (err)
                goto err_free_netdev;
 
-       qs = nic->qs;
-
-       err = nicvf_set_real_num_queues(netdev, qs->sq_cnt, qs->rq_cnt);
-       if (err)
-               goto err_free_netdev;
-
        /* Check if PF is alive and get MAC address for this VF */
        err = nicvf_register_misc_interrupt(nic);
        if (err)
                goto err_free_netdev;
 
+       nicvf_send_vf_struct(nic);
+
+       /* Check if this VF is in QS only mode */
+       if (nic->sqs_mode)
+               return 0;
+
+       err = nicvf_set_real_num_queues(netdev, nic->tx_queues, nic->rx_queues);
+       if (err)
+               goto err_unregister_interrupts;
+
        netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG |
                               NETIF_F_TSO | NETIF_F_GRO |
-                              NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_RXHASH);
+                              NETIF_F_HW_VLAN_CTAG_RX);
+
+       netdev->hw_features |= NETIF_F_RXHASH;
 
        netdev->features |= netdev->hw_features;
 
@@ -1389,8 +1554,13 @@ static void nicvf_remove(struct pci_dev *pdev)
 {
        struct net_device *netdev = pci_get_drvdata(pdev);
        struct nicvf *nic = netdev_priv(netdev);
+       struct net_device *pnetdev = nic->pnicvf->netdev;
 
-       unregister_netdev(netdev);
+       /* Check if this Qset is assigned to different VF.
+        * If yes, clean primary and all secondary Qsets.
+        */
+       if (pnetdev && (pnetdev->reg_state == NETREG_REGISTERED))
+               unregister_netdev(pnetdev);
        nicvf_unregister_interrupts(nic);
        pci_set_drvdata(pdev, NULL);
        free_netdev(netdev);
This page took 0.02838 seconds and 5 git commands to generate.