cxgb4: Add support for dynamic allocation of resources for ULD
[cascardo/linux.git] / drivers / net / ethernet / chelsio / cxgb4 / cxgb4_main.c
index c45de49..85e30f1 100644 (file)
@@ -223,8 +223,8 @@ MODULE_PARM_DESC(select_queue,
 
 static struct dentry *cxgb4_debugfs_root;
 
-static LIST_HEAD(adapter_list);
-static DEFINE_MUTEX(uld_mutex);
+LIST_HEAD(adapter_list);
+DEFINE_MUTEX(uld_mutex);
 /* Adapter list to be accessed from atomic context */
 static LIST_HEAD(adap_rcu_list);
 static DEFINE_SPINLOCK(adap_rcu_lock);
@@ -1066,20 +1066,20 @@ static int alloc_ofld_rxqs(struct adapter *adap, struct sge_ofld_rxq *q,
  */
 static int setup_sge_queues(struct adapter *adap)
 {
-       int err, msi_idx, i, j;
+       int err, i, j;
        struct sge *s = &adap->sge;
 
        bitmap_zero(s->starving_fl, s->egr_sz);
        bitmap_zero(s->txq_maperr, s->egr_sz);
 
        if (adap->flags & USING_MSIX)
-               msi_idx = 1;         /* vector 0 is for non-queue interrupts */
+               adap->msi_idx = 1;         /* vector 0 is for non-queue interrupts */
        else {
                err = t4_sge_alloc_rxq(adap, &s->intrq, false, adap->port[0], 0,
                                       NULL, NULL, NULL, -1);
                if (err)
                        return err;
-               msi_idx = -((int)s->intrq.abs_id + 1);
+               adap->msi_idx = -((int)s->intrq.abs_id + 1);
        }
 
        /* NOTE: If you add/delete any Ingress/Egress Queue allocations in here,
@@ -1096,7 +1096,7 @@ static int setup_sge_queues(struct adapter *adap)
         *    new/deleted queues.
         */
        err = t4_sge_alloc_rxq(adap, &s->fw_evtq, true, adap->port[0],
-                              msi_idx, NULL, fwevtq_handler, NULL, -1);
+                              adap->msi_idx, NULL, fwevtq_handler, NULL, -1);
        if (err) {
 freeout:       t4_free_sge_resources(adap);
                return err;
@@ -1109,10 +1109,10 @@ freeout:        t4_free_sge_resources(adap);
                struct sge_eth_txq *t = &s->ethtxq[pi->first_qset];
 
                for (j = 0; j < pi->nqsets; j++, q++) {
-                       if (msi_idx > 0)
-                               msi_idx++;
+                       if (adap->msi_idx > 0)
+                               adap->msi_idx++;
                        err = t4_sge_alloc_rxq(adap, &q->rspq, false, dev,
-                                              msi_idx, &q->fl,
+                                              adap->msi_idx, &q->fl,
                                               t4_ethrx_handler,
                                               NULL,
                                               t4_get_mps_bg_map(adap,
@@ -1141,11 +1141,11 @@ freeout:        t4_free_sge_resources(adap);
        }
 
 #define ALLOC_OFLD_RXQS(firstq, nq, per_chan, ids, lro) do { \
-       err = alloc_ofld_rxqs(adap, firstq, nq, per_chan, msi_idx, ids, lro); \
+       err = alloc_ofld_rxqs(adap, firstq, nq, per_chan, adap->msi_idx, ids, lro); \
        if (err) \
                goto freeout; \
-       if (msi_idx > 0) \
-               msi_idx += nq; \
+       if (adap->msi_idx > 0) \
+               adap->msi_idx += nq; \
 } while (0)
 
        ALLOC_OFLD_RXQS(s->iscsirxq, s->iscsiqsets, j, s->iscsi_rxq, false);
@@ -2565,6 +2565,12 @@ static void detach_ulds(struct adapter *adap)
                                             CXGB4_STATE_DETACH);
                        adap->uld_handle[i] = NULL;
                }
+       for (i = 0; i < CXGB4_PCI_ULD_MAX; i++)
+               if (adap->uld && adap->uld[i].handle) {
+                       adap->uld[i].state_change(adap->uld[i].handle,
+                                            CXGB4_STATE_DETACH);
+                       adap->uld[i].handle = NULL;
+               }
        if (netevent_registered && list_empty(&adapter_list)) {
                unregister_netevent_notifier(&cxgb4_netevent_nb);
                netevent_registered = false;
@@ -2584,6 +2590,10 @@ static void notify_ulds(struct adapter *adap, enum cxgb4_state new_state)
        for (i = 0; i < CXGB4_ULD_MAX; i++)
                if (adap->uld_handle[i])
                        ulds[i].state_change(adap->uld_handle[i], new_state);
+       for (i = 0; i < CXGB4_PCI_ULD_MAX; i++)
+               if (adap->uld && adap->uld[i].handle)
+                       adap->uld[i].state_change(adap->uld[i].handle,
+                                                 new_state);
        mutex_unlock(&uld_mutex);
 }
 
@@ -3078,6 +3088,26 @@ static int cxgb_change_mtu(struct net_device *dev, int new_mtu)
        return ret;
 }
 
+#ifdef CONFIG_PCI_IOV
+static int cxgb_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
+{
+       struct port_info *pi = netdev_priv(dev);
+       struct adapter *adap = pi->adapter;
+
+       /* verify MAC addr is valid */
+       if (!is_valid_ether_addr(mac)) {
+               dev_err(pi->adapter->pdev_dev,
+                       "Invalid Ethernet address %pM for VF %d\n",
+                       mac, vf);
+               return -EINVAL;
+       }
+
+       dev_info(pi->adapter->pdev_dev,
+                "Setting MAC %pM on VF %d\n", mac, vf);
+       return t4_set_vf_mac_acl(adap, vf + 1, 1, mac);
+}
+#endif
+
 static int cxgb_set_mac_addr(struct net_device *dev, void *p)
 {
        int ret;
@@ -3136,7 +3166,27 @@ static const struct net_device_ops cxgb4_netdev_ops = {
 #ifdef CONFIG_NET_RX_BUSY_POLL
        .ndo_busy_poll        = cxgb_busy_poll,
 #endif
+};
+
+static const struct net_device_ops cxgb4_mgmt_netdev_ops = {
+#ifdef CONFIG_PCI_IOV
+       .ndo_set_vf_mac       = cxgb_set_vf_mac,
+#endif
+};
+
+static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+       struct adapter *adapter = netdev2adap(dev);
 
+       strlcpy(info->driver, cxgb4_driver_name, sizeof(info->driver));
+       strlcpy(info->version, cxgb4_driver_version,
+               sizeof(info->version));
+       strlcpy(info->bus_info, pci_name(adapter->pdev),
+               sizeof(info->bus_info));
+}
+
+static const struct ethtool_ops cxgb4_mgmt_ethtool_ops = {
+       .get_drvinfo       = get_drvinfo,
 };
 
 void t4_fatal_err(struct adapter *adap)
@@ -4130,6 +4180,11 @@ static int adap_init0(struct adapter *adap)
                adap->vres.iscsi.start = val[0];
                adap->vres.iscsi.size = val[1] - val[0] + 1;
        }
+       if (caps_cmd.cryptocaps) {
+               /* Should query params here...TODO */
+               adap->params.crypto |= ULP_CRYPTO_LOOKASIDE;
+               adap->num_uld += 1;
+       }
 #undef FW_PARAM_PFVF
 #undef FW_PARAM_DEV
 
@@ -4311,16 +4366,6 @@ static inline bool is_x_10g_port(const struct link_config *lc)
               (lc->supported & FW_PORT_CAP_SPEED_40G) != 0;
 }
 
-static inline void init_rspq(struct adapter *adap, struct sge_rspq *q,
-                            unsigned int us, unsigned int cnt,
-                            unsigned int size, unsigned int iqe_size)
-{
-       q->adap = adap;
-       cxgb4_set_rspq_intr_params(q, us, cnt);
-       q->iqe_len = iqe_size;
-       q->size = size;
-}
-
 /*
  * Perform default configuration of DMA queues depending on the number and type
  * of ports we found and the number of available CPUs.  Most settings can be
@@ -4335,6 +4380,15 @@ static void cfg_queues(struct adapter *adap)
 #endif
        int ciq_size;
 
+       /* Reduce memory usage in kdump environment, disable all offload.
+        */
+       if (is_kdump_kernel()) {
+               adap->params.offload = 0;
+               adap->params.crypto = 0;
+       } else if (adap->num_uld && uld_mem_alloc(adap)) {
+               adap->params.crypto = 0;
+       }
+
        for_each_port(adap, i)
                n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg);
 #ifdef CONFIG_CHELSIO_T4_DCB
@@ -4365,11 +4419,6 @@ static void cfg_queues(struct adapter *adap)
        if (q10g > netif_get_num_default_rss_queues())
                q10g = netif_get_num_default_rss_queues();
 
-       /* Reduce memory usage in kdump environment, disable all offload.
-        */
-       if (is_kdump_kernel())
-               adap->params.offload = 0;
-
        for_each_port(adap, i) {
                struct port_info *pi = adap2pinfo(adap, i);
 
@@ -4498,23 +4547,58 @@ static void reduce_ethqs(struct adapter *adap, int n)
        }
 }
 
+static int get_msix_info(struct adapter *adap)
+{
+       struct uld_msix_info *msix_info;
+       int max_ingq = (MAX_OFLD_QSETS * adap->num_uld);
+
+       msix_info = kcalloc(max_ingq, sizeof(*msix_info), GFP_KERNEL);
+       if (!msix_info)
+               return -ENOMEM;
+
+       adap->msix_bmap_ulds.msix_bmap = kcalloc(BITS_TO_LONGS(max_ingq),
+                                                sizeof(long), GFP_KERNEL);
+       if (!adap->msix_bmap_ulds.msix_bmap) {
+               kfree(msix_info);
+               return -ENOMEM;
+       }
+       spin_lock_init(&adap->msix_bmap_ulds.lock);
+       adap->msix_info_ulds = msix_info;
+       return 0;
+}
+
+static void free_msix_info(struct adapter *adap)
+{
+       if (!adap->num_uld)
+               return;
+
+       kfree(adap->msix_info_ulds);
+       kfree(adap->msix_bmap_ulds.msix_bmap);
+}
+
 /* 2 MSI-X vectors needed for the FW queue and non-data interrupts */
 #define EXTRA_VECS 2
 
 static int enable_msix(struct adapter *adap)
 {
-       int ofld_need = 0;
-       int i, want, need, allocated;
+       int ofld_need = 0, uld_need = 0;
+       int i, j, want, need, allocated;
        struct sge *s = &adap->sge;
        unsigned int nchan = adap->params.nports;
        struct msix_entry *entries;
+       int max_ingq = MAX_INGQ;
 
-       entries = kmalloc(sizeof(*entries) * (MAX_INGQ + 1),
+       max_ingq += (MAX_OFLD_QSETS * adap->num_uld);
+       entries = kmalloc(sizeof(*entries) * (max_ingq + 1),
                          GFP_KERNEL);
        if (!entries)
                return -ENOMEM;
 
-       for (i = 0; i < MAX_INGQ + 1; ++i)
+       /* map for msix */
+       if (is_pci_uld(adap) && get_msix_info(adap))
+               adap->params.crypto = 0;
+
+       for (i = 0; i < max_ingq + 1; ++i)
                entries[i].entry = i;
 
        want = s->max_ethqsets + EXTRA_VECS;
@@ -4527,13 +4611,17 @@ static int enable_msix(struct adapter *adap)
                else
                        ofld_need = 4 * nchan;
        }
+       if (is_pci_uld(adap)) {
+               want += netif_get_num_default_rss_queues() * nchan;
+               uld_need = nchan;
+       }
 #ifdef CONFIG_CHELSIO_T4_DCB
        /* For Data Center Bridging we need 8 Ethernet TX Priority Queues for
         * each port.
         */
-       need = 8 * adap->params.nports + EXTRA_VECS + ofld_need;
+       need = 8 * adap->params.nports + EXTRA_VECS + ofld_need + uld_need;
 #else
-       need = adap->params.nports + EXTRA_VECS + ofld_need;
+       need = adap->params.nports + EXTRA_VECS + ofld_need + uld_need;
 #endif
        allocated = pci_enable_msix_range(adap->pdev, entries, need, want);
        if (allocated < 0) {
@@ -4547,12 +4635,20 @@ static int enable_msix(struct adapter *adap)
         * Every group gets its minimum requirement and NIC gets top
         * priority for leftovers.
         */
-       i = allocated - EXTRA_VECS - ofld_need;
+       i = allocated - EXTRA_VECS - ofld_need - uld_need;
        if (i < s->max_ethqsets) {
                s->max_ethqsets = i;
                if (i < s->ethqsets)
                        reduce_ethqs(adap, i);
        }
+       if (is_pci_uld(adap)) {
+               if (allocated < want)
+                       s->nqs_per_uld = nchan;
+               else
+                       s->nqs_per_uld = netif_get_num_default_rss_queues() *
+                                       nchan;
+       }
+
        if (is_offload(adap)) {
                if (allocated < want) {
                        s->rdmaqs = nchan;
@@ -4564,16 +4660,24 @@ static int enable_msix(struct adapter *adap)
 
                /* leftovers go to OFLD */
                i = allocated - EXTRA_VECS - s->max_ethqsets -
-                   s->rdmaqs - s->rdmaciqs - s->niscsitq;
+                       s->rdmaqs - s->rdmaciqs - s->niscsitq;
+               if (is_pci_uld(adap))
+                       i -= s->nqs_per_uld * adap->num_uld;
                s->iscsiqsets = (i / nchan) * nchan;  /* round down */
 
        }
-       for (i = 0; i < allocated; ++i)
+
+       for (i = 0; i < (allocated - (s->nqs_per_uld * adap->num_uld)); ++i)
                adap->msix_info[i].vec = entries[i].vector;
+       if (is_pci_uld(adap)) {
+               for (j = 0 ; i < allocated; ++i, j++)
+                       adap->msix_info_ulds[j].vec = entries[i].vector;
+               adap->msix_bmap_ulds.mapsize = j;
+       }
        dev_info(adap->pdev_dev, "%d MSI-X vectors allocated, "
-                "nic %d iscsi %d rdma cpl %d rdma ciq %d\n",
+                "nic %d iscsi %d rdma cpl %d rdma ciq %d uld %d\n",
                 allocated, s->max_ethqsets, s->iscsiqsets, s->rdmaqs,
-                s->rdmaciqs);
+                s->rdmaciqs, s->nqs_per_uld);
 
        kfree(entries);
        return 0;
@@ -4836,19 +4940,12 @@ static int get_chip_type(struct pci_dev *pdev, u32 pl_rev)
 #ifdef CONFIG_PCI_IOV
 static int cxgb4_iov_configure(struct pci_dev *pdev, int num_vfs)
 {
+       struct adapter *adap = pci_get_drvdata(pdev);
        int err = 0;
        int current_vfs = pci_num_vf(pdev);
        u32 pcie_fw;
-       void __iomem *regs;
 
-       regs = pci_ioremap_bar(pdev, 0);
-       if (!regs) {
-               dev_err(&pdev->dev, "cannot map device registers\n");
-               return -ENOMEM;
-       }
-
-       pcie_fw = readl(regs + PCIE_FW_A);
-       iounmap(regs);
+       pcie_fw = readl(adap->regs + PCIE_FW_A);
        /* Check if cxgb4 is the MASTER and fw is initialized */
        if (!(pcie_fw & PCIE_FW_INIT_F) ||
            !(pcie_fw & PCIE_FW_MASTER_VLD_F) ||
@@ -4875,6 +4972,8 @@ static int cxgb4_iov_configure(struct pci_dev *pdev, int num_vfs)
         */
        if (!num_vfs) {
                pci_disable_sriov(pdev);
+               if (adap->port[0]->reg_state == NETREG_REGISTERED)
+                       unregister_netdev(adap->port[0]);
                return num_vfs;
        }
 
@@ -4882,6 +4981,12 @@ static int cxgb4_iov_configure(struct pci_dev *pdev, int num_vfs)
                err = pci_enable_sriov(pdev, num_vfs);
                if (err)
                        return err;
+
+               if (adap->port[0]->reg_state == NETREG_UNINITIALIZED) {
+                       err = register_netdev(adap->port[0]);
+                       if (err < 0)
+                               pr_info("Unable to register VF mgmt netdev\n");
+               }
        }
        return num_vfs;
 }
@@ -4893,9 +4998,14 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        struct port_info *pi;
        bool highdma = false;
        struct adapter *adapter = NULL;
+       struct net_device *netdev;
+#ifdef CONFIG_PCI_IOV
+       char name[IFNAMSIZ];
+#endif
        void __iomem *regs;
        u32 whoami, pl_rev;
        enum chip_type chip;
+       static int adap_idx = 1;
 
        printk_once(KERN_INFO "%s - version %s\n", DRV_DESC, DRV_VERSION);
 
@@ -4930,7 +5040,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        func = CHELSIO_CHIP_VERSION(chip) <= CHELSIO_T5 ?
                SOURCEPF_G(whoami) : T6_SOURCEPF_G(whoami);
        if (func != ent->driver_data) {
+#ifndef CONFIG_PCI_IOV
                iounmap(regs);
+#endif
                pci_disable_device(pdev);
                pci_save_state(pdev);        /* to restore SR-IOV later */
                goto sriov;
@@ -4962,6 +5074,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                err = -ENOMEM;
                goto out_unmap_bar0;
        }
+       adap_idx++;
 
        adapter->workq = create_singlethread_workqueue("cxgb4");
        if (!adapter->workq) {
@@ -5048,8 +5161,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                              T6_STATMODE_V(0)));
 
        for_each_port(adapter, i) {
-               struct net_device *netdev;
-
                netdev = alloc_etherdev_mq(sizeof(struct port_info),
                                           MAX_ETH_QSETS);
                if (!netdev) {
@@ -5168,8 +5279,11 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        /* See what interrupts we'll be using */
        if (msi > 1 && enable_msix(adapter) == 0)
                adapter->flags |= USING_MSIX;
-       else if (msi > 0 && pci_enable_msi(pdev) == 0)
+       else if (msi > 0 && pci_enable_msi(pdev) == 0) {
                adapter->flags |= USING_MSI;
+               if (msi > 1)
+                       free_msix_info(adapter);
+       }
 
        /* check for PCI Express bandwidth capabiltites */
        cxgb4_check_pcie_caps(adapter);
@@ -5217,6 +5331,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                attach_ulds(adapter);
 
        print_adapter_info(adapter);
+       return 0;
 
 sriov:
 #ifdef CONFIG_PCI_IOV
@@ -5230,11 +5345,64 @@ sriov:
                                 "instantiated %u virtual functions\n",
                                 num_vf[func]);
        }
-#endif
+
+       adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
+       if (!adapter) {
+               err = -ENOMEM;
+               goto free_pci_region;
+       }
+
+       snprintf(name, IFNAMSIZ, "mgmtpf%d%d", adap_idx, func);
+       netdev = alloc_netdev(0, name, NET_NAME_UNKNOWN, ether_setup);
+       if (!netdev) {
+               err = -ENOMEM;
+               goto free_adapter;
+       }
+
+       adapter->pdev = pdev;
+       adapter->pdev_dev = &pdev->dev;
+       adapter->name = pci_name(pdev);
+       adapter->mbox = func;
+       adapter->pf = func;
+       adapter->regs = regs;
+       adapter->mbox_log = kzalloc(sizeof(*adapter->mbox_log) +
+                                   (sizeof(struct mbox_cmd) *
+                                    T4_OS_LOG_MBOX_CMDS),
+                                   GFP_KERNEL);
+       if (!adapter->mbox_log) {
+               err = -ENOMEM;
+               goto free_netdevice;
+       }
+       pi = netdev_priv(netdev);
+       pi->adapter = adapter;
+       SET_NETDEV_DEV(netdev, &pdev->dev);
+       pci_set_drvdata(pdev, adapter);
+
+       adapter->port[0] = netdev;
+       netdev->netdev_ops = &cxgb4_mgmt_netdev_ops;
+       netdev->ethtool_ops = &cxgb4_mgmt_ethtool_ops;
+
+       return 0;
+
+ free_netdevice:
+       free_netdev(adapter->port[0]);
+ free_adapter:
+       kfree(adapter);
+ free_pci_region:
+       iounmap(regs);
+       pci_disable_sriov(pdev);
+       pci_release_regions(pdev);
+       return err;
+#else
        return 0;
+#endif
 
  out_free_dev:
        free_some_resources(adapter);
+       if (adapter->flags & USING_MSIX)
+               free_msix_info(adapter);
+       if (adapter->num_uld)
+               uld_mem_free(adapter);
  out_unmap_bar:
        if (!is_t4(adapter->params.chip))
                iounmap(adapter->bar2);
@@ -5258,12 +5426,12 @@ static void remove_one(struct pci_dev *pdev)
 {
        struct adapter *adapter = pci_get_drvdata(pdev);
 
-#ifdef CONFIG_PCI_IOV
-       pci_disable_sriov(pdev);
-
-#endif
+       if (!adapter) {
+               pci_release_regions(pdev);
+               return;
+       }
 
-       if (adapter) {
+       if (adapter->pf == 4) {
                int i;
 
                /* Tear down per-adapter Work Queue first since it can contain
@@ -5296,6 +5464,10 @@ static void remove_one(struct pci_dev *pdev)
                if (adapter->flags & FULL_INIT_DONE)
                        cxgb_down(adapter);
 
+               if (adapter->flags & USING_MSIX)
+                       free_msix_info(adapter);
+               if (adapter->num_uld)
+                       uld_mem_free(adapter);
                free_some_resources(adapter);
 #if IS_ENABLED(CONFIG_IPV6)
                t4_cleanup_clip_tbl(adapter);
@@ -5312,8 +5484,18 @@ static void remove_one(struct pci_dev *pdev)
                kfree(adapter->mbox_log);
                synchronize_rcu();
                kfree(adapter);
-       } else
+       }
+#ifdef CONFIG_PCI_IOV
+       else {
+               if (adapter->port[0]->reg_state == NETREG_REGISTERED)
+                       unregister_netdev(adapter->port[0]);
+               free_netdev(adapter->port[0]);
+               iounmap(adapter->regs);
+               kfree(adapter);
+               pci_disable_sriov(pdev);
                pci_release_regions(pdev);
+       }
+#endif
 }
 
 static struct pci_driver cxgb4_driver = {