From 0eaec62a91ed9d5694b7e2bff30a1b1ad7107be7 Mon Sep 17 00:00:00 2001 From: Casey Leedom Date: Wed, 4 Jul 2018 15:12:56 +0530 Subject: [PATCH] cxgb4: Add support to read actual provisioned resources In highly constrained resources environments (like the 124VF T5 and 248VF T6 configurations), PF4 may not have very many resources at all and we need to adapt to whatever we've been allocated, this patch adds support to get the provisioned resources. Signed-off-by: Casey Leedom Signed-off-by: Ganesh Goudar Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 17 +++ .../ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 39 +++++ .../net/ethernet/chelsio/cxgb4/cxgb4_main.c | 134 +++++++++++++----- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 51 +++++++ 4 files changed, 206 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 4a8cbd864ef7..3da9299cd786 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -320,6 +320,21 @@ struct vpd_params { u8 na[MACADDR_LEN + 1]; }; +/* Maximum resources provisioned for a PCI PF. + */ +struct pf_resources { + unsigned int nvi; /* N virtual interfaces */ + unsigned int neq; /* N egress Qs */ + unsigned int nethctrl; /* N egress ETH or CTRL Qs */ + unsigned int niqflint; /* N ingress Qs/w free list(s) & intr */ + unsigned int niq; /* N ingress Qs */ + unsigned int tc; /* PCI-E traffic class */ + unsigned int pmask; /* port access rights mask */ + unsigned int nexactf; /* N exact MPS filters */ + unsigned int r_caps; /* read capabilities */ + unsigned int wx_caps; /* write/execute capabilities */ +}; + struct pci_params { unsigned int vpd_cap_addr; unsigned char speed; @@ -347,6 +362,7 @@ struct adapter_params { struct sge_params sge; struct tp_params tp; struct vpd_params vpd; + struct pf_resources pfres; struct pci_params pci; struct devlog_params devlog; enum pcie_memwin drv_memwin; @@ -1568,6 +1584,7 @@ int t4_eeprom_ptov(unsigned int phys_addr, unsigned int fn, unsigned int sz); int t4_seeprom_wp(struct adapter *adapter, bool enable); int t4_get_raw_vpd_params(struct adapter *adapter, struct vpd_params *p); int t4_get_vpd_params(struct adapter *adapter, struct vpd_params *p); +int t4_get_pfres(struct adapter *adapter); int t4_read_flash(struct adapter *adapter, unsigned int addr, unsigned int nwords, u32 *data, int byte_oriented); int t4_load_fw(struct adapter *adapter, const u8 *fw_data, unsigned int size); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index c301aaf79d64..516c883e1613 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -2414,6 +2414,44 @@ static const struct file_operations rss_vf_config_debugfs_fops = { .release = seq_release_private }; +static int resources_show(struct seq_file *seq, void *v) +{ + struct adapter *adapter = seq->private; + struct pf_resources *pfres = &adapter->params.pfres; + + #define S(desc, fmt, var) \ + seq_printf(seq, "%-60s " fmt "\n", \ + desc " (" #var "):", pfres->var) + + S("Virtual Interfaces", "%d", nvi); + S("Egress Queues", "%d", neq); + S("Ethernet Control", "%d", nethctrl); + S("Ingress Queues/w Free Lists/Interrupts", "%d", niqflint); + S("Ingress Queues", "%d", niq); + S("Traffic Class", "%d", tc); + S("Port Access Rights Mask", "%#x", pmask); + S("MAC Address Filters", "%d", nexactf); + S("Firmware Command Read Capabilities", "%#x", r_caps); + S("Firmware Command Write/Execute Capabilities", "%#x", wx_caps); + + #undef S + + return 0; +} + +static int resources_open(struct inode *inode, struct file *file) +{ + return single_open(file, resources_show, inode->i_private); +} + +static const struct file_operations resources_debugfs_fops = { + .owner = THIS_MODULE, + .open = resources_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + /** * ethqset2pinfo - return port_info of an Ethernet Queue Set * @adap: the adapter @@ -2973,6 +3011,7 @@ int t4_setup_debugfs(struct adapter *adap) { "rss_key", &rss_key_debugfs_fops, 0400, 0 }, { "rss_pf_config", &rss_pf_config_debugfs_fops, 0400, 0 }, { "rss_vf_config", &rss_vf_config_debugfs_fops, 0400, 0 }, + { "resources", &resources_debugfs_fops, 0400, 0 }, { "sge_qinfo", &sge_qinfo_debugfs_fops, 0400, 0 }, { "ibq_tp0", &cim_ibq_fops, 0400, 0 }, { "ibq_tp1", &cim_ibq_fops, 0400, 1 }, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 1c0374cbb890..96fcbd1c33a3 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -924,6 +924,7 @@ static int setup_sge_queues(struct adapter *adap) QUEUENUMBER_V(s->ethrxq[0].rspq.abs_id)); return 0; freeout: + dev_err(adap->pdev_dev, "Can't allocate queues, err=%d\n", -err); t4_free_sge_resources(adap); return err; } @@ -3536,6 +3537,16 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c) u32 v; int ret; + /* Now that we've successfully configured and initialized the adapter + * can ask the Firmware what resources it has provisioned for us. + */ + ret = t4_get_pfres(adap); + if (ret) { + dev_err(adap->pdev_dev, + "Unable to retrieve resource provisioning information\n"); + return ret; + } + /* get device capabilities */ memset(c, 0, sizeof(*c)); c->op_to_write = htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) | @@ -4170,32 +4181,6 @@ static int adap_init0(struct adapter *adap) goto bye; } - /* - * Grab VPD parameters. This should be done after we establish a - * connection to the firmware since some of the VPD parameters - * (notably the Core Clock frequency) are retrieved via requests to - * the firmware. On the other hand, we need these fairly early on - * so we do this right after getting ahold of the firmware. - */ - ret = t4_get_vpd_params(adap, &adap->params.vpd); - if (ret < 0) - goto bye; - - /* - * Find out what ports are available to us. Note that we need to do - * this before calling adap_init0_no_config() since it needs nports - * and portvec ... - */ - v = - FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | - FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_PORTVEC); - ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1, &v, &port_vec); - if (ret < 0) - goto bye; - - adap->params.nports = hweight32(port_vec); - adap->params.portvec = port_vec; - /* If the firmware is initialized already, emit a simply note to that * effect. Otherwise, it's time to try initializing the adapter. */ @@ -4246,6 +4231,45 @@ static int adap_init0(struct adapter *adap) } } + /* Now that we've successfully configured and initialized the adapter + * (or found it already initialized), we can ask the Firmware what + * resources it has provisioned for us. + */ + ret = t4_get_pfres(adap); + if (ret) { + dev_err(adap->pdev_dev, + "Unable to retrieve resource provisioning information\n"); + goto bye; + } + + /* Grab VPD parameters. This should be done after we establish a + * connection to the firmware since some of the VPD parameters + * (notably the Core Clock frequency) are retrieved via requests to + * the firmware. On the other hand, we need these fairly early on + * so we do this right after getting ahold of the firmware. + * + * We need to do this after initializing the adapter because someone + * could have FLASHed a new VPD which won't be read by the firmware + * until we do the RESET ... + */ + ret = t4_get_vpd_params(adap, &adap->params.vpd); + if (ret < 0) + goto bye; + + /* Find out what ports are available to us. Note that we need to do + * this before calling adap_init0_no_config() since it needs nports + * and portvec ... + */ + v = + FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_PORTVEC); + ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1, &v, &port_vec); + if (ret < 0) + goto bye; + + adap->params.nports = hweight32(port_vec); + adap->params.portvec = port_vec; + /* Give the SGE code a chance to pull in anything that it needs ... * Note that this must be called after we retrieve our VPD parameters * in order to know how to convert core ticks to seconds, etc. @@ -4797,10 +4821,12 @@ static inline bool is_x_10g_port(const struct link_config *lc) * of ports we found and the number of available CPUs. Most settings can be * modified by the admin prior to actual use. */ -static void cfg_queues(struct adapter *adap) +static int cfg_queues(struct adapter *adap) { struct sge *s = &adap->sge; - int i = 0, n10g = 0, qidx = 0; + int i, n10g = 0, qidx = 0; + int niqflint, neq, avail_eth_qsets; + int max_eth_qsets = 32; #ifndef CONFIG_CHELSIO_T4_DCB int q10g = 0; #endif @@ -4812,16 +4838,46 @@ static void cfg_queues(struct adapter *adap) adap->params.crypto = 0; } - n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg); + /* Calculate the number of Ethernet Queue Sets available based on + * resources provisioned for us. We always have an Asynchronous + * Firmware Event Ingress Queue. If we're operating in MSI or Legacy + * IRQ Pin Interrupt mode, then we'll also have a Forwarded Interrupt + * Ingress Queue. Meanwhile, we need two Egress Queues for each + * Queue Set: one for the Free List and one for the Ethernet TX Queue. + * + * Note that we should also take into account all of the various + * Offload Queues. But, in any situation where we're operating in + * a Resource Constrained Provisioning environment, doing any Offload + * at all is problematic ... + */ + niqflint = adap->params.pfres.niqflint - 1; + if (!(adap->flags & USING_MSIX)) + niqflint--; + neq = adap->params.pfres.neq / 2; + avail_eth_qsets = min(niqflint, neq); + + if (avail_eth_qsets > max_eth_qsets) + avail_eth_qsets = max_eth_qsets; + + if (avail_eth_qsets < adap->params.nports) { + dev_err(adap->pdev_dev, "avail_eth_qsets=%d < nports=%d\n", + avail_eth_qsets, adap->params.nports); + return -ENOMEM; + } + + /* Count the number of 10Gb/s or better ports */ + for_each_port(adap, i) + n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg); + #ifdef CONFIG_CHELSIO_T4_DCB /* For Data Center Bridging support we need to be able to support up * to 8 Traffic Priorities; each of which will be assigned to its * own TX Queue in order to prevent Head-Of-Line Blocking. */ - if (adap->params.nports * 8 > MAX_ETH_QSETS) { - dev_err(adap->pdev_dev, "MAX_ETH_QSETS=%d < %d!\n", - MAX_ETH_QSETS, adap->params.nports * 8); - BUG_ON(1); + if (adap->params.nports * 8 > avail_eth_qsets) { + dev_err(adap->pdev_dev, "DCB avail_eth_qsets=%d < %d!\n", + avail_eth_qsets, adap->params.nports * 8); + return -ENOMEM; } for_each_port(adap, i) { @@ -4837,7 +4893,7 @@ static void cfg_queues(struct adapter *adap) * per 10G port. */ if (n10g) - q10g = (MAX_ETH_QSETS - (adap->params.nports - n10g)) / n10g; + q10g = (avail_eth_qsets - (adap->params.nports - n10g)) / n10g; if (q10g > netif_get_num_default_rss_queues()) q10g = netif_get_num_default_rss_queues(); @@ -4888,6 +4944,8 @@ static void cfg_queues(struct adapter *adap) init_rspq(adap, &s->fw_evtq, 0, 1, 1024, 64); init_rspq(adap, &s->intrq, 0, 1, 512, 64); + + return 0; } /* @@ -5628,10 +5686,15 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } } + if (!(adapter->flags & FW_OK)) + goto fw_attach_fail; + /* Configure queues and allocate tables now, they can be needed as * soon as the first register_netdev completes. */ - cfg_queues(adapter); + err = cfg_queues(adapter); + if (err) + goto out_free_dev; adapter->smt = t4_init_smt(); if (!adapter->smt) { @@ -5738,6 +5801,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_free_dev; } +fw_attach_fail: /* * The card is now ready to go. If any errors occur during device * registration we do not fail the whole card but rather proceed only diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 974a868a4824..d266177aeef5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -2882,6 +2882,57 @@ int t4_get_vpd_params(struct adapter *adapter, struct vpd_params *p) return 0; } +/** + * t4_get_pfres - retrieve VF resource limits + * @adapter: the adapter + * + * Retrieves configured resource limits and capabilities for a physical + * function. The results are stored in @adapter->pfres. + */ +int t4_get_pfres(struct adapter *adapter) +{ + struct pf_resources *pfres = &adapter->params.pfres; + struct fw_pfvf_cmd cmd, rpl; + int v; + u32 word; + + /* Execute PFVF Read command to get VF resource limits; bail out early + * with error on command failure. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.op_to_vfn = cpu_to_be32(FW_CMD_OP_V(FW_PFVF_CMD) | + FW_CMD_REQUEST_F | + FW_CMD_READ_F | + FW_PFVF_CMD_PFN_V(adapter->pf) | + FW_PFVF_CMD_VFN_V(0)); + cmd.retval_len16 = cpu_to_be32(FW_LEN16(cmd)); + v = t4_wr_mbox(adapter, adapter->mbox, &cmd, sizeof(cmd), &rpl); + if (v != FW_SUCCESS) + return v; + + /* Extract PF resource limits and return success. + */ + word = be32_to_cpu(rpl.niqflint_niq); + pfres->niqflint = FW_PFVF_CMD_NIQFLINT_G(word); + pfres->niq = FW_PFVF_CMD_NIQ_G(word); + + word = be32_to_cpu(rpl.type_to_neq); + pfres->neq = FW_PFVF_CMD_NEQ_G(word); + pfres->pmask = FW_PFVF_CMD_PMASK_G(word); + + word = be32_to_cpu(rpl.tc_to_nexactf); + pfres->tc = FW_PFVF_CMD_TC_G(word); + pfres->nvi = FW_PFVF_CMD_NVI_G(word); + pfres->nexactf = FW_PFVF_CMD_NEXACTF_G(word); + + word = be32_to_cpu(rpl.r_caps_to_nethctrl); + pfres->r_caps = FW_PFVF_CMD_R_CAPS_G(word); + pfres->wx_caps = FW_PFVF_CMD_WX_CAPS_G(word); + pfres->nethctrl = FW_PFVF_CMD_NETHCTRL_G(word); + + return 0; +} + /* serial flash and firmware constants */ enum { SF_ATTEMPTS = 10, /* max retries for SF operations */ -- 2.30.2