ql4_os.c

来自「linux 内核源代码」· C语言 代码 · 共 1,732 行 · 第 1/4 页

C
1,732
字号
/* * QLogic iSCSI HBA Driver * Copyright (c)  2003-2006 QLogic Corporation * * See LICENSE.qla4xxx for copyright and licensing details. */#include <linux/moduleparam.h>#include <scsi/scsi_tcq.h>#include <scsi/scsicam.h>#include "ql4_def.h"#include "ql4_version.h"#include "ql4_glbl.h"#include "ql4_dbg.h"#include "ql4_inline.h"/* * Driver version */static char qla4xxx_version_str[40];/* * SRB allocation cache */static struct kmem_cache *srb_cachep;/* * Module parameter information and variables */int ql4xdiscoverywait = 60;module_param(ql4xdiscoverywait, int, S_IRUGO | S_IRUSR);MODULE_PARM_DESC(ql4xdiscoverywait, "Discovery wait time");int ql4xdontresethba = 0;module_param(ql4xdontresethba, int, S_IRUGO | S_IRUSR);MODULE_PARM_DESC(ql4xdontresethba,		 "Dont reset the HBA when the driver gets 0x8002 AEN "		 " default it will reset hba :0"		 " set to 1 to avoid resetting HBA");int ql4xextended_error_logging = 0; /* 0 = off, 1 = log errors */module_param(ql4xextended_error_logging, int, S_IRUGO | S_IRUSR);MODULE_PARM_DESC(ql4xextended_error_logging,		 "Option to enable extended error logging, "		 "Default is 0 - no logging, 1 - debug logging");int ql4_mod_unload = 0;/* * SCSI host template entry points */static void qla4xxx_config_dma_addressing(struct scsi_qla_host *ha);/* * iSCSI template entry points */static int qla4xxx_tgt_dscvr(struct Scsi_Host *shost,			     enum iscsi_tgt_dscvr type, uint32_t enable,			     struct sockaddr *dst_addr);static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn,				  enum iscsi_param param, char *buf);static int qla4xxx_sess_get_param(struct iscsi_cls_session *sess,				  enum iscsi_param param, char *buf);static int qla4xxx_host_get_param(struct Scsi_Host *shost,				  enum iscsi_host_param param, char *buf);static void qla4xxx_conn_stop(struct iscsi_cls_conn *conn, int flag);static int qla4xxx_conn_start(struct iscsi_cls_conn *conn);static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session);/* * SCSI host template entry points */static int qla4xxx_queuecommand(struct scsi_cmnd *cmd,				void (*done) (struct scsi_cmnd *));static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd);static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd);static int qla4xxx_slave_alloc(struct scsi_device *device);static int qla4xxx_slave_configure(struct scsi_device *device);static void qla4xxx_slave_destroy(struct scsi_device *sdev);static struct scsi_host_template qla4xxx_driver_template = {	.module			= THIS_MODULE,	.name			= DRIVER_NAME,	.proc_name		= DRIVER_NAME,	.queuecommand		= qla4xxx_queuecommand,	.eh_device_reset_handler = qla4xxx_eh_device_reset,	.eh_host_reset_handler	= qla4xxx_eh_host_reset,	.slave_configure	= qla4xxx_slave_configure,	.slave_alloc		= qla4xxx_slave_alloc,	.slave_destroy		= qla4xxx_slave_destroy,	.this_id		= -1,	.cmd_per_lun		= 3,	.use_clustering		= ENABLE_CLUSTERING,	.use_sg_chaining	= ENABLE_SG_CHAINING,	.sg_tablesize		= SG_ALL,	.max_sectors		= 0xFFFF,};static struct iscsi_transport qla4xxx_iscsi_transport = {	.owner			= THIS_MODULE,	.name			= DRIVER_NAME,	.caps			= CAP_FW_DB | CAP_SENDTARGETS_OFFLOAD |				  CAP_DATA_PATH_OFFLOAD,	.param_mask		= ISCSI_CONN_PORT | ISCSI_CONN_ADDRESS |				  ISCSI_TARGET_NAME | ISCSI_TPGT,	.host_param_mask	= ISCSI_HOST_HWADDRESS |				  ISCSI_HOST_IPADDRESS |				  ISCSI_HOST_INITIATOR_NAME,	.sessiondata_size	= sizeof(struct ddb_entry),	.host_template		= &qla4xxx_driver_template,	.tgt_dscvr		= qla4xxx_tgt_dscvr,	.get_conn_param		= qla4xxx_conn_get_param,	.get_session_param	= qla4xxx_sess_get_param,	.get_host_param		= qla4xxx_host_get_param,	.start_conn		= qla4xxx_conn_start,	.stop_conn		= qla4xxx_conn_stop,	.session_recovery_timedout = qla4xxx_recovery_timedout,};static struct scsi_transport_template *qla4xxx_scsi_transport;static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session){	struct ddb_entry *ddb_entry = session->dd_data;	struct scsi_qla_host *ha = ddb_entry->ha;	DEBUG2(printk("scsi%ld: %s: index [%d] port down retry count of (%d) "		      "secs exhausted, marking device DEAD.\n", ha->host_no,		      __func__, ddb_entry->fw_ddb_index,		      ha->port_down_retry_count));	atomic_set(&ddb_entry->state, DDB_STATE_DEAD);	DEBUG2(printk("scsi%ld: %s: scheduling dpc routine - dpc flags = "		      "0x%lx\n", ha->host_no, __func__, ha->dpc_flags));	queue_work(ha->dpc_thread, &ha->dpc_work);}static int qla4xxx_conn_start(struct iscsi_cls_conn *conn){	struct iscsi_cls_session *session;	struct ddb_entry *ddb_entry;	session = iscsi_dev_to_session(conn->dev.parent);	ddb_entry = session->dd_data;	DEBUG2(printk("scsi%ld: %s: index [%d] starting conn\n",		      ddb_entry->ha->host_no, __func__,		      ddb_entry->fw_ddb_index));	iscsi_unblock_session(session);	return 0;}static void qla4xxx_conn_stop(struct iscsi_cls_conn *conn, int flag){	struct iscsi_cls_session *session;	struct ddb_entry *ddb_entry;	session = iscsi_dev_to_session(conn->dev.parent);	ddb_entry = session->dd_data;	DEBUG2(printk("scsi%ld: %s: index [%d] stopping conn\n",		      ddb_entry->ha->host_no, __func__,		      ddb_entry->fw_ddb_index));	if (flag == STOP_CONN_RECOVER)		iscsi_block_session(session);	else		printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag);}static ssize_t format_addr(char *buf, const unsigned char *addr, int len){	int i;	char *cp = buf;	for (i = 0; i < len; i++)		cp += sprintf(cp, "%02x%c", addr[i],			      i == (len - 1) ? '\n' : ':');	return cp - buf;}static int qla4xxx_host_get_param(struct Scsi_Host *shost,				  enum iscsi_host_param param, char *buf){	struct scsi_qla_host *ha = to_qla_host(shost);	int len;	switch (param) {	case ISCSI_HOST_PARAM_HWADDRESS:		len = format_addr(buf, ha->my_mac, MAC_ADDR_LEN);		break;	case ISCSI_HOST_PARAM_IPADDRESS:		len = sprintf(buf, "%d.%d.%d.%d\n", ha->ip_address[0],			      ha->ip_address[1], ha->ip_address[2],			      ha->ip_address[3]);		break;	case ISCSI_HOST_PARAM_INITIATOR_NAME:		len = sprintf(buf, "%s\n", ha->name_string);		break;	default:		return -ENOSYS;	}	return len;}static int qla4xxx_sess_get_param(struct iscsi_cls_session *sess,				  enum iscsi_param param, char *buf){	struct ddb_entry *ddb_entry = sess->dd_data;	int len;	switch (param) {	case ISCSI_PARAM_TARGET_NAME:		len = snprintf(buf, PAGE_SIZE - 1, "%s\n",			       ddb_entry->iscsi_name);		break;	case ISCSI_PARAM_TPGT:		len = sprintf(buf, "%u\n", ddb_entry->tpgt);		break;	default:		return -ENOSYS;	}	return len;}static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn,				  enum iscsi_param param, char *buf){	struct iscsi_cls_session *session;	struct ddb_entry *ddb_entry;	int len;	session = iscsi_dev_to_session(conn->dev.parent);	ddb_entry = session->dd_data;	switch (param) {	case ISCSI_PARAM_CONN_PORT:		len = sprintf(buf, "%hu\n", ddb_entry->port);		break;	case ISCSI_PARAM_CONN_ADDRESS:		/* TODO: what are the ipv6 bits */		len = sprintf(buf, "%u.%u.%u.%u\n",			      NIPQUAD(ddb_entry->ip_addr));		break;	default:		return -ENOSYS;	}	return len;}static int qla4xxx_tgt_dscvr(struct Scsi_Host *shost,			     enum iscsi_tgt_dscvr type, uint32_t enable,			     struct sockaddr *dst_addr){	struct scsi_qla_host *ha;	struct sockaddr_in *addr;	struct sockaddr_in6 *addr6;	int ret = 0;	ha = (struct scsi_qla_host *) shost->hostdata;	switch (type) {	case ISCSI_TGT_DSCVR_SEND_TARGETS:		if (dst_addr->sa_family == AF_INET) {			addr = (struct sockaddr_in *)dst_addr;			if (qla4xxx_send_tgts(ha, (char *)&addr->sin_addr,					      addr->sin_port) != QLA_SUCCESS)				ret = -EIO;		} else if (dst_addr->sa_family == AF_INET6) {			/*			 * TODO: fix qla4xxx_send_tgts			 */			addr6 = (struct sockaddr_in6 *)dst_addr;			if (qla4xxx_send_tgts(ha, (char *)&addr6->sin6_addr,					      addr6->sin6_port) != QLA_SUCCESS)				ret = -EIO;		} else			ret = -ENOSYS;		break;	default:		ret = -ENOSYS;	}	return ret;}void qla4xxx_destroy_sess(struct ddb_entry *ddb_entry){	if (!ddb_entry->sess)		return;	if (ddb_entry->conn) {		iscsi_if_destroy_session_done(ddb_entry->conn);		iscsi_destroy_conn(ddb_entry->conn);		iscsi_remove_session(ddb_entry->sess);	}	iscsi_free_session(ddb_entry->sess);}int qla4xxx_add_sess(struct ddb_entry *ddb_entry){	int err;	err = iscsi_add_session(ddb_entry->sess, ddb_entry->fw_ddb_index);	if (err) {		DEBUG2(printk(KERN_ERR "Could not add session.\n"));		return err;	}	ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0);	if (!ddb_entry->conn) {		iscsi_remove_session(ddb_entry->sess);		DEBUG2(printk(KERN_ERR "Could not add connection.\n"));		return -ENOMEM;	}	ddb_entry->sess->recovery_tmo = ddb_entry->ha->port_down_retry_count;	iscsi_if_create_session_done(ddb_entry->conn);	return 0;}struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha){	struct ddb_entry *ddb_entry;	struct iscsi_cls_session *sess;	sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport);	if (!sess)		return NULL;	ddb_entry = sess->dd_data;	memset(ddb_entry, 0, sizeof(*ddb_entry));	ddb_entry->ha = ha;	ddb_entry->sess = sess;	return ddb_entry;}/* * Timer routines */static void qla4xxx_start_timer(struct scsi_qla_host *ha, void *func,				unsigned long interval){	DEBUG(printk("scsi: %s: Starting timer thread for adapter %d\n",		     __func__, ha->host->host_no));	init_timer(&ha->timer);	ha->timer.expires = jiffies + interval * HZ;	ha->timer.data = (unsigned long)ha;	ha->timer.function = (void (*)(unsigned long))func;	add_timer(&ha->timer);	ha->timer_active = 1;}static void qla4xxx_stop_timer(struct scsi_qla_host *ha){	del_timer_sync(&ha->timer);	ha->timer_active = 0;}/*** * qla4xxx_mark_device_missing - mark a device as missing. * @ha: Pointer to host adapter structure. * @ddb_entry: Pointer to device database entry * * This routine marks a device missing and resets the relogin retry count. **/void qla4xxx_mark_device_missing(struct scsi_qla_host *ha,				 struct ddb_entry *ddb_entry){	atomic_set(&ddb_entry->state, DDB_STATE_MISSING);	DEBUG3(printk("scsi%d:%d:%d: index [%d] marked MISSING\n",		      ha->host_no, ddb_entry->bus, ddb_entry->target,		      ddb_entry->fw_ddb_index));	iscsi_conn_error(ddb_entry->conn, ISCSI_ERR_CONN_FAILED);}static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha,				       struct ddb_entry *ddb_entry,				       struct scsi_cmnd *cmd,				       void (*done)(struct scsi_cmnd *)){	struct srb *srb;	srb = mempool_alloc(ha->srb_mempool, GFP_ATOMIC);	if (!srb)		return srb;	atomic_set(&srb->ref_count, 1);	srb->ha = ha;	srb->ddb = ddb_entry;	srb->cmd = cmd;	srb->flags = 0;	cmd->SCp.ptr = (void *)srb;	cmd->scsi_done = done;	return srb;}static void qla4xxx_srb_free_dma(struct scsi_qla_host *ha, struct srb *srb){	struct scsi_cmnd *cmd = srb->cmd;	if (srb->flags & SRB_DMA_VALID) {		scsi_dma_unmap(cmd);		srb->flags &= ~SRB_DMA_VALID;	}	cmd->SCp.ptr = NULL;}void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb){	struct scsi_cmnd *cmd = srb->cmd;	qla4xxx_srb_free_dma(ha, srb);	mempool_free(srb, ha->srb_mempool);	cmd->scsi_done(cmd);}/** * qla4xxx_queuecommand - scsi layer issues scsi command to driver. * @cmd: Pointer to Linux's SCSI command structure * @done_fn: Function that the driver calls to notify the SCSI mid-layer

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?