📄 qla_os.c
字号:
/* * QLOGIC LINUX SOFTWARE * * QLogic ISP2x00 device driver for Linux 2.6.x * Copyright (C) 2003-2004 QLogic Corporation * (www.qlogic.com) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * */#include "qla_def.h"#include <linux/moduleparam.h>#include <linux/vmalloc.h>#include <linux/smp_lock.h>#include <linux/delay.h>#include <scsi/scsi_tcq.h>#include <scsi/scsicam.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_transport_fc.h>/* * Driver version */char qla2x00_version_str[40];/* * SRB allocation cache */char srb_cachep_name[16];kmem_cache_t *srb_cachep;/* * Stats for all adpaters. */struct _qla2x00stats qla2x00_stats;/* * Ioctl related information. */int num_hosts;int apiHBAInstance;/* * Module parameter information and variables */int ql2xmaxqdepth;module_param(ql2xmaxqdepth, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(ql2xmaxqdepth, "Maximum queue depth to report for target devices.");int ql2xlogintimeout = 20;module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR);MODULE_PARM_DESC(ql2xlogintimeout, "Login timeout value in seconds.");int qlport_down_retry;module_param(qlport_down_retry, int, S_IRUGO|S_IRUSR);MODULE_PARM_DESC(qlport_down_retry, "Maximum number of command retries to a port that returns" "a PORT-DOWN status.");int ql2xretrycount = 20;module_param(ql2xretrycount, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(ql2xretrycount, "Maximum number of mid-layer retries allowed for a command. " "Default value is 20, ");int displayConfig;module_param(displayConfig, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(displayConfig, "If 1 then display the configuration used in /etc/modprobe.conf.");int ql2xplogiabsentdevice;module_param(ql2xplogiabsentdevice, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(ql2xplogiabsentdevice, "Option to enable PLOGI to devices that are not present after " "a Fabric scan. This is needed for several broken switches." "Default is 0 - no PLOGI. 1 - perfom PLOGI.");int ql2xenablezio = 0;module_param(ql2xenablezio, int, S_IRUGO|S_IRUSR);MODULE_PARM_DESC(ql2xenablezio, "Option to enable ZIO:If 1 then enable it otherwise" " use the default set in the NVRAM." " Default is 0 : disabled");int ql2xintrdelaytimer = 10;module_param(ql2xintrdelaytimer, int, S_IRUGO|S_IRUSR);MODULE_PARM_DESC(ql2xintrdelaytimer, "ZIO: Waiting time for Firmware before it generates an " "interrupt to the host to notify completion of request.");int ConfigRequired;module_param(ConfigRequired, int, S_IRUGO|S_IRUSR);MODULE_PARM_DESC(ConfigRequired, "If 1, then only configured devices passed in through the" "ql2xopts parameter will be presented to the OS");int Bind = BIND_BY_PORT_NAME;module_param(Bind, int, S_IRUGO|S_IRUSR);MODULE_PARM_DESC(Bind, "Target persistent binding method: " "0 by Portname (default); 1 by PortID; 2 by Nodename. ");int ql2xsuspendcount = SUSPEND_COUNT;module_param(ql2xsuspendcount, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(ql2xsuspendcount, "Number of 6-second suspend iterations to perform while a " "target returns a <NOT READY> status. Default is 10 " "iterations.");int ql2xdoinitscan = 1;module_param(ql2xdoinitscan, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(ql2xdoinitscan, "Signal mid-layer to perform scan after driver load: 0 -- no " "signal sent to mid-layer.");int ql2xloginretrycount = 0;module_param(ql2xloginretrycount, int, S_IRUGO|S_IRUSR);MODULE_PARM_DESC(ql2xloginretrycount, "Specify an alternate value for the NVRAM login retry count.");/* * Proc structures and functions */struct info_str { char *buffer; int length; off_t offset; int pos;};static void copy_mem_info(struct info_str *, char *, int);static int copy_info(struct info_str *, char *, ...);static void qla2x00_free_device(scsi_qla_host_t *);static void qla2x00_config_dma_addressing(scsi_qla_host_t *ha);/* * SCSI host template entry points */static int qla2xxx_slave_configure(struct scsi_device * device);static int qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *));static int qla2xxx_eh_abort(struct scsi_cmnd *);static int qla2xxx_eh_device_reset(struct scsi_cmnd *);static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);static int qla2xxx_eh_host_reset(struct scsi_cmnd *);static int qla2x00_loop_reset(scsi_qla_host_t *ha);static int qla2x00_device_reset(scsi_qla_host_t *, fc_port_t *);static int qla2x00_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);static struct scsi_host_template qla2x00_driver_template = { .module = THIS_MODULE, .name = "qla2xxx", .proc_name = "qla2xxx", .proc_info = qla2x00_proc_info, .queuecommand = qla2x00_queuecommand, .eh_abort_handler = qla2xxx_eh_abort, .eh_device_reset_handler = qla2xxx_eh_device_reset, .eh_bus_reset_handler = qla2xxx_eh_bus_reset, .eh_host_reset_handler = qla2xxx_eh_host_reset, .slave_configure = qla2xxx_slave_configure, .this_id = -1, .cmd_per_lun = 3, .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = SG_ALL, /* * The RISC allows for each command to transfer (2^32-1) bytes of data, * which equates to 0x800000 sectors. */ .max_sectors = 0xFFFF,};static struct scsi_transport_template *qla2xxx_transport_template = NULL;static void qla2x00_display_fc_names(scsi_qla_host_t *);/* TODO Convert to inlines * * Timer routines */#define WATCH_INTERVAL 1 /* number of seconds */static void qla2x00_timer(scsi_qla_host_t *);static __inline__ void qla2x00_start_timer(scsi_qla_host_t *, void *, unsigned long);static __inline__ void qla2x00_restart_timer(scsi_qla_host_t *, unsigned long);static __inline__ void qla2x00_stop_timer(scsi_qla_host_t *);static inline voidqla2x00_start_timer(scsi_qla_host_t *ha, void *func, unsigned long interval){ 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 inline voidqla2x00_restart_timer(scsi_qla_host_t *ha, unsigned long interval){ mod_timer(&ha->timer, jiffies + interval * HZ);}static __inline__ voidqla2x00_stop_timer(scsi_qla_host_t *ha){ del_timer_sync(&ha->timer); ha->timer_active = 0;}void qla2x00_cmd_timeout(srb_t *);static __inline__ void qla2x00_callback(scsi_qla_host_t *, struct scsi_cmnd *);static __inline__ void sp_put(struct scsi_qla_host * ha, srb_t *sp);static __inline__ void sp_get(struct scsi_qla_host * ha, srb_t *sp);static __inline__ voidqla2x00_delete_from_done_queue(scsi_qla_host_t *, srb_t *); /** qla2x00_callback* Returns the completed SCSI command to LINUX.** Input:* ha -- Host adapter structure* cmd -- SCSI mid-level command structure.* Returns:* None* Note:From failover point of view we always get the sp* from vis_ha pool in queuecommand.So when we put it * back to the pool it has to be the vis_ha. * So rely on struct scsi_cmnd to get the vis_ha and not on sp. */static inline voidqla2x00_callback(scsi_qla_host_t *ha, struct scsi_cmnd *cmd){ srb_t *sp = (srb_t *) CMD_SP(cmd); scsi_qla_host_t *vis_ha; os_lun_t *lq; int got_sense; unsigned long cpu_flags = 0; cmd->host_scribble = (unsigned char *) NULL; vis_ha = (scsi_qla_host_t *) cmd->device->host->hostdata; if (sp == NULL) { qla_printk(KERN_INFO, ha, "%s(): **** CMD derives a NULL SP\n", __func__); DEBUG2(BUG();) return; } /* * If command status is not DID_BUS_BUSY then go ahead and freed sp. */ /* * Cancel command timeout */ qla2x00_delete_timer_from_cmd(sp); /* * Put SP back in the free queue */ sp->cmd = NULL; CMD_SP(cmd) = NULL; lq = sp->lun_queue; got_sense = (sp->flags & SRB_GOT_SENSE)? 1: 0; add_to_free_queue(vis_ha, sp); if (host_byte(cmd->result) == DID_OK) { /* device ok */ ha->total_bytes += cmd->bufflen; if (!got_sense) { /* If lun was suspended then clear retry count */ spin_lock_irqsave(&lq->q_lock, cpu_flags); if (!test_bit(LUN_EXEC_DELAYED, &lq->q_flag)) lq->q_state = LUN_STATE_READY; spin_unlock_irqrestore(&lq->q_lock, cpu_flags); } } else if (host_byte(cmd->result) == DID_ERROR) { /* device error */ ha->total_dev_errs++; } /* Call the mid-level driver interrupt handler */ (*(cmd)->scsi_done)(cmd);}/*************************************************************************** sp_put** Description:* Decrement reference count and call the callback if we're the last* owner of the specified sp. Will get the host_lock before calling* the callback.** Input:* ha - pointer to the scsi_qla_host_t where the callback is to occur.* sp - pointer to srb_t structure to use.** Returns:***************************************************************************/static inline voidsp_put(struct scsi_qla_host * ha, srb_t *sp){ if (atomic_read(&sp->ref_count) == 0) { qla_printk(KERN_INFO, ha, "%s(): **** SP->ref_count not zero\n", __func__); DEBUG2(BUG();) return; } if (!atomic_dec_and_test(&sp->ref_count)) { return; } qla2x00_callback(ha, sp->cmd);}/*************************************************************************** sp_get** Description:* Increment reference count of the specified sp.** Input:* sp - pointer to srb_t structure to use.** Returns:***************************************************************************/static inline voidsp_get(struct scsi_qla_host * ha, srb_t *sp){ atomic_inc(&sp->ref_count); if (atomic_read(&sp->ref_count) > 2) { qla_printk(KERN_INFO, ha, "%s(): **** SP->ref_count greater than two\n", __func__); DEBUG2(BUG();) return; }}static inline void qla2x00_delete_from_done_queue(scsi_qla_host_t *dest_ha, srb_t *sp) { /* remove command from done list */ list_del_init(&sp->list); dest_ha->done_q_cnt--; sp->state = SRB_NO_QUEUE_STATE; if (sp->flags & SRB_DMA_VALID) { sp->flags &= ~SRB_DMA_VALID; /* Release memory used for this I/O */ if (sp->cmd->use_sg) { pci_unmap_sg(dest_ha->pdev, sp->cmd->request_buffer, sp->cmd->use_sg, sp->cmd->sc_data_direction); } else if (sp->cmd->request_bufflen) { pci_unmap_page(dest_ha->pdev, sp->dma_handle, sp->cmd->request_bufflen, sp->cmd->sc_data_direction); } }}static int qla2x00_do_dpc(void *data);static void qla2x00_rst_aen(scsi_qla_host_t *);static uint8_t qla2x00_mem_alloc(scsi_qla_host_t *);static void qla2x00_mem_free(scsi_qla_host_t *ha);static int qla2x00_allocate_sp_pool( scsi_qla_host_t *ha);static void qla2x00_free_sp_pool(scsi_qla_host_t *ha);static srb_t *qla2x00_get_new_sp(scsi_qla_host_t *ha);static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *, char *, loff_t, size_t);static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *, char *, loff_t, size_t);static struct bin_attribute sysfs_fw_dump_attr = { .attr = { .name = "fw_dump", .mode = S_IRUSR | S_IWUSR, .owner = THIS_MODULE, }, .size = 0, .read = qla2x00_sysfs_read_fw_dump, .write = qla2x00_sysfs_write_fw_dump,};static ssize_t qla2x00_sysfs_read_nvram(struct kobject *, char *, loff_t, size_t);static ssize_t qla2x00_sysfs_write_nvram(struct kobject *, char *, loff_t, size_t);static struct bin_attribute sysfs_nvram_attr = { .attr = { .name = "nvram", .mode = S_IRUSR | S_IWUSR, .owner = THIS_MODULE, }, .size = sizeof(nvram_t), .read = qla2x00_sysfs_read_nvram, .write = qla2x00_sysfs_write_nvram,};/* -------------------------------------------------------------------------- *//* SysFS attributes. */static ssize_t qla2x00_sysfs_read_fw_dump(struct kobject *kobj, char *buf, loff_t off, size_t count){ struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); if (ha->fw_dump_reading == 0) return 0; if (off > ha->fw_dump_buffer_len) return 0; if (off + count > ha->fw_dump_buffer_len) count = ha->fw_dump_buffer_len - off; memcpy(buf, &ha->fw_dump_buffer[off], count); return (count);}static ssize_t qla2x00_sysfs_write_fw_dump(struct kobject *kobj, char *buf, loff_t off, size_t count){ struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); int reading; uint32_t dump_size; if (off != 0) return (0); reading = simple_strtol(buf, NULL, 10); switch (reading) { case 0: if (ha->fw_dump_reading == 1) { qla_printk(KERN_INFO, ha, "Firmware dump cleared on (%ld).\n", ha->host_no); vfree(ha->fw_dump_buffer); free_pages((unsigned long)ha->fw_dump, ha->fw_dump_order); ha->fw_dump_reading = 0; ha->fw_dump_buffer = NULL; ha->fw_dump = NULL; } break; case 1: if (ha->fw_dump != NULL && !ha->fw_dump_reading) { ha->fw_dump_reading = 1; dump_size = FW_DUMP_SIZE_1M; if (ha->fw_memory_size < 0x20000) dump_size = FW_DUMP_SIZE_128K; else if (ha->fw_memory_size < 0x80000) dump_size = FW_DUMP_SIZE_512K; ha->fw_dump_buffer = (char *)vmalloc(dump_size); if (ha->fw_dump_buffer == NULL) { qla_printk(KERN_WARNING, ha, "Unable to allocate memory for firmware " "dump buffer (%d).\n", dump_size); ha->fw_dump_reading = 0; return (count); } qla_printk(KERN_INFO, ha, "Firmware dump ready for read on (%ld).\n", ha->host_no); memset(ha->fw_dump_buffer, 0, dump_size); if (IS_QLA2100(ha) || IS_QLA2200(ha)) qla2100_ascii_fw_dump(ha); else qla2300_ascii_fw_dump(ha); ha->fw_dump_buffer_len = strlen(ha->fw_dump_buffer); } break; } return (count);}static ssize_t qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -