cxgb3_main.c
来自「linux 内核源代码」· C语言 代码 · 共 2,534 行 · 第 1/5 页
C
2,534 行
/* * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/dma-mapping.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/if_vlan.h>#include <linux/mii.h>#include <linux/sockios.h>#include <linux/workqueue.h>#include <linux/proc_fs.h>#include <linux/rtnetlink.h>#include <linux/firmware.h>#include <linux/log2.h>#include <asm/uaccess.h>#include "common.h"#include "cxgb3_ioctl.h"#include "regs.h"#include "cxgb3_offload.h"#include "version.h"#include "cxgb3_ctl_defs.h"#include "t3_cpl.h"#include "firmware_exports.h"enum { MAX_TXQ_ENTRIES = 16384, MAX_CTRL_TXQ_ENTRIES = 1024, MAX_RSPQ_ENTRIES = 16384, MAX_RX_BUFFERS = 16384, MAX_RX_JUMBO_BUFFERS = 16384, MIN_TXQ_ENTRIES = 4, MIN_CTRL_TXQ_ENTRIES = 4, MIN_RSPQ_ENTRIES = 32, MIN_FL_ENTRIES = 32};#define PORT_MASK ((1 << MAX_NPORTS) - 1)#define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\ NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)#define EEPROM_MAGIC 0x38E2F10C#define CH_DEVICE(devid, ssid, idx) \ { PCI_VENDOR_ID_CHELSIO, devid, PCI_ANY_ID, ssid, 0, 0, idx }static const struct pci_device_id cxgb3_pci_tbl[] = { CH_DEVICE(0x20, 1, 0), /* PE9000 */ CH_DEVICE(0x21, 1, 1), /* T302E */ CH_DEVICE(0x22, 1, 2), /* T310E */ CH_DEVICE(0x23, 1, 3), /* T320X */ CH_DEVICE(0x24, 1, 1), /* T302X */ CH_DEVICE(0x25, 1, 3), /* T320E */ CH_DEVICE(0x26, 1, 2), /* T310X */ CH_DEVICE(0x30, 1, 2), /* T3B10 */ CH_DEVICE(0x31, 1, 3), /* T3B20 */ CH_DEVICE(0x32, 1, 1), /* T3B02 */ {0,}};MODULE_DESCRIPTION(DRV_DESC);MODULE_AUTHOR("Chelsio Communications");MODULE_LICENSE("Dual BSD/GPL");MODULE_VERSION(DRV_VERSION);MODULE_DEVICE_TABLE(pci, cxgb3_pci_tbl);static int dflt_msg_enable = DFLT_MSG_ENABLE;module_param(dflt_msg_enable, int, 0644);MODULE_PARM_DESC(dflt_msg_enable, "Chelsio T3 default message enable bitmap");/* * The driver uses the best interrupt scheme available on a platform in the * order MSI-X, MSI, legacy pin interrupts. This parameter determines which * of these schemes the driver may consider as follows: * * msi = 2: choose from among all three options * msi = 1: only consider MSI and pin interrupts * msi = 0: force pin interrupts */static int msi = 2;module_param(msi, int, 0644);MODULE_PARM_DESC(msi, "whether to use MSI or MSI-X");/* * The driver enables offload as a default. * To disable it, use ofld_disable = 1. */static int ofld_disable = 0;module_param(ofld_disable, int, 0644);MODULE_PARM_DESC(ofld_disable, "whether to enable offload at init time or not");/* * We have work elements that we need to cancel when an interface is taken * down. Normally the work elements would be executed by keventd but that * can deadlock because of linkwatch. If our close method takes the rtnl * lock and linkwatch is ahead of our work elements in keventd, linkwatch * will block keventd as it needs the rtnl lock, and we'll deadlock waiting * for our work to complete. Get our own work queue to solve this. */static struct workqueue_struct *cxgb3_wq;/** * link_report - show link status and link speed/duplex * @p: the port whose settings are to be reported * * Shows the link status, speed, and duplex of a port. */static void link_report(struct net_device *dev){ if (!netif_carrier_ok(dev)) printk(KERN_INFO "%s: link down\n", dev->name); else { const char *s = "10Mbps"; const struct port_info *p = netdev_priv(dev); switch (p->link_config.speed) { case SPEED_10000: s = "10Gbps"; break; case SPEED_1000: s = "1000Mbps"; break; case SPEED_100: s = "100Mbps"; break; } printk(KERN_INFO "%s: link up, %s, %s-duplex\n", dev->name, s, p->link_config.duplex == DUPLEX_FULL ? "full" : "half"); }}/** * t3_os_link_changed - handle link status changes * @adapter: the adapter associated with the link change * @port_id: the port index whose limk status has changed * @link_stat: the new status of the link * @speed: the new speed setting * @duplex: the new duplex setting * @pause: the new flow-control setting * * This is the OS-dependent handler for link status changes. The OS * neutral handler takes care of most of the processing for these events, * then calls this handler for any OS-specific processing. */void t3_os_link_changed(struct adapter *adapter, int port_id, int link_stat, int speed, int duplex, int pause){ struct net_device *dev = adapter->port[port_id]; struct port_info *pi = netdev_priv(dev); struct cmac *mac = &pi->mac; /* Skip changes from disabled ports. */ if (!netif_running(dev)) return; if (link_stat != netif_carrier_ok(dev)) { if (link_stat) { t3_mac_enable(mac, MAC_DIRECTION_RX); netif_carrier_on(dev); } else { netif_carrier_off(dev); pi->phy.ops->power_down(&pi->phy, 1); t3_mac_disable(mac, MAC_DIRECTION_RX); t3_link_start(&pi->phy, mac, &pi->link_config); } link_report(dev); }}static void cxgb_set_rxmode(struct net_device *dev){ struct t3_rx_mode rm; struct port_info *pi = netdev_priv(dev); init_rx_mode(&rm, dev, dev->mc_list); t3_mac_set_rx_mode(&pi->mac, &rm);}/** * link_start - enable a port * @dev: the device to enable * * Performs the MAC and PHY actions needed to enable a port. */static void link_start(struct net_device *dev){ struct t3_rx_mode rm; struct port_info *pi = netdev_priv(dev); struct cmac *mac = &pi->mac; init_rx_mode(&rm, dev, dev->mc_list); t3_mac_reset(mac); t3_mac_set_mtu(mac, dev->mtu); t3_mac_set_address(mac, 0, dev->dev_addr); t3_mac_set_rx_mode(mac, &rm); t3_link_start(&pi->phy, mac, &pi->link_config); t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);}static inline void cxgb_disable_msi(struct adapter *adapter){ if (adapter->flags & USING_MSIX) { pci_disable_msix(adapter->pdev); adapter->flags &= ~USING_MSIX; } else if (adapter->flags & USING_MSI) { pci_disable_msi(adapter->pdev); adapter->flags &= ~USING_MSI; }}/* * Interrupt handler for asynchronous events used with MSI-X. */static irqreturn_t t3_async_intr_handler(int irq, void *cookie){ t3_slow_intr_handler(cookie); return IRQ_HANDLED;}/* * Name the MSI-X interrupts. */static void name_msix_vecs(struct adapter *adap){ int i, j, msi_idx = 1, n = sizeof(adap->msix_info[0].desc) - 1; snprintf(adap->msix_info[0].desc, n, "%s", adap->name); adap->msix_info[0].desc[n] = 0; for_each_port(adap, j) { struct net_device *d = adap->port[j]; const struct port_info *pi = netdev_priv(d); for (i = 0; i < pi->nqsets; i++, msi_idx++) { snprintf(adap->msix_info[msi_idx].desc, n, "%s (queue %d)", d->name, i); adap->msix_info[msi_idx].desc[n] = 0; } }}static int request_msix_data_irqs(struct adapter *adap){ int i, j, err, qidx = 0; for_each_port(adap, i) { int nqsets = adap2pinfo(adap, i)->nqsets; for (j = 0; j < nqsets; ++j) { err = request_irq(adap->msix_info[qidx + 1].vec, t3_intr_handler(adap, adap->sge.qs[qidx]. rspq.polling), 0, adap->msix_info[qidx + 1].desc, &adap->sge.qs[qidx]); if (err) { while (--qidx >= 0) free_irq(adap->msix_info[qidx + 1].vec, &adap->sge.qs[qidx]); return err; } qidx++; } } return 0;}/** * setup_rss - configure RSS * @adap: the adapter * * Sets up RSS to distribute packets to multiple receive queues. We * configure the RSS CPU lookup table to distribute to the number of HW * receive queues, and the response queue lookup table to narrow that * down to the response queues actually configured for each port. * We always configure the RSS mapping for two ports since the mapping * table has plenty of entries. */static void setup_rss(struct adapter *adap){ int i; unsigned int nq0 = adap2pinfo(adap, 0)->nqsets; unsigned int nq1 = adap->port[1] ? adap2pinfo(adap, 1)->nqsets : 1; u8 cpus[SGE_QSETS + 1]; u16 rspq_map[RSS_TABLE_SIZE]; for (i = 0; i < SGE_QSETS; ++i) cpus[i] = i; cpus[SGE_QSETS] = 0xff; /* terminator */ for (i = 0; i < RSS_TABLE_SIZE / 2; ++i) { rspq_map[i] = i % nq0; rspq_map[i + RSS_TABLE_SIZE / 2] = (i % nq1) + nq0; } t3_config_rss(adap, F_RQFEEDBACKENABLE | F_TNLLKPEN | F_TNLMAPEN | F_TNLPRTEN | F_TNL2TUPEN | F_TNL4TUPEN | V_RRCPLCPUSIZE(6), cpus, rspq_map);}static void init_napi(struct adapter *adap){ int i; for (i = 0; i < SGE_QSETS; i++) { struct sge_qset *qs = &adap->sge.qs[i]; if (qs->adap) netif_napi_add(qs->netdev, &qs->napi, qs->napi.poll, 64); }}/* * Wait until all NAPI handlers are descheduled. This includes the handlers of * both netdevices representing interfaces and the dummy ones for the extra * queues. */static void quiesce_rx(struct adapter *adap){ int i; for (i = 0; i < SGE_QSETS; i++) if (adap->sge.qs[i].adap) napi_disable(&adap->sge.qs[i].napi);}static void enable_all_napi(struct adapter *adap){ int i; for (i = 0; i < SGE_QSETS; i++) if (adap->sge.qs[i].adap) napi_enable(&adap->sge.qs[i].napi);}/** * setup_sge_qsets - configure SGE Tx/Rx/response queues * @adap: the adapter * * Determines how many sets of SGE queues to use and initializes them. * We support multiple queue sets per port if we have MSI-X, otherwise * just one queue set per port. */static int setup_sge_qsets(struct adapter *adap){ int i, j, err, irq_idx = 0, qset_idx = 0; unsigned int ntxq = SGE_TXQ_PER_SET; if (adap->params.rev > 0 && !(adap->flags & USING_MSI)) irq_idx = -1; for_each_port(adap, i) { struct net_device *dev = adap->port[i]; struct port_info *pi = netdev_priv(dev); pi->qs = &adap->sge.qs[pi->first_qset]; for (j = 0; j < pi->nqsets; ++j, ++qset_idx) { err = t3_sge_alloc_qset(adap, qset_idx, 1, (adap->flags & USING_MSIX) ? qset_idx + 1 : irq_idx, &adap->params.sge.qset[qset_idx], ntxq, dev); if (err) { t3_free_sge_resources(adap); return err; } } } return 0;}static ssize_t attr_show(struct device *d, struct device_attribute *attr, char *buf, ssize_t(*format) (struct net_device *, char *)){ ssize_t len; /* Synchronize with ioctls that may shut down the device */ rtnl_lock(); len = (*format) (to_net_dev(d), buf); rtnl_unlock(); return len;}static ssize_t attr_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len, ssize_t(*set) (struct net_device *, unsigned int), unsigned int min_val, unsigned int max_val){ char *endp; ssize_t ret; unsigned int val; if (!capable(CAP_NET_ADMIN)) return -EPERM; val = simple_strtoul(buf, &endp, 0); if (endp == buf || val < min_val || val > max_val) return -EINVAL; rtnl_lock(); ret = (*set) (to_net_dev(d), val); if (!ret) ret = len; rtnl_unlock(); return ret;}#define CXGB3_SHOW(name, val_expr) \static ssize_t format_##name(struct net_device *dev, char *buf) \{ \ struct port_info *pi = netdev_priv(dev); \ struct adapter *adap = pi->adapter; \ return sprintf(buf, "%u\n", val_expr); \} \static ssize_t show_##name(struct device *d, struct device_attribute *attr, \ char *buf) \{ \ return attr_show(d, attr, buf, format_##name); \}static ssize_t set_nfilters(struct net_device *dev, unsigned int val){ struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; int min_tids = is_offload(adap) ? MC5_MIN_TIDS : 0; if (adap->flags & FULL_INIT_DONE) return -EBUSY; if (val && adap->params.rev == 0) return -EINVAL; if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nservers - min_tids) return -EINVAL; adap->params.mc5.nfilters = val; return 0;}static ssize_t store_nfilters(struct device *d, struct device_attribute *attr, const char *buf, size_t len){ return attr_store(d, attr, buf, len, set_nfilters, 0, ~0);}static ssize_t set_nservers(struct net_device *dev, unsigned int val){ struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; if (adap->flags & FULL_INIT_DONE) return -EBUSY; if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nfilters - MC5_MIN_TIDS) return -EINVAL; adap->params.mc5.nservers = val; return 0;}static ssize_t store_nservers(struct device *d, struct device_attribute *attr, const char *buf, size_t len){ return attr_store(d, attr, buf, len, set_nservers, 0, ~0);}#define CXGB3_ATTR_R(name, val_expr) \CXGB3_SHOW(name, val_expr) \
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?