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 + -
显示快捷键?