| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
| Mike Miller | bd4f36d | 2007-10-24 10:30:34 +0200 | [diff] [blame] | 2 |  *    Disk Array driver for HP Smart Array controllers. | 
 | 3 |  *    (C) Copyright 2000, 2007 Hewlett-Packard Development Company, L.P. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4 |  * | 
 | 5 |  *    This program is free software; you can redistribute it and/or modify | 
 | 6 |  *    it under the terms of the GNU General Public License as published by | 
| Mike Miller | bd4f36d | 2007-10-24 10:30:34 +0200 | [diff] [blame] | 7 |  *    the Free Software Foundation; version 2 of the License. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 8 |  * | 
 | 9 |  *    This program is distributed in the hope that it will be useful, | 
 | 10 |  *    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| Mike Miller | bd4f36d | 2007-10-24 10:30:34 +0200 | [diff] [blame] | 11 |  *    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 
 | 12 |  *    General Public License for more details. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 13 |  * | 
 | 14 |  *    You should have received a copy of the GNU General Public License | 
 | 15 |  *    along with this program; if not, write to the Free Software | 
| Mike Miller | bd4f36d | 2007-10-24 10:30:34 +0200 | [diff] [blame] | 16 |  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 
 | 17 |  *    02111-1307, USA. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 |  * | 
 | 19 |  *    Questions/Comments/Bugfixes to iss_storagedev@hp.com | 
 | 20 |  * | 
 | 21 |  */ | 
 | 22 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 23 | #include <linux/module.h> | 
 | 24 | #include <linux/interrupt.h> | 
 | 25 | #include <linux/types.h> | 
 | 26 | #include <linux/pci.h> | 
 | 27 | #include <linux/kernel.h> | 
 | 28 | #include <linux/slab.h> | 
 | 29 | #include <linux/delay.h> | 
 | 30 | #include <linux/major.h> | 
 | 31 | #include <linux/fs.h> | 
 | 32 | #include <linux/bio.h> | 
 | 33 | #include <linux/blkpg.h> | 
 | 34 | #include <linux/timer.h> | 
 | 35 | #include <linux/proc_fs.h> | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 36 | #include <linux/seq_file.h> | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 37 | #include <linux/init.h> | 
| Randy Dunlap | 4d76160 | 2009-09-18 12:58:48 -0700 | [diff] [blame] | 38 | #include <linux/jiffies.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 39 | #include <linux/hdreg.h> | 
 | 40 | #include <linux/spinlock.h> | 
 | 41 | #include <linux/compat.h> | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 42 | #include <linux/mutex.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 43 | #include <asm/uaccess.h> | 
 | 44 | #include <asm/io.h> | 
 | 45 |  | 
| mike.miller@hp.com | eb0df99 | 2005-06-10 14:51:04 -0500 | [diff] [blame] | 46 | #include <linux/dma-mapping.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 47 | #include <linux/blkdev.h> | 
 | 48 | #include <linux/genhd.h> | 
 | 49 | #include <linux/completion.h> | 
| Stephen Cameron | d5d3b73 | 2007-05-08 00:30:02 -0700 | [diff] [blame] | 50 | #include <scsi/scsi.h> | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 51 | #include <scsi/sg.h> | 
 | 52 | #include <scsi/scsi_ioctl.h> | 
 | 53 | #include <linux/cdrom.h> | 
| Mike Pagano | 231bc2a | 2008-04-10 21:29:26 -0700 | [diff] [blame] | 54 | #include <linux/scatterlist.h> | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 55 | #include <linux/kthread.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 56 |  | 
 | 57 | #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) | 
| Mike Miller | 841fdff | 2010-06-02 12:58:09 -0700 | [diff] [blame] | 58 | #define DRIVER_NAME "HP CISS Driver (v 3.6.26)" | 
 | 59 | #define DRIVER_VERSION CCISS_DRIVER_VERSION(3, 6, 26) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 60 |  | 
 | 61 | /* Embedded module documentation macros - see modules.h */ | 
 | 62 | MODULE_AUTHOR("Hewlett-Packard Company"); | 
| Mike Miller | 24aac48 | 2008-06-12 15:21:34 -0700 | [diff] [blame] | 63 | MODULE_DESCRIPTION("Driver for HP Smart Array Controllers"); | 
| Mike Miller | 841fdff | 2010-06-02 12:58:09 -0700 | [diff] [blame] | 64 | MODULE_SUPPORTED_DEVICE("HP Smart Array Controllers"); | 
 | 65 | MODULE_VERSION("3.6.26"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 66 | MODULE_LICENSE("GPL"); | 
| Stephen M. Cameron | 8a4ec67 | 2011-05-03 14:54:12 -0500 | [diff] [blame] | 67 | static int cciss_tape_cmds = 6; | 
 | 68 | module_param(cciss_tape_cmds, int, 0644); | 
 | 69 | MODULE_PARM_DESC(cciss_tape_cmds, | 
 | 70 | 	"number of commands to allocate for tape devices (default: 6)"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 71 |  | 
| Arnd Bergmann | 2a48fc0 | 2010-06-02 14:28:52 +0200 | [diff] [blame] | 72 | static DEFINE_MUTEX(cciss_mutex); | 
| Jens Axboe | bbe425c | 2010-11-17 11:56:13 +0100 | [diff] [blame] | 73 | static struct proc_dir_entry *proc_cciss; | 
| Stephen M. Cameron | 2ec24ff | 2009-10-13 09:18:22 +0200 | [diff] [blame] | 74 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 75 | #include "cciss_cmd.h" | 
 | 76 | #include "cciss.h" | 
 | 77 | #include <linux/cciss_ioctl.h> | 
 | 78 |  | 
 | 79 | /* define the PCI info for the cards we can control */ | 
 | 80 | static const struct pci_device_id cciss_pci_device_id[] = { | 
| Bjorn Helgaas | f82ccdb | 2006-06-25 05:49:07 -0700 | [diff] [blame] | 81 | 	{PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISS,  0x0E11, 0x4070}, | 
 | 82 | 	{PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSB, 0x0E11, 0x4080}, | 
 | 83 | 	{PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSB, 0x0E11, 0x4082}, | 
 | 84 | 	{PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSB, 0x0E11, 0x4083}, | 
 | 85 | 	{PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, 0x0E11, 0x4091}, | 
 | 86 | 	{PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, 0x0E11, 0x409A}, | 
 | 87 | 	{PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, 0x0E11, 0x409B}, | 
 | 88 | 	{PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, 0x0E11, 0x409C}, | 
 | 89 | 	{PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, 0x0E11, 0x409D}, | 
 | 90 | 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSA,     0x103C, 0x3225}, | 
 | 91 | 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSC,     0x103C, 0x3223}, | 
 | 92 | 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSC,     0x103C, 0x3234}, | 
 | 93 | 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSC,     0x103C, 0x3235}, | 
 | 94 | 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSD,     0x103C, 0x3211}, | 
 | 95 | 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSD,     0x103C, 0x3212}, | 
 | 96 | 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSD,     0x103C, 0x3213}, | 
 | 97 | 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSD,     0x103C, 0x3214}, | 
 | 98 | 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSD,     0x103C, 0x3215}, | 
| Mike Miller | de92391 | 2006-12-06 20:35:03 -0800 | [diff] [blame] | 99 | 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSC,     0x103C, 0x3237}, | 
| Mike Miller (OS Dev | 9cff3b3 | 2007-06-19 20:52:18 +0200 | [diff] [blame] | 100 | 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSC,     0x103C, 0x323D}, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 101 | 	{0,} | 
 | 102 | }; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 103 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 104 | MODULE_DEVICE_TABLE(pci, cciss_pci_device_id); | 
 | 105 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 106 | /*  board_id = Subsystem Device ID & Vendor ID | 
 | 107 |  *  product = Marketing Name for the board | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 108 |  *  access = Address of the struct of function pointers | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 109 |  */ | 
 | 110 | static struct board_type products[] = { | 
| Mike Miller | 4915399 | 2008-07-04 10:00:02 -0700 | [diff] [blame] | 111 | 	{0x40700E11, "Smart Array 5300", &SA5_access}, | 
 | 112 | 	{0x40800E11, "Smart Array 5i", &SA5B_access}, | 
 | 113 | 	{0x40820E11, "Smart Array 532", &SA5B_access}, | 
 | 114 | 	{0x40830E11, "Smart Array 5312", &SA5B_access}, | 
 | 115 | 	{0x409A0E11, "Smart Array 641", &SA5_access}, | 
 | 116 | 	{0x409B0E11, "Smart Array 642", &SA5_access}, | 
 | 117 | 	{0x409C0E11, "Smart Array 6400", &SA5_access}, | 
 | 118 | 	{0x409D0E11, "Smart Array 6400 EM", &SA5_access}, | 
 | 119 | 	{0x40910E11, "Smart Array 6i", &SA5_access}, | 
 | 120 | 	{0x3225103C, "Smart Array P600", &SA5_access}, | 
| Stephen M. Cameron | 4205df3 | 2010-10-23 18:47:31 +0200 | [diff] [blame] | 121 | 	{0x3223103C, "Smart Array P800", &SA5_access}, | 
 | 122 | 	{0x3234103C, "Smart Array P400", &SA5_access}, | 
| Mike Miller | 4915399 | 2008-07-04 10:00:02 -0700 | [diff] [blame] | 123 | 	{0x3235103C, "Smart Array P400i", &SA5_access}, | 
 | 124 | 	{0x3211103C, "Smart Array E200i", &SA5_access}, | 
 | 125 | 	{0x3212103C, "Smart Array E200", &SA5_access}, | 
 | 126 | 	{0x3213103C, "Smart Array E200i", &SA5_access}, | 
 | 127 | 	{0x3214103C, "Smart Array E200i", &SA5_access}, | 
 | 128 | 	{0x3215103C, "Smart Array E200i", &SA5_access}, | 
 | 129 | 	{0x3237103C, "Smart Array E500", &SA5_access}, | 
| Stephen M. Cameron | 2ec24ff | 2009-10-13 09:18:22 +0200 | [diff] [blame] | 130 | 	{0x3223103C, "Smart Array P800", &SA5_access}, | 
 | 131 | 	{0x3234103C, "Smart Array P400", &SA5_access}, | 
| Mike Miller | 4915399 | 2008-07-04 10:00:02 -0700 | [diff] [blame] | 132 | 	{0x323D103C, "Smart Array P700m", &SA5_access}, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 133 | }; | 
 | 134 |  | 
| Bjorn Helgaas | d14c4ab | 2006-06-25 05:49:04 -0700 | [diff] [blame] | 135 | /* How long to wait (in milliseconds) for board to go into simple mode */ | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 136 | #define MAX_CONFIG_WAIT 30000 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 137 | #define MAX_IOCTL_CONFIG_WAIT 1000 | 
 | 138 |  | 
 | 139 | /*define how many times we will try a command because of bus resets */ | 
 | 140 | #define MAX_CMD_RETRIES 3 | 
 | 141 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 142 | #define MAX_CTLR	32 | 
 | 143 |  | 
 | 144 | /* Originally cciss driver only supports 8 major numbers */ | 
 | 145 | #define MAX_CTLR_ORIG 	8 | 
 | 146 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 147 | static ctlr_info_t *hba[MAX_CTLR]; | 
 | 148 |  | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 149 | static struct task_struct *cciss_scan_thread; | 
 | 150 | static DEFINE_MUTEX(scan_mutex); | 
 | 151 | static LIST_HEAD(scan_q); | 
 | 152 |  | 
| Jens Axboe | 165125e | 2007-07-24 09:28:11 +0200 | [diff] [blame] | 153 | static void do_cciss_request(struct request_queue *q); | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 154 | static irqreturn_t do_cciss_intx(int irq, void *dev_id); | 
 | 155 | static irqreturn_t do_cciss_msix_intr(int irq, void *dev_id); | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 156 | static int cciss_open(struct block_device *bdev, fmode_t mode); | 
| Arnd Bergmann | 6e9624b | 2010-08-07 18:25:34 +0200 | [diff] [blame] | 157 | static int cciss_unlocked_open(struct block_device *bdev, fmode_t mode); | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 158 | static int cciss_release(struct gendisk *disk, fmode_t mode); | 
| Arnd Bergmann | 8a6cfeb | 2010-07-08 10:18:46 +0200 | [diff] [blame] | 159 | static int do_ioctl(struct block_device *bdev, fmode_t mode, | 
 | 160 | 		    unsigned int cmd, unsigned long arg); | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 161 | static int cciss_ioctl(struct block_device *bdev, fmode_t mode, | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 162 | 		       unsigned int cmd, unsigned long arg); | 
| Christoph Hellwig | a885c8c | 2006-01-08 01:02:50 -0800 | [diff] [blame] | 163 | static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 164 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 165 | static int cciss_revalidate(struct gendisk *disk); | 
| Stephen M. Cameron | 2d11d99 | 2009-09-17 13:47:44 -0500 | [diff] [blame] | 166 | static int rebuild_lun_table(ctlr_info_t *h, int first_time, int via_ioctl); | 
| Stephen M. Cameron | a0ea862 | 2008-12-18 14:55:51 +0100 | [diff] [blame] | 167 | static int deregister_disk(ctlr_info_t *h, int drv_index, | 
| Stephen M. Cameron | 2d11d99 | 2009-09-17 13:47:44 -0500 | [diff] [blame] | 168 | 			   int clear_all, int via_ioctl); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 169 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 170 | static void cciss_read_capacity(ctlr_info_t *h, int logvol, | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 171 | 			sector_t *total_size, unsigned int *block_size); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 172 | static void cciss_read_capacity_16(ctlr_info_t *h, int logvol, | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 173 | 			sector_t *total_size, unsigned int *block_size); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 174 | static void cciss_geometry_inquiry(ctlr_info_t *h, int logvol, | 
| Stephen M. Cameron | 7b838bd | 2009-11-12 12:49:30 -0600 | [diff] [blame] | 175 | 			sector_t total_size, | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 176 | 			unsigned int block_size, InquiryData_struct *inq_buff, | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 177 | 				   drive_info_struct *drv); | 
| Stephen M. Cameron | dac5488 | 2010-07-19 13:45:05 -0500 | [diff] [blame] | 178 | static void __devinit cciss_interrupt_mode(ctlr_info_t *); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 179 | static void start_io(ctlr_info_t *h); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 180 | static int sendcmd_withirq(ctlr_info_t *h, __u8 cmd, void *buff, size_t size, | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 181 | 			__u8 page_code, unsigned char scsi3addr[], | 
 | 182 | 			int cmd_type); | 
| scameron@beardog.cca.cpqcorp.net | 85cc61a | 2009-06-08 16:07:45 -0500 | [diff] [blame] | 183 | static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c, | 
 | 184 | 	int attempt_retry); | 
 | 185 | static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 186 |  | 
| Andrew Patterson | d6f4965 | 2009-09-17 13:47:03 -0500 | [diff] [blame] | 187 | static int add_to_scan_list(struct ctlr_info *h); | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 188 | static int scan_thread(void *data); | 
 | 189 | static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c); | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 190 | static void cciss_hba_release(struct device *dev); | 
 | 191 | static void cciss_device_release(struct device *dev); | 
| Stephen M. Cameron | 361e9b0 | 2009-09-17 13:47:29 -0500 | [diff] [blame] | 192 | static void cciss_free_gendisk(ctlr_info_t *h, int drv_index); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 193 | static void cciss_free_drive_info(ctlr_info_t *h, int drv_index); | 
| Mike Miller | 29979a7 | 2010-06-11 13:13:35 +0200 | [diff] [blame] | 194 | static inline u32 next_command(ctlr_info_t *h); | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 195 | static int __devinit cciss_find_cfg_addrs(struct pci_dev *pdev, | 
 | 196 | 	void __iomem *vaddr, u32 *cfg_base_addr, u64 *cfg_base_addr_index, | 
 | 197 | 	u64 *cfg_offset); | 
 | 198 | static int __devinit cciss_pci_find_memory_BAR(struct pci_dev *pdev, | 
 | 199 | 	unsigned long *memory_bar); | 
| Stephen M. Cameron | 1601113 | 2011-03-12 10:02:21 +0100 | [diff] [blame] | 200 | static inline u32 cciss_tag_discard_error_bits(ctlr_info_t *h, u32 tag); | 
| Stephen M. Cameron | 62710ae | 2011-05-03 14:53:00 -0500 | [diff] [blame] | 201 | static __devinit int write_driver_ver_to_cfgtable( | 
 | 202 | 	CfgTable_struct __iomem *cfgtable); | 
| Mike Miller | 33079b2 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 203 |  | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 204 | /* performant mode helper functions */ | 
 | 205 | static void  calc_bucket_map(int *bucket, int num_buckets, int nsgs, | 
 | 206 | 				int *bucket_map); | 
 | 207 | static void cciss_put_controller_into_performant_mode(ctlr_info_t *h); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 208 |  | 
 | 209 | #ifdef CONFIG_PROC_FS | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 210 | static void cciss_procinit(ctlr_info_t *h); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 211 | #else | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 212 | static void cciss_procinit(ctlr_info_t *h) | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 213 | { | 
 | 214 | } | 
 | 215 | #endif				/* CONFIG_PROC_FS */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 216 |  | 
 | 217 | #ifdef CONFIG_COMPAT | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 218 | static int cciss_compat_ioctl(struct block_device *, fmode_t, | 
 | 219 | 			      unsigned, unsigned long); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 220 | #endif | 
 | 221 |  | 
| Alexey Dobriyan | 83d5cde | 2009-09-21 17:01:13 -0700 | [diff] [blame] | 222 | static const struct block_device_operations cciss_fops = { | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 223 | 	.owner = THIS_MODULE, | 
| Arnd Bergmann | 6e9624b | 2010-08-07 18:25:34 +0200 | [diff] [blame] | 224 | 	.open = cciss_unlocked_open, | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 225 | 	.release = cciss_release, | 
| Arnd Bergmann | 8a6cfeb | 2010-07-08 10:18:46 +0200 | [diff] [blame] | 226 | 	.ioctl = do_ioctl, | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 227 | 	.getgeo = cciss_getgeo, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 228 | #ifdef CONFIG_COMPAT | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 229 | 	.compat_ioctl = cciss_compat_ioctl, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 230 | #endif | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 231 | 	.revalidate_disk = cciss_revalidate, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 232 | }; | 
 | 233 |  | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 234 | /* set_performant_mode: Modify the tag for cciss performant | 
 | 235 |  * set bit 0 for pull model, bits 3-1 for block fetch | 
 | 236 |  * register number | 
 | 237 |  */ | 
 | 238 | static void set_performant_mode(ctlr_info_t *h, CommandList_struct *c) | 
 | 239 | { | 
| Stephen M. Cameron | 0498cc2 | 2011-03-12 10:02:16 +0100 | [diff] [blame] | 240 | 	if (likely(h->transMethod & CFGTBL_Trans_Performant)) | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 241 | 		c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1); | 
 | 242 | } | 
 | 243 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 244 | /* | 
 | 245 |  * Enqueuing and dequeuing functions for cmdlists. | 
 | 246 |  */ | 
| Jens Axboe | e6e1ee9 | 2011-01-10 21:50:33 +0100 | [diff] [blame] | 247 | static inline void addQ(struct list_head *list, CommandList_struct *c) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 248 | { | 
| Jens Axboe | e6e1ee9 | 2011-01-10 21:50:33 +0100 | [diff] [blame] | 249 | 	list_add_tail(&c->list, list); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 250 | } | 
 | 251 |  | 
| Jens Axboe | 8a3173d | 2008-11-20 09:46:09 +0100 | [diff] [blame] | 252 | static inline void removeQ(CommandList_struct *c) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 253 | { | 
| Hannes Reinecke | b59e64d | 2009-07-02 22:02:06 +0200 | [diff] [blame] | 254 | 	/* | 
 | 255 | 	 * After kexec/dump some commands might still | 
 | 256 | 	 * be in flight, which the firmware will try | 
 | 257 | 	 * to complete. Resetting the firmware doesn't work | 
 | 258 | 	 * with old fw revisions, so we have to mark | 
 | 259 | 	 * them off as 'stale' to prevent the driver from | 
 | 260 | 	 * falling over. | 
 | 261 | 	 */ | 
| Jens Axboe | e6e1ee9 | 2011-01-10 21:50:33 +0100 | [diff] [blame] | 262 | 	if (WARN_ON(list_empty(&c->list))) { | 
| Hannes Reinecke | b59e64d | 2009-07-02 22:02:06 +0200 | [diff] [blame] | 263 | 		c->cmd_type = CMD_MSG_STALE; | 
| Jens Axboe | 8a3173d | 2008-11-20 09:46:09 +0100 | [diff] [blame] | 264 | 		return; | 
| Hannes Reinecke | b59e64d | 2009-07-02 22:02:06 +0200 | [diff] [blame] | 265 | 	} | 
| Jens Axboe | 8a3173d | 2008-11-20 09:46:09 +0100 | [diff] [blame] | 266 |  | 
| Jens Axboe | e6e1ee9 | 2011-01-10 21:50:33 +0100 | [diff] [blame] | 267 | 	list_del_init(&c->list); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 268 | } | 
 | 269 |  | 
| Mike Miller | 664a717 | 2010-06-02 12:57:58 -0700 | [diff] [blame] | 270 | static void enqueue_cmd_and_start_io(ctlr_info_t *h, | 
 | 271 | 	CommandList_struct *c) | 
 | 272 | { | 
 | 273 | 	unsigned long flags; | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 274 | 	set_performant_mode(h, c); | 
| Mike Miller | 664a717 | 2010-06-02 12:57:58 -0700 | [diff] [blame] | 275 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 276 | 	addQ(&h->reqQ, c); | 
 | 277 | 	h->Qdepth++; | 
| Stephen M. Cameron | 2a643ec | 2010-08-25 19:58:53 +0200 | [diff] [blame] | 278 | 	if (h->Qdepth > h->maxQsinceinit) | 
 | 279 | 		h->maxQsinceinit = h->Qdepth; | 
| Mike Miller | 664a717 | 2010-06-02 12:57:58 -0700 | [diff] [blame] | 280 | 	start_io(h); | 
 | 281 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 282 | } | 
 | 283 |  | 
| Stephen M. Cameron | dccc9b5 | 2010-02-26 16:01:27 -0600 | [diff] [blame] | 284 | static void cciss_free_sg_chain_blocks(SGDescriptor_struct **cmd_sg_list, | 
| Stephen M. Cameron | 49fc560 | 2010-02-26 16:01:22 -0600 | [diff] [blame] | 285 | 	int nr_cmds) | 
 | 286 | { | 
 | 287 | 	int i; | 
 | 288 |  | 
 | 289 | 	if (!cmd_sg_list) | 
 | 290 | 		return; | 
 | 291 | 	for (i = 0; i < nr_cmds; i++) { | 
| Stephen M. Cameron | dccc9b5 | 2010-02-26 16:01:27 -0600 | [diff] [blame] | 292 | 		kfree(cmd_sg_list[i]); | 
 | 293 | 		cmd_sg_list[i] = NULL; | 
| Stephen M. Cameron | 49fc560 | 2010-02-26 16:01:22 -0600 | [diff] [blame] | 294 | 	} | 
 | 295 | 	kfree(cmd_sg_list); | 
 | 296 | } | 
 | 297 |  | 
| Stephen M. Cameron | dccc9b5 | 2010-02-26 16:01:27 -0600 | [diff] [blame] | 298 | static SGDescriptor_struct **cciss_allocate_sg_chain_blocks( | 
 | 299 | 	ctlr_info_t *h, int chainsize, int nr_cmds) | 
| Stephen M. Cameron | 49fc560 | 2010-02-26 16:01:22 -0600 | [diff] [blame] | 300 | { | 
 | 301 | 	int j; | 
| Stephen M. Cameron | dccc9b5 | 2010-02-26 16:01:27 -0600 | [diff] [blame] | 302 | 	SGDescriptor_struct **cmd_sg_list; | 
| Stephen M. Cameron | 49fc560 | 2010-02-26 16:01:22 -0600 | [diff] [blame] | 303 |  | 
 | 304 | 	if (chainsize <= 0) | 
 | 305 | 		return NULL; | 
 | 306 |  | 
 | 307 | 	cmd_sg_list = kmalloc(sizeof(*cmd_sg_list) * nr_cmds, GFP_KERNEL); | 
 | 308 | 	if (!cmd_sg_list) | 
 | 309 | 		return NULL; | 
 | 310 |  | 
 | 311 | 	/* Build up chain blocks for each command */ | 
 | 312 | 	for (j = 0; j < nr_cmds; j++) { | 
| Stephen M. Cameron | 49fc560 | 2010-02-26 16:01:22 -0600 | [diff] [blame] | 313 | 		/* Need a block of chainsized s/g elements. */ | 
| Stephen M. Cameron | dccc9b5 | 2010-02-26 16:01:27 -0600 | [diff] [blame] | 314 | 		cmd_sg_list[j] = kmalloc((chainsize * | 
 | 315 | 			sizeof(*cmd_sg_list[j])), GFP_KERNEL); | 
 | 316 | 		if (!cmd_sg_list[j]) { | 
| Stephen M. Cameron | 49fc560 | 2010-02-26 16:01:22 -0600 | [diff] [blame] | 317 | 			dev_err(&h->pdev->dev, "Cannot get memory " | 
 | 318 | 				"for s/g chains.\n"); | 
 | 319 | 			goto clean; | 
 | 320 | 		} | 
 | 321 | 	} | 
 | 322 | 	return cmd_sg_list; | 
 | 323 | clean: | 
 | 324 | 	cciss_free_sg_chain_blocks(cmd_sg_list, nr_cmds); | 
 | 325 | 	return NULL; | 
 | 326 | } | 
 | 327 |  | 
| Stephen M. Cameron | d45033e | 2010-02-26 16:01:37 -0600 | [diff] [blame] | 328 | static void cciss_unmap_sg_chain_block(ctlr_info_t *h, CommandList_struct *c) | 
 | 329 | { | 
 | 330 | 	SGDescriptor_struct *chain_sg; | 
 | 331 | 	u64bit temp64; | 
 | 332 |  | 
 | 333 | 	if (c->Header.SGTotal <= h->max_cmd_sgentries) | 
 | 334 | 		return; | 
 | 335 |  | 
 | 336 | 	chain_sg = &c->SG[h->max_cmd_sgentries - 1]; | 
 | 337 | 	temp64.val32.lower = chain_sg->Addr.lower; | 
 | 338 | 	temp64.val32.upper = chain_sg->Addr.upper; | 
 | 339 | 	pci_unmap_single(h->pdev, temp64.val, chain_sg->Len, PCI_DMA_TODEVICE); | 
 | 340 | } | 
 | 341 |  | 
 | 342 | static void cciss_map_sg_chain_block(ctlr_info_t *h, CommandList_struct *c, | 
 | 343 | 	SGDescriptor_struct *chain_block, int len) | 
 | 344 | { | 
 | 345 | 	SGDescriptor_struct *chain_sg; | 
 | 346 | 	u64bit temp64; | 
 | 347 |  | 
 | 348 | 	chain_sg = &c->SG[h->max_cmd_sgentries - 1]; | 
 | 349 | 	chain_sg->Ext = CCISS_SG_CHAIN; | 
 | 350 | 	chain_sg->Len = len; | 
 | 351 | 	temp64.val = pci_map_single(h->pdev, chain_block, len, | 
 | 352 | 				PCI_DMA_TODEVICE); | 
 | 353 | 	chain_sg->Addr.lower = temp64.val32.lower; | 
 | 354 | 	chain_sg->Addr.upper = temp64.val32.upper; | 
 | 355 | } | 
 | 356 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 357 | #include "cciss_scsi.c"		/* For SCSI tape support */ | 
 | 358 |  | 
| Alexander Beregalov | 1e6f2dc | 2009-09-24 16:15:38 +0200 | [diff] [blame] | 359 | static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG", | 
 | 360 | 	"UNKNOWN" | 
 | 361 | }; | 
| Kulikov Vasiliy | 0e4a9d0 | 2010-06-28 15:54:44 +0400 | [diff] [blame] | 362 | #define RAID_UNKNOWN (ARRAY_SIZE(raid_label)-1) | 
| Randy Dunlap | 0f5486e | 2006-12-29 16:48:31 -0800 | [diff] [blame] | 363 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 364 | #ifdef CONFIG_PROC_FS | 
 | 365 |  | 
 | 366 | /* | 
 | 367 |  * Report information about this controller. | 
 | 368 |  */ | 
 | 369 | #define ENG_GIG 1000000000 | 
 | 370 | #define ENG_GIG_FACTOR (ENG_GIG/512) | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 371 | #define ENGAGE_SCSI	"engage scsi" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 372 |  | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 373 | static void cciss_seq_show_header(struct seq_file *seq) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 374 | { | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 375 | 	ctlr_info_t *h = seq->private; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 376 |  | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 377 | 	seq_printf(seq, "%s: HP %s Controller\n" | 
 | 378 | 		"Board ID: 0x%08lx\n" | 
 | 379 | 		"Firmware Version: %c%c%c%c\n" | 
 | 380 | 		"IRQ: %d\n" | 
 | 381 | 		"Logical drives: %d\n" | 
 | 382 | 		"Current Q depth: %d\n" | 
 | 383 | 		"Current # commands on controller: %d\n" | 
 | 384 | 		"Max Q depth since init: %d\n" | 
 | 385 | 		"Max # commands on controller since init: %d\n" | 
 | 386 | 		"Max SG entries since init: %d\n", | 
 | 387 | 		h->devname, | 
 | 388 | 		h->product_name, | 
 | 389 | 		(unsigned long)h->board_id, | 
 | 390 | 		h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 391 | 		h->firm_ver[3], (unsigned int)h->intr[PERF_MODE_INT], | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 392 | 		h->num_luns, | 
 | 393 | 		h->Qdepth, h->commands_outstanding, | 
 | 394 | 		h->maxQsinceinit, h->max_outstanding, h->maxSG); | 
 | 395 |  | 
 | 396 | #ifdef CONFIG_CISS_SCSI_TAPE | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 397 | 	cciss_seq_tape_report(seq, h); | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 398 | #endif /* CONFIG_CISS_SCSI_TAPE */ | 
 | 399 | } | 
 | 400 |  | 
 | 401 | static void *cciss_seq_start(struct seq_file *seq, loff_t *pos) | 
 | 402 | { | 
 | 403 | 	ctlr_info_t *h = seq->private; | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 404 | 	unsigned long flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 405 |  | 
 | 406 | 	/* prevent displaying bogus info during configuration | 
 | 407 | 	 * or deconfiguration of a logical volume | 
 | 408 | 	 */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 409 | 	spin_lock_irqsave(&h->lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 410 | 	if (h->busy_configuring) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 411 | 		spin_unlock_irqrestore(&h->lock, flags); | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 412 | 		return ERR_PTR(-EBUSY); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 413 | 	} | 
 | 414 | 	h->busy_configuring = 1; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 415 | 	spin_unlock_irqrestore(&h->lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 416 |  | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 417 | 	if (*pos == 0) | 
 | 418 | 		cciss_seq_show_header(seq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 419 |  | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 420 | 	return pos; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 421 | } | 
 | 422 |  | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 423 | static int cciss_seq_show(struct seq_file *seq, void *v) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 424 | { | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 425 | 	sector_t vol_sz, vol_sz_frac; | 
 | 426 | 	ctlr_info_t *h = seq->private; | 
 | 427 | 	unsigned ctlr = h->ctlr; | 
 | 428 | 	loff_t *pos = v; | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 429 | 	drive_info_struct *drv = h->drv[*pos]; | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 430 |  | 
 | 431 | 	if (*pos > h->highest_lun) | 
 | 432 | 		return 0; | 
 | 433 |  | 
| Stephen M. Cameron | 531c2dc | 2010-02-05 13:14:04 +0100 | [diff] [blame] | 434 | 	if (drv == NULL) /* it's possible for h->drv[] to have holes. */ | 
 | 435 | 		return 0; | 
 | 436 |  | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 437 | 	if (drv->heads == 0) | 
 | 438 | 		return 0; | 
 | 439 |  | 
 | 440 | 	vol_sz = drv->nr_blocks; | 
 | 441 | 	vol_sz_frac = sector_div(vol_sz, ENG_GIG_FACTOR); | 
 | 442 | 	vol_sz_frac *= 100; | 
 | 443 | 	sector_div(vol_sz_frac, ENG_GIG_FACTOR); | 
 | 444 |  | 
| Stephen M. Cameron | fa52bec | 2009-09-17 13:48:15 -0500 | [diff] [blame] | 445 | 	if (drv->raid_level < 0 || drv->raid_level > RAID_UNKNOWN) | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 446 | 		drv->raid_level = RAID_UNKNOWN; | 
 | 447 | 	seq_printf(seq, "cciss/c%dd%d:" | 
 | 448 | 			"\t%4u.%02uGB\tRAID %s\n", | 
 | 449 | 			ctlr, (int) *pos, (int)vol_sz, (int)vol_sz_frac, | 
 | 450 | 			raid_label[drv->raid_level]); | 
 | 451 | 	return 0; | 
 | 452 | } | 
 | 453 |  | 
 | 454 | static void *cciss_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 
 | 455 | { | 
 | 456 | 	ctlr_info_t *h = seq->private; | 
 | 457 |  | 
 | 458 | 	if (*pos > h->highest_lun) | 
 | 459 | 		return NULL; | 
 | 460 | 	*pos += 1; | 
 | 461 |  | 
 | 462 | 	return pos; | 
 | 463 | } | 
 | 464 |  | 
 | 465 | static void cciss_seq_stop(struct seq_file *seq, void *v) | 
 | 466 | { | 
 | 467 | 	ctlr_info_t *h = seq->private; | 
 | 468 |  | 
 | 469 | 	/* Only reset h->busy_configuring if we succeeded in setting | 
 | 470 | 	 * it during cciss_seq_start. */ | 
 | 471 | 	if (v == ERR_PTR(-EBUSY)) | 
 | 472 | 		return; | 
 | 473 |  | 
 | 474 | 	h->busy_configuring = 0; | 
 | 475 | } | 
 | 476 |  | 
| James Morris | 88e9d34 | 2009-09-22 16:43:43 -0700 | [diff] [blame] | 477 | static const struct seq_operations cciss_seq_ops = { | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 478 | 	.start = cciss_seq_start, | 
 | 479 | 	.show  = cciss_seq_show, | 
 | 480 | 	.next  = cciss_seq_next, | 
 | 481 | 	.stop  = cciss_seq_stop, | 
 | 482 | }; | 
 | 483 |  | 
 | 484 | static int cciss_seq_open(struct inode *inode, struct file *file) | 
 | 485 | { | 
 | 486 | 	int ret = seq_open(file, &cciss_seq_ops); | 
 | 487 | 	struct seq_file *seq = file->private_data; | 
 | 488 |  | 
 | 489 | 	if (!ret) | 
 | 490 | 		seq->private = PDE(inode)->data; | 
 | 491 |  | 
 | 492 | 	return ret; | 
 | 493 | } | 
 | 494 |  | 
 | 495 | static ssize_t | 
 | 496 | cciss_proc_write(struct file *file, const char __user *buf, | 
 | 497 | 		 size_t length, loff_t *ppos) | 
 | 498 | { | 
 | 499 | 	int err; | 
 | 500 | 	char *buffer; | 
 | 501 |  | 
 | 502 | #ifndef CONFIG_CISS_SCSI_TAPE | 
 | 503 | 	return -EINVAL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 504 | #endif | 
 | 505 |  | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 506 | 	if (!buf || length > PAGE_SIZE - 1) | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 507 | 		return -EINVAL; | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 508 |  | 
 | 509 | 	buffer = (char *)__get_free_page(GFP_KERNEL); | 
 | 510 | 	if (!buffer) | 
 | 511 | 		return -ENOMEM; | 
 | 512 |  | 
 | 513 | 	err = -EFAULT; | 
 | 514 | 	if (copy_from_user(buffer, buf, length)) | 
 | 515 | 		goto out; | 
 | 516 | 	buffer[length] = '\0'; | 
 | 517 |  | 
 | 518 | #ifdef CONFIG_CISS_SCSI_TAPE | 
 | 519 | 	if (strncmp(ENGAGE_SCSI, buffer, sizeof ENGAGE_SCSI - 1) == 0) { | 
 | 520 | 		struct seq_file *seq = file->private_data; | 
 | 521 | 		ctlr_info_t *h = seq->private; | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 522 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 523 | 		err = cciss_engage_scsi(h); | 
| Stephen M. Cameron | 8721c81 | 2009-11-12 12:50:06 -0600 | [diff] [blame] | 524 | 		if (err == 0) | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 525 | 			err = length; | 
 | 526 | 	} else | 
 | 527 | #endif /* CONFIG_CISS_SCSI_TAPE */ | 
 | 528 | 		err = -EINVAL; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 529 | 	/* might be nice to have "disengage" too, but it's not | 
 | 530 | 	   safely possible. (only 1 module use count, lock issues.) */ | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 531 |  | 
 | 532 | out: | 
 | 533 | 	free_page((unsigned long)buffer); | 
 | 534 | 	return err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 535 | } | 
 | 536 |  | 
| Alexey Dobriyan | 828c095 | 2009-10-01 15:43:56 -0700 | [diff] [blame] | 537 | static const struct file_operations cciss_proc_fops = { | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 538 | 	.owner	 = THIS_MODULE, | 
 | 539 | 	.open    = cciss_seq_open, | 
 | 540 | 	.read    = seq_read, | 
 | 541 | 	.llseek  = seq_lseek, | 
 | 542 | 	.release = seq_release, | 
 | 543 | 	.write	 = cciss_proc_write, | 
 | 544 | }; | 
 | 545 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 546 | static void __devinit cciss_procinit(ctlr_info_t *h) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 547 | { | 
 | 548 | 	struct proc_dir_entry *pde; | 
 | 549 |  | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 550 | 	if (proc_cciss == NULL) | 
| Alexey Dobriyan | 928b4d8 | 2008-04-29 01:01:44 -0700 | [diff] [blame] | 551 | 		proc_cciss = proc_mkdir("driver/cciss", NULL); | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 552 | 	if (!proc_cciss) | 
 | 553 | 		return; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 554 | 	pde = proc_create_data(h->devname, S_IWUSR | S_IRUSR | S_IRGRP | | 
| Mike Miller | 89b6e74 | 2008-02-21 08:54:03 +0100 | [diff] [blame] | 555 | 					S_IROTH, proc_cciss, | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 556 | 					&cciss_proc_fops, h); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 557 | } | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 558 | #endif				/* CONFIG_PROC_FS */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 559 |  | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 560 | #define MAX_PRODUCT_NAME_LEN 19 | 
 | 561 |  | 
 | 562 | #define to_hba(n) container_of(n, struct ctlr_info, dev) | 
 | 563 | #define to_drv(n) container_of(n, drive_info_struct, dev) | 
 | 564 |  | 
| Stephen M. Cameron | ec52d5f | 2011-05-03 14:54:02 -0500 | [diff] [blame] | 565 | /* List of controllers which cannot be hard reset on kexec with reset_devices */ | 
| Stephen M. Cameron | 957c2ec | 2011-03-11 20:06:09 +0100 | [diff] [blame] | 566 | static u32 unresettable_controller[] = { | 
 | 567 | 	0x324a103C, /* Smart Array P712m */ | 
 | 568 | 	0x324b103C, /* SmartArray P711m */ | 
 | 569 | 	0x3223103C, /* Smart Array P800 */ | 
 | 570 | 	0x3234103C, /* Smart Array P400 */ | 
 | 571 | 	0x3235103C, /* Smart Array P400i */ | 
 | 572 | 	0x3211103C, /* Smart Array E200i */ | 
 | 573 | 	0x3212103C, /* Smart Array E200 */ | 
 | 574 | 	0x3213103C, /* Smart Array E200i */ | 
 | 575 | 	0x3214103C, /* Smart Array E200i */ | 
 | 576 | 	0x3215103C, /* Smart Array E200i */ | 
 | 577 | 	0x3237103C, /* Smart Array E500 */ | 
 | 578 | 	0x323D103C, /* Smart Array P700m */ | 
 | 579 | 	0x409C0E11, /* Smart Array 6400 */ | 
 | 580 | 	0x409D0E11, /* Smart Array 6400 EM */ | 
 | 581 | }; | 
 | 582 |  | 
| Stephen M. Cameron | ec52d5f | 2011-05-03 14:54:02 -0500 | [diff] [blame] | 583 | /* List of controllers which cannot even be soft reset */ | 
 | 584 | static u32 soft_unresettable_controller[] = { | 
 | 585 | 	0x409C0E11, /* Smart Array 6400 */ | 
 | 586 | 	0x409D0E11, /* Smart Array 6400 EM */ | 
 | 587 | }; | 
 | 588 |  | 
 | 589 | static int ctlr_is_hard_resettable(u32 board_id) | 
| Stephen M. Cameron | 957c2ec | 2011-03-11 20:06:09 +0100 | [diff] [blame] | 590 | { | 
 | 591 | 	int i; | 
 | 592 |  | 
 | 593 | 	for (i = 0; i < ARRAY_SIZE(unresettable_controller); i++) | 
| Stephen M. Cameron | ec52d5f | 2011-05-03 14:54:02 -0500 | [diff] [blame] | 594 | 		if (unresettable_controller[i] == board_id) | 
| Stephen M. Cameron | 957c2ec | 2011-03-11 20:06:09 +0100 | [diff] [blame] | 595 | 			return 0; | 
 | 596 | 	return 1; | 
 | 597 | } | 
 | 598 |  | 
| Stephen M. Cameron | ec52d5f | 2011-05-03 14:54:02 -0500 | [diff] [blame] | 599 | static int ctlr_is_soft_resettable(u32 board_id) | 
 | 600 | { | 
 | 601 | 	int i; | 
 | 602 |  | 
 | 603 | 	for (i = 0; i < ARRAY_SIZE(soft_unresettable_controller); i++) | 
 | 604 | 		if (soft_unresettable_controller[i] == board_id) | 
 | 605 | 			return 0; | 
 | 606 | 	return 1; | 
 | 607 | } | 
 | 608 |  | 
 | 609 | static int ctlr_is_resettable(u32 board_id) | 
 | 610 | { | 
 | 611 | 	return ctlr_is_hard_resettable(board_id) || | 
 | 612 | 		ctlr_is_soft_resettable(board_id); | 
 | 613 | } | 
 | 614 |  | 
| Stephen M. Cameron | 957c2ec | 2011-03-11 20:06:09 +0100 | [diff] [blame] | 615 | static ssize_t host_show_resettable(struct device *dev, | 
 | 616 | 				    struct device_attribute *attr, | 
 | 617 | 				    char *buf) | 
 | 618 | { | 
 | 619 | 	struct ctlr_info *h = to_hba(dev); | 
 | 620 |  | 
| Stephen M. Cameron | ec52d5f | 2011-05-03 14:54:02 -0500 | [diff] [blame] | 621 | 	return snprintf(buf, 20, "%d\n", ctlr_is_resettable(h->board_id)); | 
| Stephen M. Cameron | 957c2ec | 2011-03-11 20:06:09 +0100 | [diff] [blame] | 622 | } | 
 | 623 | static DEVICE_ATTR(resettable, S_IRUGO, host_show_resettable, NULL); | 
 | 624 |  | 
| Andrew Patterson | d6f4965 | 2009-09-17 13:47:03 -0500 | [diff] [blame] | 625 | static ssize_t host_store_rescan(struct device *dev, | 
 | 626 | 				 struct device_attribute *attr, | 
 | 627 | 				 const char *buf, size_t count) | 
 | 628 | { | 
 | 629 | 	struct ctlr_info *h = to_hba(dev); | 
 | 630 |  | 
 | 631 | 	add_to_scan_list(h); | 
 | 632 | 	wake_up_process(cciss_scan_thread); | 
 | 633 | 	wait_for_completion_interruptible(&h->scan_wait); | 
 | 634 |  | 
 | 635 | 	return count; | 
 | 636 | } | 
| Alex Chiang | 8ba95c6 | 2009-11-12 12:49:14 -0600 | [diff] [blame] | 637 | static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 638 |  | 
 | 639 | static ssize_t dev_show_unique_id(struct device *dev, | 
 | 640 | 				 struct device_attribute *attr, | 
 | 641 | 				 char *buf) | 
 | 642 | { | 
 | 643 | 	drive_info_struct *drv = to_drv(dev); | 
 | 644 | 	struct ctlr_info *h = to_hba(drv->dev.parent); | 
 | 645 | 	__u8 sn[16]; | 
 | 646 | 	unsigned long flags; | 
 | 647 | 	int ret = 0; | 
 | 648 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 649 | 	spin_lock_irqsave(&h->lock, flags); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 650 | 	if (h->busy_configuring) | 
 | 651 | 		ret = -EBUSY; | 
 | 652 | 	else | 
 | 653 | 		memcpy(sn, drv->serial_no, sizeof(sn)); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 654 | 	spin_unlock_irqrestore(&h->lock, flags); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 655 |  | 
 | 656 | 	if (ret) | 
 | 657 | 		return ret; | 
 | 658 | 	else | 
 | 659 | 		return snprintf(buf, 16 * 2 + 2, | 
 | 660 | 				"%02X%02X%02X%02X%02X%02X%02X%02X" | 
 | 661 | 				"%02X%02X%02X%02X%02X%02X%02X%02X\n", | 
 | 662 | 				sn[0], sn[1], sn[2], sn[3], | 
 | 663 | 				sn[4], sn[5], sn[6], sn[7], | 
 | 664 | 				sn[8], sn[9], sn[10], sn[11], | 
 | 665 | 				sn[12], sn[13], sn[14], sn[15]); | 
 | 666 | } | 
| Alex Chiang | 8ba95c6 | 2009-11-12 12:49:14 -0600 | [diff] [blame] | 667 | static DEVICE_ATTR(unique_id, S_IRUGO, dev_show_unique_id, NULL); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 668 |  | 
 | 669 | static ssize_t dev_show_vendor(struct device *dev, | 
 | 670 | 			       struct device_attribute *attr, | 
 | 671 | 			       char *buf) | 
 | 672 | { | 
 | 673 | 	drive_info_struct *drv = to_drv(dev); | 
 | 674 | 	struct ctlr_info *h = to_hba(drv->dev.parent); | 
 | 675 | 	char vendor[VENDOR_LEN + 1]; | 
 | 676 | 	unsigned long flags; | 
 | 677 | 	int ret = 0; | 
 | 678 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 679 | 	spin_lock_irqsave(&h->lock, flags); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 680 | 	if (h->busy_configuring) | 
 | 681 | 		ret = -EBUSY; | 
 | 682 | 	else | 
 | 683 | 		memcpy(vendor, drv->vendor, VENDOR_LEN + 1); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 684 | 	spin_unlock_irqrestore(&h->lock, flags); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 685 |  | 
 | 686 | 	if (ret) | 
 | 687 | 		return ret; | 
 | 688 | 	else | 
 | 689 | 		return snprintf(buf, sizeof(vendor) + 1, "%s\n", drv->vendor); | 
 | 690 | } | 
| Alex Chiang | 8ba95c6 | 2009-11-12 12:49:14 -0600 | [diff] [blame] | 691 | static DEVICE_ATTR(vendor, S_IRUGO, dev_show_vendor, NULL); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 692 |  | 
 | 693 | static ssize_t dev_show_model(struct device *dev, | 
 | 694 | 			      struct device_attribute *attr, | 
 | 695 | 			      char *buf) | 
 | 696 | { | 
 | 697 | 	drive_info_struct *drv = to_drv(dev); | 
 | 698 | 	struct ctlr_info *h = to_hba(drv->dev.parent); | 
 | 699 | 	char model[MODEL_LEN + 1]; | 
 | 700 | 	unsigned long flags; | 
 | 701 | 	int ret = 0; | 
 | 702 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 703 | 	spin_lock_irqsave(&h->lock, flags); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 704 | 	if (h->busy_configuring) | 
 | 705 | 		ret = -EBUSY; | 
 | 706 | 	else | 
 | 707 | 		memcpy(model, drv->model, MODEL_LEN + 1); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 708 | 	spin_unlock_irqrestore(&h->lock, flags); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 709 |  | 
 | 710 | 	if (ret) | 
 | 711 | 		return ret; | 
 | 712 | 	else | 
 | 713 | 		return snprintf(buf, sizeof(model) + 1, "%s\n", drv->model); | 
 | 714 | } | 
| Alex Chiang | 8ba95c6 | 2009-11-12 12:49:14 -0600 | [diff] [blame] | 715 | static DEVICE_ATTR(model, S_IRUGO, dev_show_model, NULL); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 716 |  | 
 | 717 | static ssize_t dev_show_rev(struct device *dev, | 
 | 718 | 			    struct device_attribute *attr, | 
 | 719 | 			    char *buf) | 
 | 720 | { | 
 | 721 | 	drive_info_struct *drv = to_drv(dev); | 
 | 722 | 	struct ctlr_info *h = to_hba(drv->dev.parent); | 
 | 723 | 	char rev[REV_LEN + 1]; | 
 | 724 | 	unsigned long flags; | 
 | 725 | 	int ret = 0; | 
 | 726 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 727 | 	spin_lock_irqsave(&h->lock, flags); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 728 | 	if (h->busy_configuring) | 
 | 729 | 		ret = -EBUSY; | 
 | 730 | 	else | 
 | 731 | 		memcpy(rev, drv->rev, REV_LEN + 1); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 732 | 	spin_unlock_irqrestore(&h->lock, flags); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 733 |  | 
 | 734 | 	if (ret) | 
 | 735 | 		return ret; | 
 | 736 | 	else | 
 | 737 | 		return snprintf(buf, sizeof(rev) + 1, "%s\n", drv->rev); | 
 | 738 | } | 
| Alex Chiang | 8ba95c6 | 2009-11-12 12:49:14 -0600 | [diff] [blame] | 739 | static DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 740 |  | 
| Stephen M. Cameron | ce84a8a | 2009-09-17 13:48:10 -0500 | [diff] [blame] | 741 | static ssize_t cciss_show_lunid(struct device *dev, | 
 | 742 | 				struct device_attribute *attr, char *buf) | 
 | 743 | { | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 744 | 	drive_info_struct *drv = to_drv(dev); | 
 | 745 | 	struct ctlr_info *h = to_hba(drv->dev.parent); | 
| Stephen M. Cameron | ce84a8a | 2009-09-17 13:48:10 -0500 | [diff] [blame] | 746 | 	unsigned long flags; | 
 | 747 | 	unsigned char lunid[8]; | 
 | 748 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 749 | 	spin_lock_irqsave(&h->lock, flags); | 
| Stephen M. Cameron | ce84a8a | 2009-09-17 13:48:10 -0500 | [diff] [blame] | 750 | 	if (h->busy_configuring) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 751 | 		spin_unlock_irqrestore(&h->lock, flags); | 
| Stephen M. Cameron | ce84a8a | 2009-09-17 13:48:10 -0500 | [diff] [blame] | 752 | 		return -EBUSY; | 
 | 753 | 	} | 
 | 754 | 	if (!drv->heads) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 755 | 		spin_unlock_irqrestore(&h->lock, flags); | 
| Stephen M. Cameron | ce84a8a | 2009-09-17 13:48:10 -0500 | [diff] [blame] | 756 | 		return -ENOTTY; | 
 | 757 | 	} | 
 | 758 | 	memcpy(lunid, drv->LunID, sizeof(lunid)); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 759 | 	spin_unlock_irqrestore(&h->lock, flags); | 
| Stephen M. Cameron | ce84a8a | 2009-09-17 13:48:10 -0500 | [diff] [blame] | 760 | 	return snprintf(buf, 20, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", | 
 | 761 | 		lunid[0], lunid[1], lunid[2], lunid[3], | 
 | 762 | 		lunid[4], lunid[5], lunid[6], lunid[7]); | 
 | 763 | } | 
| Alex Chiang | 8ba95c6 | 2009-11-12 12:49:14 -0600 | [diff] [blame] | 764 | static DEVICE_ATTR(lunid, S_IRUGO, cciss_show_lunid, NULL); | 
| Stephen M. Cameron | ce84a8a | 2009-09-17 13:48:10 -0500 | [diff] [blame] | 765 |  | 
| Stephen M. Cameron | 3ff1111 | 2009-09-17 13:48:21 -0500 | [diff] [blame] | 766 | static ssize_t cciss_show_raid_level(struct device *dev, | 
 | 767 | 				     struct device_attribute *attr, char *buf) | 
 | 768 | { | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 769 | 	drive_info_struct *drv = to_drv(dev); | 
 | 770 | 	struct ctlr_info *h = to_hba(drv->dev.parent); | 
| Stephen M. Cameron | 3ff1111 | 2009-09-17 13:48:21 -0500 | [diff] [blame] | 771 | 	int raid; | 
 | 772 | 	unsigned long flags; | 
 | 773 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 774 | 	spin_lock_irqsave(&h->lock, flags); | 
| Stephen M. Cameron | 3ff1111 | 2009-09-17 13:48:21 -0500 | [diff] [blame] | 775 | 	if (h->busy_configuring) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 776 | 		spin_unlock_irqrestore(&h->lock, flags); | 
| Stephen M. Cameron | 3ff1111 | 2009-09-17 13:48:21 -0500 | [diff] [blame] | 777 | 		return -EBUSY; | 
 | 778 | 	} | 
 | 779 | 	raid = drv->raid_level; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 780 | 	spin_unlock_irqrestore(&h->lock, flags); | 
| Stephen M. Cameron | 3ff1111 | 2009-09-17 13:48:21 -0500 | [diff] [blame] | 781 | 	if (raid < 0 || raid > RAID_UNKNOWN) | 
 | 782 | 		raid = RAID_UNKNOWN; | 
 | 783 |  | 
 | 784 | 	return snprintf(buf, strlen(raid_label[raid]) + 7, "RAID %s\n", | 
 | 785 | 			raid_label[raid]); | 
 | 786 | } | 
| Alex Chiang | 8ba95c6 | 2009-11-12 12:49:14 -0600 | [diff] [blame] | 787 | static DEVICE_ATTR(raid_level, S_IRUGO, cciss_show_raid_level, NULL); | 
| Stephen M. Cameron | 3ff1111 | 2009-09-17 13:48:21 -0500 | [diff] [blame] | 788 |  | 
| Stephen M. Cameron | e272afe | 2009-09-17 13:48:26 -0500 | [diff] [blame] | 789 | static ssize_t cciss_show_usage_count(struct device *dev, | 
 | 790 | 				      struct device_attribute *attr, char *buf) | 
 | 791 | { | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 792 | 	drive_info_struct *drv = to_drv(dev); | 
 | 793 | 	struct ctlr_info *h = to_hba(drv->dev.parent); | 
| Stephen M. Cameron | e272afe | 2009-09-17 13:48:26 -0500 | [diff] [blame] | 794 | 	unsigned long flags; | 
 | 795 | 	int count; | 
 | 796 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 797 | 	spin_lock_irqsave(&h->lock, flags); | 
| Stephen M. Cameron | e272afe | 2009-09-17 13:48:26 -0500 | [diff] [blame] | 798 | 	if (h->busy_configuring) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 799 | 		spin_unlock_irqrestore(&h->lock, flags); | 
| Stephen M. Cameron | e272afe | 2009-09-17 13:48:26 -0500 | [diff] [blame] | 800 | 		return -EBUSY; | 
 | 801 | 	} | 
 | 802 | 	count = drv->usage_count; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 803 | 	spin_unlock_irqrestore(&h->lock, flags); | 
| Stephen M. Cameron | e272afe | 2009-09-17 13:48:26 -0500 | [diff] [blame] | 804 | 	return snprintf(buf, 20, "%d\n", count); | 
 | 805 | } | 
| Alex Chiang | 8ba95c6 | 2009-11-12 12:49:14 -0600 | [diff] [blame] | 806 | static DEVICE_ATTR(usage_count, S_IRUGO, cciss_show_usage_count, NULL); | 
| Stephen M. Cameron | e272afe | 2009-09-17 13:48:26 -0500 | [diff] [blame] | 807 |  | 
| Andrew Patterson | d6f4965 | 2009-09-17 13:47:03 -0500 | [diff] [blame] | 808 | static struct attribute *cciss_host_attrs[] = { | 
 | 809 | 	&dev_attr_rescan.attr, | 
| Stephen M. Cameron | 957c2ec | 2011-03-11 20:06:09 +0100 | [diff] [blame] | 810 | 	&dev_attr_resettable.attr, | 
| Andrew Patterson | d6f4965 | 2009-09-17 13:47:03 -0500 | [diff] [blame] | 811 | 	NULL | 
 | 812 | }; | 
 | 813 |  | 
 | 814 | static struct attribute_group cciss_host_attr_group = { | 
 | 815 | 	.attrs = cciss_host_attrs, | 
 | 816 | }; | 
 | 817 |  | 
| Jens Axboe | 9f792d9 | 2009-09-18 22:24:21 +0200 | [diff] [blame] | 818 | static const struct attribute_group *cciss_host_attr_groups[] = { | 
| Andrew Patterson | d6f4965 | 2009-09-17 13:47:03 -0500 | [diff] [blame] | 819 | 	&cciss_host_attr_group, | 
 | 820 | 	NULL | 
 | 821 | }; | 
 | 822 |  | 
 | 823 | static struct device_type cciss_host_type = { | 
 | 824 | 	.name		= "cciss_host", | 
 | 825 | 	.groups		= cciss_host_attr_groups, | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 826 | 	.release	= cciss_hba_release, | 
| Andrew Patterson | d6f4965 | 2009-09-17 13:47:03 -0500 | [diff] [blame] | 827 | }; | 
 | 828 |  | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 829 | static struct attribute *cciss_dev_attrs[] = { | 
 | 830 | 	&dev_attr_unique_id.attr, | 
 | 831 | 	&dev_attr_model.attr, | 
 | 832 | 	&dev_attr_vendor.attr, | 
 | 833 | 	&dev_attr_rev.attr, | 
| Stephen M. Cameron | ce84a8a | 2009-09-17 13:48:10 -0500 | [diff] [blame] | 834 | 	&dev_attr_lunid.attr, | 
| Stephen M. Cameron | 3ff1111 | 2009-09-17 13:48:21 -0500 | [diff] [blame] | 835 | 	&dev_attr_raid_level.attr, | 
| Stephen M. Cameron | e272afe | 2009-09-17 13:48:26 -0500 | [diff] [blame] | 836 | 	&dev_attr_usage_count.attr, | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 837 | 	NULL | 
 | 838 | }; | 
 | 839 |  | 
 | 840 | static struct attribute_group cciss_dev_attr_group = { | 
 | 841 | 	.attrs = cciss_dev_attrs, | 
 | 842 | }; | 
 | 843 |  | 
| David Brownell | a4dbd67 | 2009-06-24 10:06:31 -0700 | [diff] [blame] | 844 | static const struct attribute_group *cciss_dev_attr_groups[] = { | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 845 | 	&cciss_dev_attr_group, | 
 | 846 | 	NULL | 
 | 847 | }; | 
 | 848 |  | 
 | 849 | static struct device_type cciss_dev_type = { | 
 | 850 | 	.name		= "cciss_device", | 
 | 851 | 	.groups		= cciss_dev_attr_groups, | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 852 | 	.release	= cciss_device_release, | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 853 | }; | 
 | 854 |  | 
 | 855 | static struct bus_type cciss_bus_type = { | 
 | 856 | 	.name		= "cciss", | 
 | 857 | }; | 
 | 858 |  | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 859 | /* | 
 | 860 |  * cciss_hba_release is called when the reference count | 
 | 861 |  * of h->dev goes to zero. | 
 | 862 |  */ | 
 | 863 | static void cciss_hba_release(struct device *dev) | 
 | 864 | { | 
 | 865 | 	/* | 
 | 866 | 	 * nothing to do, but need this to avoid a warning | 
 | 867 | 	 * about not having a release handler from lib/kref.c. | 
 | 868 | 	 */ | 
 | 869 | } | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 870 |  | 
 | 871 | /* | 
 | 872 |  * Initialize sysfs entry for each controller.  This sets up and registers | 
 | 873 |  * the 'cciss#' directory for each individual controller under | 
 | 874 |  * /sys/bus/pci/devices/<dev>/. | 
 | 875 |  */ | 
 | 876 | static int cciss_create_hba_sysfs_entry(struct ctlr_info *h) | 
 | 877 | { | 
 | 878 | 	device_initialize(&h->dev); | 
 | 879 | 	h->dev.type = &cciss_host_type; | 
 | 880 | 	h->dev.bus = &cciss_bus_type; | 
 | 881 | 	dev_set_name(&h->dev, "%s", h->devname); | 
 | 882 | 	h->dev.parent = &h->pdev->dev; | 
 | 883 |  | 
 | 884 | 	return device_add(&h->dev); | 
 | 885 | } | 
 | 886 |  | 
 | 887 | /* | 
 | 888 |  * Remove sysfs entries for an hba. | 
 | 889 |  */ | 
 | 890 | static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h) | 
 | 891 | { | 
 | 892 | 	device_del(&h->dev); | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 893 | 	put_device(&h->dev); /* final put. */ | 
 | 894 | } | 
 | 895 |  | 
 | 896 | /* cciss_device_release is called when the reference count | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 897 |  * of h->drv[x]dev goes to zero. | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 898 |  */ | 
 | 899 | static void cciss_device_release(struct device *dev) | 
 | 900 | { | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 901 | 	drive_info_struct *drv = to_drv(dev); | 
 | 902 | 	kfree(drv); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 903 | } | 
 | 904 |  | 
 | 905 | /* | 
 | 906 |  * Initialize sysfs for each logical drive.  This sets up and registers | 
 | 907 |  * the 'c#d#' directory for each individual logical drive under | 
 | 908 |  * /sys/bus/pci/devices/<dev/ccis#/. We also create a link from | 
 | 909 |  * /sys/block/cciss!c#d# to this entry. | 
 | 910 |  */ | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 911 | static long cciss_create_ld_sysfs_entry(struct ctlr_info *h, | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 912 | 				       int drv_index) | 
 | 913 | { | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 914 | 	struct device *dev; | 
 | 915 |  | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 916 | 	if (h->drv[drv_index]->device_initialized) | 
| Stephen M. Cameron | 8ce5196 | 2009-09-17 13:47:34 -0500 | [diff] [blame] | 917 | 		return 0; | 
 | 918 |  | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 919 | 	dev = &h->drv[drv_index]->dev; | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 920 | 	device_initialize(dev); | 
 | 921 | 	dev->type = &cciss_dev_type; | 
 | 922 | 	dev->bus = &cciss_bus_type; | 
 | 923 | 	dev_set_name(dev, "c%dd%d", h->ctlr, drv_index); | 
 | 924 | 	dev->parent = &h->dev; | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 925 | 	h->drv[drv_index]->device_initialized = 1; | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 926 | 	return device_add(dev); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 927 | } | 
 | 928 |  | 
 | 929 | /* | 
 | 930 |  * Remove sysfs entries for a logical drive. | 
 | 931 |  */ | 
| Stephen M. Cameron | 8ce5196 | 2009-09-17 13:47:34 -0500 | [diff] [blame] | 932 | static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index, | 
 | 933 | 	int ctlr_exiting) | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 934 | { | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 935 | 	struct device *dev = &h->drv[drv_index]->dev; | 
| Stephen M. Cameron | 8ce5196 | 2009-09-17 13:47:34 -0500 | [diff] [blame] | 936 |  | 
 | 937 | 	/* special case for c*d0, we only destroy it on controller exit */ | 
 | 938 | 	if (drv_index == 0 && !ctlr_exiting) | 
 | 939 | 		return; | 
 | 940 |  | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 941 | 	device_del(dev); | 
 | 942 | 	put_device(dev); /* the "final" put. */ | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 943 | 	h->drv[drv_index] = NULL; | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 944 | } | 
 | 945 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 946 | /* | 
 | 947 |  * For operations that cannot sleep, a command block is allocated at init, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 948 |  * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 949 |  * which ones are free or in use. | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 950 |  */ | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 951 | static CommandList_struct *cmd_alloc(ctlr_info_t *h) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 952 | { | 
 | 953 | 	CommandList_struct *c; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 954 | 	int i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 955 | 	u64bit temp64; | 
 | 956 | 	dma_addr_t cmd_dma_handle, err_dma_handle; | 
 | 957 |  | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 958 | 	do { | 
 | 959 | 		i = find_first_zero_bit(h->cmd_pool_bits, h->nr_cmds); | 
 | 960 | 		if (i == h->nr_cmds) | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 961 | 			return NULL; | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 962 | 	} while (test_and_set_bit(i & (BITS_PER_LONG - 1), | 
 | 963 | 		  h->cmd_pool_bits + (i / BITS_PER_LONG)) != 0); | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 964 | 	c = h->cmd_pool + i; | 
 | 965 | 	memset(c, 0, sizeof(CommandList_struct)); | 
 | 966 | 	cmd_dma_handle = h->cmd_pool_dhandle + i * sizeof(CommandList_struct); | 
 | 967 | 	c->err_info = h->errinfo_pool + i; | 
 | 968 | 	memset(c->err_info, 0, sizeof(ErrorInfo_struct)); | 
 | 969 | 	err_dma_handle = h->errinfo_pool_dhandle | 
 | 970 | 	    + i * sizeof(ErrorInfo_struct); | 
 | 971 | 	h->nr_allocs++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 972 |  | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 973 | 	c->cmdindex = i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 974 |  | 
| Jens Axboe | e6e1ee9 | 2011-01-10 21:50:33 +0100 | [diff] [blame] | 975 | 	INIT_LIST_HEAD(&c->list); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 976 | 	c->busaddr = (__u32) cmd_dma_handle; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 977 | 	temp64.val = (__u64) err_dma_handle; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 978 | 	c->ErrDesc.Addr.lower = temp64.val32.lower; | 
 | 979 | 	c->ErrDesc.Addr.upper = temp64.val32.upper; | 
 | 980 | 	c->ErrDesc.Len = sizeof(ErrorInfo_struct); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 981 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 982 | 	c->ctlr = h->ctlr; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 983 | 	return c; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 984 | } | 
 | 985 |  | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 986 | /* allocate a command using pci_alloc_consistent, used for ioctls, | 
 | 987 |  * etc., not for the main i/o path. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 988 |  */ | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 989 | static CommandList_struct *cmd_special_alloc(ctlr_info_t *h) | 
 | 990 | { | 
 | 991 | 	CommandList_struct *c; | 
 | 992 | 	u64bit temp64; | 
 | 993 | 	dma_addr_t cmd_dma_handle, err_dma_handle; | 
 | 994 |  | 
 | 995 | 	c = (CommandList_struct *) pci_alloc_consistent(h->pdev, | 
 | 996 | 		sizeof(CommandList_struct), &cmd_dma_handle); | 
 | 997 | 	if (c == NULL) | 
 | 998 | 		return NULL; | 
 | 999 | 	memset(c, 0, sizeof(CommandList_struct)); | 
 | 1000 |  | 
 | 1001 | 	c->cmdindex = -1; | 
 | 1002 |  | 
 | 1003 | 	c->err_info = (ErrorInfo_struct *) | 
 | 1004 | 	    pci_alloc_consistent(h->pdev, sizeof(ErrorInfo_struct), | 
 | 1005 | 		    &err_dma_handle); | 
 | 1006 |  | 
 | 1007 | 	if (c->err_info == NULL) { | 
 | 1008 | 		pci_free_consistent(h->pdev, | 
 | 1009 | 			sizeof(CommandList_struct), c, cmd_dma_handle); | 
 | 1010 | 		return NULL; | 
 | 1011 | 	} | 
 | 1012 | 	memset(c->err_info, 0, sizeof(ErrorInfo_struct)); | 
 | 1013 |  | 
| Jens Axboe | e6e1ee9 | 2011-01-10 21:50:33 +0100 | [diff] [blame] | 1014 | 	INIT_LIST_HEAD(&c->list); | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 1015 | 	c->busaddr = (__u32) cmd_dma_handle; | 
 | 1016 | 	temp64.val = (__u64) err_dma_handle; | 
 | 1017 | 	c->ErrDesc.Addr.lower = temp64.val32.lower; | 
 | 1018 | 	c->ErrDesc.Addr.upper = temp64.val32.upper; | 
 | 1019 | 	c->ErrDesc.Len = sizeof(ErrorInfo_struct); | 
 | 1020 |  | 
 | 1021 | 	c->ctlr = h->ctlr; | 
 | 1022 | 	return c; | 
 | 1023 | } | 
 | 1024 |  | 
 | 1025 | static void cmd_free(ctlr_info_t *h, CommandList_struct *c) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1026 | { | 
 | 1027 | 	int i; | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 1028 |  | 
 | 1029 | 	i = c - h->cmd_pool; | 
 | 1030 | 	clear_bit(i & (BITS_PER_LONG - 1), | 
 | 1031 | 		  h->cmd_pool_bits + (i / BITS_PER_LONG)); | 
 | 1032 | 	h->nr_frees++; | 
 | 1033 | } | 
 | 1034 |  | 
 | 1035 | static void cmd_special_free(ctlr_info_t *h, CommandList_struct *c) | 
 | 1036 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1037 | 	u64bit temp64; | 
 | 1038 |  | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 1039 | 	temp64.val32.lower = c->ErrDesc.Addr.lower; | 
 | 1040 | 	temp64.val32.upper = c->ErrDesc.Addr.upper; | 
 | 1041 | 	pci_free_consistent(h->pdev, sizeof(ErrorInfo_struct), | 
 | 1042 | 			    c->err_info, (dma_addr_t) temp64.val); | 
| Stephen M. Cameron | 1601113 | 2011-03-12 10:02:21 +0100 | [diff] [blame] | 1043 | 	pci_free_consistent(h->pdev, sizeof(CommandList_struct), c, | 
 | 1044 | 		(dma_addr_t) cciss_tag_discard_error_bits(h, (u32) c->busaddr)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1045 | } | 
 | 1046 |  | 
 | 1047 | static inline ctlr_info_t *get_host(struct gendisk *disk) | 
 | 1048 | { | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1049 | 	return disk->queue->queuedata; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1050 | } | 
 | 1051 |  | 
 | 1052 | static inline drive_info_struct *get_drv(struct gendisk *disk) | 
 | 1053 | { | 
 | 1054 | 	return disk->private_data; | 
 | 1055 | } | 
 | 1056 |  | 
 | 1057 | /* | 
 | 1058 |  * Open.  Make sure the device is really there. | 
 | 1059 |  */ | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1060 | static int cciss_open(struct block_device *bdev, fmode_t mode) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1061 | { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1062 | 	ctlr_info_t *h = get_host(bdev->bd_disk); | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1063 | 	drive_info_struct *drv = get_drv(bdev->bd_disk); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1064 |  | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 1065 | 	dev_dbg(&h->pdev->dev, "cciss_open %s\n", bdev->bd_disk->disk_name); | 
| Stephen M. Cameron | 2e04398 | 2009-09-17 13:48:05 -0500 | [diff] [blame] | 1066 | 	if (drv->busy_configuring) | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 1067 | 		return -EBUSY; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1068 | 	/* | 
 | 1069 | 	 * Root is allowed to open raw volume zero even if it's not configured | 
 | 1070 | 	 * so array config can still work. Root is also allowed to open any | 
 | 1071 | 	 * volume that has a LUN ID, so it can issue IOCTL to reread the | 
 | 1072 | 	 * disk information.  I don't think I really like this | 
 | 1073 | 	 * but I'm already using way to many device nodes to claim another one | 
 | 1074 | 	 * for "raw controller". | 
 | 1075 | 	 */ | 
| Mike Miller | 7a06f78 | 2006-12-06 20:35:08 -0800 | [diff] [blame] | 1076 | 	if (drv->heads == 0) { | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1077 | 		if (MINOR(bdev->bd_dev) != 0) {	/* not node 0? */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1078 | 			/* if not node 0 make sure it is a partition = 0 */ | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1079 | 			if (MINOR(bdev->bd_dev) & 0x0f) { | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1080 | 				return -ENXIO; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1081 | 				/* if it is, make sure we have a LUN ID */ | 
| Stephen M. Cameron | 39ccf9a | 2009-09-17 13:48:00 -0500 | [diff] [blame] | 1082 | 			} else if (memcmp(drv->LunID, CTLR_LUNID, | 
 | 1083 | 				sizeof(drv->LunID))) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1084 | 				return -ENXIO; | 
 | 1085 | 			} | 
 | 1086 | 		} | 
 | 1087 | 		if (!capable(CAP_SYS_ADMIN)) | 
 | 1088 | 			return -EPERM; | 
 | 1089 | 	} | 
 | 1090 | 	drv->usage_count++; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1091 | 	h->usage_count++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1092 | 	return 0; | 
 | 1093 | } | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1094 |  | 
| Arnd Bergmann | 6e9624b | 2010-08-07 18:25:34 +0200 | [diff] [blame] | 1095 | static int cciss_unlocked_open(struct block_device *bdev, fmode_t mode) | 
 | 1096 | { | 
 | 1097 | 	int ret; | 
 | 1098 |  | 
| Arnd Bergmann | 2a48fc0 | 2010-06-02 14:28:52 +0200 | [diff] [blame] | 1099 | 	mutex_lock(&cciss_mutex); | 
| Arnd Bergmann | 6e9624b | 2010-08-07 18:25:34 +0200 | [diff] [blame] | 1100 | 	ret = cciss_open(bdev, mode); | 
| Arnd Bergmann | 2a48fc0 | 2010-06-02 14:28:52 +0200 | [diff] [blame] | 1101 | 	mutex_unlock(&cciss_mutex); | 
| Arnd Bergmann | 6e9624b | 2010-08-07 18:25:34 +0200 | [diff] [blame] | 1102 |  | 
 | 1103 | 	return ret; | 
 | 1104 | } | 
 | 1105 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1106 | /* | 
 | 1107 |  * Close.  Sync first. | 
 | 1108 |  */ | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1109 | static int cciss_release(struct gendisk *disk, fmode_t mode) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1110 | { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1111 | 	ctlr_info_t *h; | 
| Arnd Bergmann | 6e9624b | 2010-08-07 18:25:34 +0200 | [diff] [blame] | 1112 | 	drive_info_struct *drv; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1113 |  | 
| Arnd Bergmann | 2a48fc0 | 2010-06-02 14:28:52 +0200 | [diff] [blame] | 1114 | 	mutex_lock(&cciss_mutex); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1115 | 	h = get_host(disk); | 
| Arnd Bergmann | 6e9624b | 2010-08-07 18:25:34 +0200 | [diff] [blame] | 1116 | 	drv = get_drv(disk); | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 1117 | 	dev_dbg(&h->pdev->dev, "cciss_release %s\n", disk->disk_name); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1118 | 	drv->usage_count--; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1119 | 	h->usage_count--; | 
| Arnd Bergmann | 2a48fc0 | 2010-06-02 14:28:52 +0200 | [diff] [blame] | 1120 | 	mutex_unlock(&cciss_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1121 | 	return 0; | 
 | 1122 | } | 
 | 1123 |  | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1124 | static int do_ioctl(struct block_device *bdev, fmode_t mode, | 
 | 1125 | 		    unsigned cmd, unsigned long arg) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1126 | { | 
 | 1127 | 	int ret; | 
| Arnd Bergmann | 2a48fc0 | 2010-06-02 14:28:52 +0200 | [diff] [blame] | 1128 | 	mutex_lock(&cciss_mutex); | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1129 | 	ret = cciss_ioctl(bdev, mode, cmd, arg); | 
| Arnd Bergmann | 2a48fc0 | 2010-06-02 14:28:52 +0200 | [diff] [blame] | 1130 | 	mutex_unlock(&cciss_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1131 | 	return ret; | 
 | 1132 | } | 
 | 1133 |  | 
| Arnd Bergmann | 8a6cfeb | 2010-07-08 10:18:46 +0200 | [diff] [blame] | 1134 | #ifdef CONFIG_COMPAT | 
 | 1135 |  | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1136 | static int cciss_ioctl32_passthru(struct block_device *bdev, fmode_t mode, | 
 | 1137 | 				  unsigned cmd, unsigned long arg); | 
 | 1138 | static int cciss_ioctl32_big_passthru(struct block_device *bdev, fmode_t mode, | 
 | 1139 | 				      unsigned cmd, unsigned long arg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1140 |  | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1141 | static int cciss_compat_ioctl(struct block_device *bdev, fmode_t mode, | 
 | 1142 | 			      unsigned cmd, unsigned long arg) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1143 | { | 
 | 1144 | 	switch (cmd) { | 
 | 1145 | 	case CCISS_GETPCIINFO: | 
 | 1146 | 	case CCISS_GETINTINFO: | 
 | 1147 | 	case CCISS_SETINTINFO: | 
 | 1148 | 	case CCISS_GETNODENAME: | 
 | 1149 | 	case CCISS_SETNODENAME: | 
 | 1150 | 	case CCISS_GETHEARTBEAT: | 
 | 1151 | 	case CCISS_GETBUSTYPES: | 
 | 1152 | 	case CCISS_GETFIRMVER: | 
 | 1153 | 	case CCISS_GETDRIVVER: | 
 | 1154 | 	case CCISS_REVALIDVOLS: | 
 | 1155 | 	case CCISS_DEREGDISK: | 
 | 1156 | 	case CCISS_REGNEWDISK: | 
 | 1157 | 	case CCISS_REGNEWD: | 
 | 1158 | 	case CCISS_RESCANDISK: | 
 | 1159 | 	case CCISS_GETLUNINFO: | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1160 | 		return do_ioctl(bdev, mode, cmd, arg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1161 |  | 
 | 1162 | 	case CCISS_PASSTHRU32: | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1163 | 		return cciss_ioctl32_passthru(bdev, mode, cmd, arg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1164 | 	case CCISS_BIG_PASSTHRU32: | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1165 | 		return cciss_ioctl32_big_passthru(bdev, mode, cmd, arg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1166 |  | 
 | 1167 | 	default: | 
 | 1168 | 		return -ENOIOCTLCMD; | 
 | 1169 | 	} | 
 | 1170 | } | 
 | 1171 |  | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1172 | static int cciss_ioctl32_passthru(struct block_device *bdev, fmode_t mode, | 
 | 1173 | 				  unsigned cmd, unsigned long arg) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1174 | { | 
 | 1175 | 	IOCTL32_Command_struct __user *arg32 = | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1176 | 	    (IOCTL32_Command_struct __user *) arg; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1177 | 	IOCTL_Command_struct arg64; | 
 | 1178 | 	IOCTL_Command_struct __user *p = compat_alloc_user_space(sizeof(arg64)); | 
 | 1179 | 	int err; | 
 | 1180 | 	u32 cp; | 
 | 1181 |  | 
 | 1182 | 	err = 0; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1183 | 	err |= | 
 | 1184 | 	    copy_from_user(&arg64.LUN_info, &arg32->LUN_info, | 
 | 1185 | 			   sizeof(arg64.LUN_info)); | 
 | 1186 | 	err |= | 
 | 1187 | 	    copy_from_user(&arg64.Request, &arg32->Request, | 
 | 1188 | 			   sizeof(arg64.Request)); | 
 | 1189 | 	err |= | 
 | 1190 | 	    copy_from_user(&arg64.error_info, &arg32->error_info, | 
 | 1191 | 			   sizeof(arg64.error_info)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1192 | 	err |= get_user(arg64.buf_size, &arg32->buf_size); | 
 | 1193 | 	err |= get_user(cp, &arg32->buf); | 
 | 1194 | 	arg64.buf = compat_ptr(cp); | 
 | 1195 | 	err |= copy_to_user(p, &arg64, sizeof(arg64)); | 
 | 1196 |  | 
 | 1197 | 	if (err) | 
 | 1198 | 		return -EFAULT; | 
 | 1199 |  | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1200 | 	err = do_ioctl(bdev, mode, CCISS_PASSTHRU, (unsigned long)p); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1201 | 	if (err) | 
 | 1202 | 		return err; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1203 | 	err |= | 
 | 1204 | 	    copy_in_user(&arg32->error_info, &p->error_info, | 
 | 1205 | 			 sizeof(arg32->error_info)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1206 | 	if (err) | 
 | 1207 | 		return -EFAULT; | 
 | 1208 | 	return err; | 
 | 1209 | } | 
 | 1210 |  | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1211 | static int cciss_ioctl32_big_passthru(struct block_device *bdev, fmode_t mode, | 
 | 1212 | 				      unsigned cmd, unsigned long arg) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1213 | { | 
 | 1214 | 	BIG_IOCTL32_Command_struct __user *arg32 = | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1215 | 	    (BIG_IOCTL32_Command_struct __user *) arg; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1216 | 	BIG_IOCTL_Command_struct arg64; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1217 | 	BIG_IOCTL_Command_struct __user *p = | 
 | 1218 | 	    compat_alloc_user_space(sizeof(arg64)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1219 | 	int err; | 
 | 1220 | 	u32 cp; | 
 | 1221 |  | 
| Vasiliy Kulikov | 7ab5118 | 2010-10-28 06:31:55 -0600 | [diff] [blame] | 1222 | 	memset(&arg64, 0, sizeof(arg64)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1223 | 	err = 0; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1224 | 	err |= | 
 | 1225 | 	    copy_from_user(&arg64.LUN_info, &arg32->LUN_info, | 
 | 1226 | 			   sizeof(arg64.LUN_info)); | 
 | 1227 | 	err |= | 
 | 1228 | 	    copy_from_user(&arg64.Request, &arg32->Request, | 
 | 1229 | 			   sizeof(arg64.Request)); | 
 | 1230 | 	err |= | 
 | 1231 | 	    copy_from_user(&arg64.error_info, &arg32->error_info, | 
 | 1232 | 			   sizeof(arg64.error_info)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1233 | 	err |= get_user(arg64.buf_size, &arg32->buf_size); | 
 | 1234 | 	err |= get_user(arg64.malloc_size, &arg32->malloc_size); | 
 | 1235 | 	err |= get_user(cp, &arg32->buf); | 
 | 1236 | 	arg64.buf = compat_ptr(cp); | 
 | 1237 | 	err |= copy_to_user(p, &arg64, sizeof(arg64)); | 
 | 1238 |  | 
 | 1239 | 	if (err) | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1240 | 		return -EFAULT; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1241 |  | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1242 | 	err = do_ioctl(bdev, mode, CCISS_BIG_PASSTHRU, (unsigned long)p); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1243 | 	if (err) | 
 | 1244 | 		return err; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1245 | 	err |= | 
 | 1246 | 	    copy_in_user(&arg32->error_info, &p->error_info, | 
 | 1247 | 			 sizeof(arg32->error_info)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1248 | 	if (err) | 
 | 1249 | 		return -EFAULT; | 
 | 1250 | 	return err; | 
 | 1251 | } | 
 | 1252 | #endif | 
| Christoph Hellwig | a885c8c | 2006-01-08 01:02:50 -0800 | [diff] [blame] | 1253 |  | 
 | 1254 | static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo) | 
 | 1255 | { | 
 | 1256 | 	drive_info_struct *drv = get_drv(bdev->bd_disk); | 
 | 1257 |  | 
 | 1258 | 	if (!drv->cylinders) | 
 | 1259 | 		return -ENXIO; | 
 | 1260 |  | 
 | 1261 | 	geo->heads = drv->heads; | 
 | 1262 | 	geo->sectors = drv->sectors; | 
 | 1263 | 	geo->cylinders = drv->cylinders; | 
 | 1264 | 	return 0; | 
 | 1265 | } | 
 | 1266 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1267 | static void check_ioctl_unit_attention(ctlr_info_t *h, CommandList_struct *c) | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 1268 | { | 
 | 1269 | 	if (c->err_info->CommandStatus == CMD_TARGET_STATUS && | 
 | 1270 | 			c->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION) | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1271 | 		(void)check_for_unit_attention(h, c); | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 1272 | } | 
| Stephen M. Cameron | 0a25a5a | 2010-08-26 13:55:34 -0500 | [diff] [blame] | 1273 |  | 
 | 1274 | static int cciss_getpciinfo(ctlr_info_t *h, void __user *argp) | 
 | 1275 | { | 
 | 1276 | 	cciss_pci_info_struct pciinfo; | 
 | 1277 |  | 
 | 1278 | 	if (!argp) | 
 | 1279 | 		return -EINVAL; | 
 | 1280 | 	pciinfo.domain = pci_domain_nr(h->pdev->bus); | 
 | 1281 | 	pciinfo.bus = h->pdev->bus->number; | 
 | 1282 | 	pciinfo.dev_fn = h->pdev->devfn; | 
 | 1283 | 	pciinfo.board_id = h->board_id; | 
 | 1284 | 	if (copy_to_user(argp, &pciinfo, sizeof(cciss_pci_info_struct))) | 
 | 1285 | 		return -EFAULT; | 
 | 1286 | 	return 0; | 
 | 1287 | } | 
 | 1288 |  | 
| Stephen M. Cameron | 576e661 | 2010-08-26 13:55:39 -0500 | [diff] [blame] | 1289 | static int cciss_getintinfo(ctlr_info_t *h, void __user *argp) | 
 | 1290 | { | 
 | 1291 | 	cciss_coalint_struct intinfo; | 
 | 1292 |  | 
 | 1293 | 	if (!argp) | 
 | 1294 | 		return -EINVAL; | 
 | 1295 | 	intinfo.delay = readl(&h->cfgtable->HostWrite.CoalIntDelay); | 
 | 1296 | 	intinfo.count = readl(&h->cfgtable->HostWrite.CoalIntCount); | 
 | 1297 | 	if (copy_to_user | 
 | 1298 | 	    (argp, &intinfo, sizeof(cciss_coalint_struct))) | 
 | 1299 | 		return -EFAULT; | 
 | 1300 | 	return 0; | 
 | 1301 | } | 
 | 1302 |  | 
| Stephen M. Cameron | 4c800ee | 2010-08-26 13:55:44 -0500 | [diff] [blame] | 1303 | static int cciss_setintinfo(ctlr_info_t *h, void __user *argp) | 
 | 1304 | { | 
 | 1305 | 	cciss_coalint_struct intinfo; | 
 | 1306 | 	unsigned long flags; | 
 | 1307 | 	int i; | 
 | 1308 |  | 
 | 1309 | 	if (!argp) | 
 | 1310 | 		return -EINVAL; | 
 | 1311 | 	if (!capable(CAP_SYS_ADMIN)) | 
 | 1312 | 		return -EPERM; | 
 | 1313 | 	if (copy_from_user(&intinfo, argp, sizeof(intinfo))) | 
 | 1314 | 		return -EFAULT; | 
 | 1315 | 	if ((intinfo.delay == 0) && (intinfo.count == 0)) | 
 | 1316 | 		return -EINVAL; | 
 | 1317 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 1318 | 	/* Update the field, and then ring the doorbell */ | 
 | 1319 | 	writel(intinfo.delay, &(h->cfgtable->HostWrite.CoalIntDelay)); | 
 | 1320 | 	writel(intinfo.count, &(h->cfgtable->HostWrite.CoalIntCount)); | 
 | 1321 | 	writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL); | 
 | 1322 |  | 
 | 1323 | 	for (i = 0; i < MAX_IOCTL_CONFIG_WAIT; i++) { | 
 | 1324 | 		if (!(readl(h->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq)) | 
 | 1325 | 			break; | 
 | 1326 | 		udelay(1000); /* delay and try again */ | 
 | 1327 | 	} | 
 | 1328 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 1329 | 	if (i >= MAX_IOCTL_CONFIG_WAIT) | 
 | 1330 | 		return -EAGAIN; | 
 | 1331 | 	return 0; | 
 | 1332 | } | 
 | 1333 |  | 
| Stephen M. Cameron | 2521610 | 2010-08-26 13:55:49 -0500 | [diff] [blame] | 1334 | static int cciss_getnodename(ctlr_info_t *h, void __user *argp) | 
 | 1335 | { | 
 | 1336 | 	NodeName_type NodeName; | 
 | 1337 | 	int i; | 
 | 1338 |  | 
 | 1339 | 	if (!argp) | 
 | 1340 | 		return -EINVAL; | 
 | 1341 | 	for (i = 0; i < 16; i++) | 
 | 1342 | 		NodeName[i] = readb(&h->cfgtable->ServerName[i]); | 
 | 1343 | 	if (copy_to_user(argp, NodeName, sizeof(NodeName_type))) | 
 | 1344 | 		return -EFAULT; | 
 | 1345 | 	return 0; | 
 | 1346 | } | 
 | 1347 |  | 
| Stephen M. Cameron | 4f43f32 | 2010-08-26 13:55:54 -0500 | [diff] [blame] | 1348 | static int cciss_setnodename(ctlr_info_t *h, void __user *argp) | 
 | 1349 | { | 
 | 1350 | 	NodeName_type NodeName; | 
 | 1351 | 	unsigned long flags; | 
 | 1352 | 	int i; | 
 | 1353 |  | 
 | 1354 | 	if (!argp) | 
 | 1355 | 		return -EINVAL; | 
 | 1356 | 	if (!capable(CAP_SYS_ADMIN)) | 
 | 1357 | 		return -EPERM; | 
 | 1358 | 	if (copy_from_user(NodeName, argp, sizeof(NodeName_type))) | 
 | 1359 | 		return -EFAULT; | 
 | 1360 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 1361 | 	/* Update the field, and then ring the doorbell */ | 
 | 1362 | 	for (i = 0; i < 16; i++) | 
 | 1363 | 		writeb(NodeName[i], &h->cfgtable->ServerName[i]); | 
 | 1364 | 	writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL); | 
 | 1365 | 	for (i = 0; i < MAX_IOCTL_CONFIG_WAIT; i++) { | 
 | 1366 | 		if (!(readl(h->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq)) | 
 | 1367 | 			break; | 
 | 1368 | 		udelay(1000); /* delay and try again */ | 
 | 1369 | 	} | 
 | 1370 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 1371 | 	if (i >= MAX_IOCTL_CONFIG_WAIT) | 
 | 1372 | 		return -EAGAIN; | 
 | 1373 | 	return 0; | 
 | 1374 | } | 
 | 1375 |  | 
| Stephen M. Cameron | 93c7493 | 2010-08-26 13:55:59 -0500 | [diff] [blame] | 1376 | static int cciss_getheartbeat(ctlr_info_t *h, void __user *argp) | 
 | 1377 | { | 
 | 1378 | 	Heartbeat_type heartbeat; | 
 | 1379 |  | 
 | 1380 | 	if (!argp) | 
 | 1381 | 		return -EINVAL; | 
 | 1382 | 	heartbeat = readl(&h->cfgtable->HeartBeat); | 
 | 1383 | 	if (copy_to_user(argp, &heartbeat, sizeof(Heartbeat_type))) | 
 | 1384 | 		return -EFAULT; | 
 | 1385 | 	return 0; | 
 | 1386 | } | 
 | 1387 |  | 
| Stephen M. Cameron | d18dfad | 2010-08-26 13:56:05 -0500 | [diff] [blame] | 1388 | static int cciss_getbustypes(ctlr_info_t *h, void __user *argp) | 
 | 1389 | { | 
 | 1390 | 	BusTypes_type BusTypes; | 
 | 1391 |  | 
 | 1392 | 	if (!argp) | 
 | 1393 | 		return -EINVAL; | 
 | 1394 | 	BusTypes = readl(&h->cfgtable->BusTypes); | 
 | 1395 | 	if (copy_to_user(argp, &BusTypes, sizeof(BusTypes_type))) | 
 | 1396 | 		return -EFAULT; | 
 | 1397 | 	return 0; | 
 | 1398 | } | 
 | 1399 |  | 
| Stephen M. Cameron | 8a4f7fb | 2010-08-26 13:56:10 -0500 | [diff] [blame] | 1400 | static int cciss_getfirmver(ctlr_info_t *h, void __user *argp) | 
 | 1401 | { | 
 | 1402 | 	FirmwareVer_type firmware; | 
 | 1403 |  | 
 | 1404 | 	if (!argp) | 
 | 1405 | 		return -EINVAL; | 
 | 1406 | 	memcpy(firmware, h->firm_ver, 4); | 
 | 1407 |  | 
 | 1408 | 	if (copy_to_user | 
 | 1409 | 	    (argp, firmware, sizeof(FirmwareVer_type))) | 
 | 1410 | 		return -EFAULT; | 
 | 1411 | 	return 0; | 
 | 1412 | } | 
 | 1413 |  | 
| Stephen M. Cameron | c525919 | 2010-08-26 13:56:15 -0500 | [diff] [blame] | 1414 | static int cciss_getdrivver(ctlr_info_t *h, void __user *argp) | 
 | 1415 | { | 
 | 1416 | 	DriverVer_type DriverVer = DRIVER_VERSION; | 
 | 1417 |  | 
 | 1418 | 	if (!argp) | 
 | 1419 | 		return -EINVAL; | 
 | 1420 | 	if (copy_to_user(argp, &DriverVer, sizeof(DriverVer_type))) | 
 | 1421 | 		return -EFAULT; | 
 | 1422 | 	return 0; | 
 | 1423 | } | 
 | 1424 |  | 
| Stephen M. Cameron | 0894b32 | 2010-08-26 13:56:20 -0500 | [diff] [blame] | 1425 | static int cciss_getluninfo(ctlr_info_t *h, | 
 | 1426 | 	struct gendisk *disk, void __user *argp) | 
 | 1427 | { | 
 | 1428 | 	LogvolInfo_struct luninfo; | 
 | 1429 | 	drive_info_struct *drv = get_drv(disk); | 
 | 1430 |  | 
 | 1431 | 	if (!argp) | 
 | 1432 | 		return -EINVAL; | 
 | 1433 | 	memcpy(&luninfo.LunID, drv->LunID, sizeof(luninfo.LunID)); | 
 | 1434 | 	luninfo.num_opens = drv->usage_count; | 
 | 1435 | 	luninfo.num_parts = 0; | 
 | 1436 | 	if (copy_to_user(argp, &luninfo, sizeof(LogvolInfo_struct))) | 
 | 1437 | 		return -EFAULT; | 
 | 1438 | 	return 0; | 
 | 1439 | } | 
 | 1440 |  | 
| Stephen M. Cameron | f32f125 | 2010-08-26 13:56:25 -0500 | [diff] [blame] | 1441 | static int cciss_passthru(ctlr_info_t *h, void __user *argp) | 
 | 1442 | { | 
 | 1443 | 	IOCTL_Command_struct iocommand; | 
 | 1444 | 	CommandList_struct *c; | 
 | 1445 | 	char *buff = NULL; | 
 | 1446 | 	u64bit temp64; | 
 | 1447 | 	DECLARE_COMPLETION_ONSTACK(wait); | 
 | 1448 |  | 
 | 1449 | 	if (!argp) | 
 | 1450 | 		return -EINVAL; | 
 | 1451 |  | 
 | 1452 | 	if (!capable(CAP_SYS_RAWIO)) | 
 | 1453 | 		return -EPERM; | 
 | 1454 |  | 
 | 1455 | 	if (copy_from_user | 
 | 1456 | 	    (&iocommand, argp, sizeof(IOCTL_Command_struct))) | 
 | 1457 | 		return -EFAULT; | 
 | 1458 | 	if ((iocommand.buf_size < 1) && | 
 | 1459 | 	    (iocommand.Request.Type.Direction != XFER_NONE)) { | 
 | 1460 | 		return -EINVAL; | 
 | 1461 | 	} | 
 | 1462 | 	if (iocommand.buf_size > 0) { | 
 | 1463 | 		buff = kmalloc(iocommand.buf_size, GFP_KERNEL); | 
 | 1464 | 		if (buff == NULL) | 
 | 1465 | 			return -EFAULT; | 
 | 1466 | 	} | 
 | 1467 | 	if (iocommand.Request.Type.Direction == XFER_WRITE) { | 
 | 1468 | 		/* Copy the data into the buffer we created */ | 
 | 1469 | 		if (copy_from_user(buff, iocommand.buf, iocommand.buf_size)) { | 
 | 1470 | 			kfree(buff); | 
 | 1471 | 			return -EFAULT; | 
 | 1472 | 		} | 
 | 1473 | 	} else { | 
 | 1474 | 		memset(buff, 0, iocommand.buf_size); | 
 | 1475 | 	} | 
 | 1476 | 	c = cmd_special_alloc(h); | 
 | 1477 | 	if (!c) { | 
 | 1478 | 		kfree(buff); | 
 | 1479 | 		return -ENOMEM; | 
 | 1480 | 	} | 
 | 1481 | 	/* Fill in the command type */ | 
 | 1482 | 	c->cmd_type = CMD_IOCTL_PEND; | 
 | 1483 | 	/* Fill in Command Header */ | 
 | 1484 | 	c->Header.ReplyQueue = 0;   /* unused in simple mode */ | 
 | 1485 | 	if (iocommand.buf_size > 0) { /* buffer to fill */ | 
 | 1486 | 		c->Header.SGList = 1; | 
 | 1487 | 		c->Header.SGTotal = 1; | 
 | 1488 | 	} else { /* no buffers to fill */ | 
 | 1489 | 		c->Header.SGList = 0; | 
 | 1490 | 		c->Header.SGTotal = 0; | 
 | 1491 | 	} | 
 | 1492 | 	c->Header.LUN = iocommand.LUN_info; | 
 | 1493 | 	/* use the kernel address the cmd block for tag */ | 
 | 1494 | 	c->Header.Tag.lower = c->busaddr; | 
 | 1495 |  | 
 | 1496 | 	/* Fill in Request block */ | 
 | 1497 | 	c->Request = iocommand.Request; | 
 | 1498 |  | 
 | 1499 | 	/* Fill in the scatter gather information */ | 
 | 1500 | 	if (iocommand.buf_size > 0) { | 
 | 1501 | 		temp64.val = pci_map_single(h->pdev, buff, | 
 | 1502 | 			iocommand.buf_size, PCI_DMA_BIDIRECTIONAL); | 
 | 1503 | 		c->SG[0].Addr.lower = temp64.val32.lower; | 
 | 1504 | 		c->SG[0].Addr.upper = temp64.val32.upper; | 
 | 1505 | 		c->SG[0].Len = iocommand.buf_size; | 
 | 1506 | 		c->SG[0].Ext = 0;  /* we are not chaining */ | 
 | 1507 | 	} | 
 | 1508 | 	c->waiting = &wait; | 
 | 1509 |  | 
 | 1510 | 	enqueue_cmd_and_start_io(h, c); | 
 | 1511 | 	wait_for_completion(&wait); | 
 | 1512 |  | 
 | 1513 | 	/* unlock the buffers from DMA */ | 
 | 1514 | 	temp64.val32.lower = c->SG[0].Addr.lower; | 
 | 1515 | 	temp64.val32.upper = c->SG[0].Addr.upper; | 
 | 1516 | 	pci_unmap_single(h->pdev, (dma_addr_t) temp64.val, iocommand.buf_size, | 
 | 1517 | 			 PCI_DMA_BIDIRECTIONAL); | 
 | 1518 | 	check_ioctl_unit_attention(h, c); | 
 | 1519 |  | 
 | 1520 | 	/* Copy the error information out */ | 
 | 1521 | 	iocommand.error_info = *(c->err_info); | 
 | 1522 | 	if (copy_to_user(argp, &iocommand, sizeof(IOCTL_Command_struct))) { | 
 | 1523 | 		kfree(buff); | 
 | 1524 | 		cmd_special_free(h, c); | 
 | 1525 | 		return -EFAULT; | 
 | 1526 | 	} | 
 | 1527 |  | 
 | 1528 | 	if (iocommand.Request.Type.Direction == XFER_READ) { | 
 | 1529 | 		/* Copy the data out of the buffer we created */ | 
 | 1530 | 		if (copy_to_user(iocommand.buf, buff, iocommand.buf_size)) { | 
 | 1531 | 			kfree(buff); | 
 | 1532 | 			cmd_special_free(h, c); | 
 | 1533 | 			return -EFAULT; | 
 | 1534 | 		} | 
 | 1535 | 	} | 
 | 1536 | 	kfree(buff); | 
 | 1537 | 	cmd_special_free(h, c); | 
 | 1538 | 	return 0; | 
 | 1539 | } | 
 | 1540 |  | 
| Stephen M. Cameron | 0c9f5ba | 2010-08-26 13:56:30 -0500 | [diff] [blame] | 1541 | static int cciss_bigpassthru(ctlr_info_t *h, void __user *argp) | 
 | 1542 | { | 
 | 1543 | 	BIG_IOCTL_Command_struct *ioc; | 
 | 1544 | 	CommandList_struct *c; | 
 | 1545 | 	unsigned char **buff = NULL; | 
 | 1546 | 	int *buff_size = NULL; | 
 | 1547 | 	u64bit temp64; | 
 | 1548 | 	BYTE sg_used = 0; | 
 | 1549 | 	int status = 0; | 
 | 1550 | 	int i; | 
 | 1551 | 	DECLARE_COMPLETION_ONSTACK(wait); | 
 | 1552 | 	__u32 left; | 
 | 1553 | 	__u32 sz; | 
 | 1554 | 	BYTE __user *data_ptr; | 
 | 1555 |  | 
 | 1556 | 	if (!argp) | 
 | 1557 | 		return -EINVAL; | 
 | 1558 | 	if (!capable(CAP_SYS_RAWIO)) | 
 | 1559 | 		return -EPERM; | 
| Stephen M. Cameron | fcab1c1 | 2011-03-12 10:02:24 +0100 | [diff] [blame] | 1560 | 	ioc = kmalloc(sizeof(*ioc), GFP_KERNEL); | 
| Stephen M. Cameron | 0c9f5ba | 2010-08-26 13:56:30 -0500 | [diff] [blame] | 1561 | 	if (!ioc) { | 
 | 1562 | 		status = -ENOMEM; | 
 | 1563 | 		goto cleanup1; | 
 | 1564 | 	} | 
 | 1565 | 	if (copy_from_user(ioc, argp, sizeof(*ioc))) { | 
 | 1566 | 		status = -EFAULT; | 
 | 1567 | 		goto cleanup1; | 
 | 1568 | 	} | 
 | 1569 | 	if ((ioc->buf_size < 1) && | 
 | 1570 | 	    (ioc->Request.Type.Direction != XFER_NONE)) { | 
 | 1571 | 		status = -EINVAL; | 
 | 1572 | 		goto cleanup1; | 
 | 1573 | 	} | 
 | 1574 | 	/* Check kmalloc limits  using all SGs */ | 
 | 1575 | 	if (ioc->malloc_size > MAX_KMALLOC_SIZE) { | 
 | 1576 | 		status = -EINVAL; | 
 | 1577 | 		goto cleanup1; | 
 | 1578 | 	} | 
 | 1579 | 	if (ioc->buf_size > ioc->malloc_size * MAXSGENTRIES) { | 
 | 1580 | 		status = -EINVAL; | 
 | 1581 | 		goto cleanup1; | 
 | 1582 | 	} | 
 | 1583 | 	buff = kzalloc(MAXSGENTRIES * sizeof(char *), GFP_KERNEL); | 
 | 1584 | 	if (!buff) { | 
 | 1585 | 		status = -ENOMEM; | 
 | 1586 | 		goto cleanup1; | 
 | 1587 | 	} | 
 | 1588 | 	buff_size = kmalloc(MAXSGENTRIES * sizeof(int), GFP_KERNEL); | 
 | 1589 | 	if (!buff_size) { | 
 | 1590 | 		status = -ENOMEM; | 
 | 1591 | 		goto cleanup1; | 
 | 1592 | 	} | 
 | 1593 | 	left = ioc->buf_size; | 
 | 1594 | 	data_ptr = ioc->buf; | 
 | 1595 | 	while (left) { | 
 | 1596 | 		sz = (left > ioc->malloc_size) ? ioc->malloc_size : left; | 
 | 1597 | 		buff_size[sg_used] = sz; | 
 | 1598 | 		buff[sg_used] = kmalloc(sz, GFP_KERNEL); | 
 | 1599 | 		if (buff[sg_used] == NULL) { | 
 | 1600 | 			status = -ENOMEM; | 
 | 1601 | 			goto cleanup1; | 
 | 1602 | 		} | 
 | 1603 | 		if (ioc->Request.Type.Direction == XFER_WRITE) { | 
 | 1604 | 			if (copy_from_user(buff[sg_used], data_ptr, sz)) { | 
 | 1605 | 				status = -EFAULT; | 
 | 1606 | 				goto cleanup1; | 
 | 1607 | 			} | 
 | 1608 | 		} else { | 
 | 1609 | 			memset(buff[sg_used], 0, sz); | 
 | 1610 | 		} | 
 | 1611 | 		left -= sz; | 
 | 1612 | 		data_ptr += sz; | 
 | 1613 | 		sg_used++; | 
 | 1614 | 	} | 
 | 1615 | 	c = cmd_special_alloc(h); | 
 | 1616 | 	if (!c) { | 
 | 1617 | 		status = -ENOMEM; | 
 | 1618 | 		goto cleanup1; | 
 | 1619 | 	} | 
 | 1620 | 	c->cmd_type = CMD_IOCTL_PEND; | 
 | 1621 | 	c->Header.ReplyQueue = 0; | 
| Stephen M. Cameron | fcfb5c0 | 2010-08-26 13:56:35 -0500 | [diff] [blame] | 1622 | 	c->Header.SGList = sg_used; | 
 | 1623 | 	c->Header.SGTotal = sg_used; | 
| Stephen M. Cameron | 0c9f5ba | 2010-08-26 13:56:30 -0500 | [diff] [blame] | 1624 | 	c->Header.LUN = ioc->LUN_info; | 
 | 1625 | 	c->Header.Tag.lower = c->busaddr; | 
 | 1626 |  | 
 | 1627 | 	c->Request = ioc->Request; | 
| Stephen M. Cameron | fcfb5c0 | 2010-08-26 13:56:35 -0500 | [diff] [blame] | 1628 | 	for (i = 0; i < sg_used; i++) { | 
 | 1629 | 		temp64.val = pci_map_single(h->pdev, buff[i], buff_size[i], | 
| Stephen M. Cameron | 0c9f5ba | 2010-08-26 13:56:30 -0500 | [diff] [blame] | 1630 | 				    PCI_DMA_BIDIRECTIONAL); | 
| Stephen M. Cameron | fcfb5c0 | 2010-08-26 13:56:35 -0500 | [diff] [blame] | 1631 | 		c->SG[i].Addr.lower = temp64.val32.lower; | 
 | 1632 | 		c->SG[i].Addr.upper = temp64.val32.upper; | 
 | 1633 | 		c->SG[i].Len = buff_size[i]; | 
 | 1634 | 		c->SG[i].Ext = 0;	/* we are not chaining */ | 
| Stephen M. Cameron | 0c9f5ba | 2010-08-26 13:56:30 -0500 | [diff] [blame] | 1635 | 	} | 
 | 1636 | 	c->waiting = &wait; | 
 | 1637 | 	enqueue_cmd_and_start_io(h, c); | 
 | 1638 | 	wait_for_completion(&wait); | 
 | 1639 | 	/* unlock the buffers from DMA */ | 
 | 1640 | 	for (i = 0; i < sg_used; i++) { | 
 | 1641 | 		temp64.val32.lower = c->SG[i].Addr.lower; | 
 | 1642 | 		temp64.val32.upper = c->SG[i].Addr.upper; | 
 | 1643 | 		pci_unmap_single(h->pdev, | 
 | 1644 | 			(dma_addr_t) temp64.val, buff_size[i], | 
 | 1645 | 			PCI_DMA_BIDIRECTIONAL); | 
 | 1646 | 	} | 
 | 1647 | 	check_ioctl_unit_attention(h, c); | 
 | 1648 | 	/* Copy the error information out */ | 
 | 1649 | 	ioc->error_info = *(c->err_info); | 
 | 1650 | 	if (copy_to_user(argp, ioc, sizeof(*ioc))) { | 
 | 1651 | 		cmd_special_free(h, c); | 
 | 1652 | 		status = -EFAULT; | 
 | 1653 | 		goto cleanup1; | 
 | 1654 | 	} | 
 | 1655 | 	if (ioc->Request.Type.Direction == XFER_READ) { | 
 | 1656 | 		/* Copy the data out of the buffer we created */ | 
 | 1657 | 		BYTE __user *ptr = ioc->buf; | 
 | 1658 | 		for (i = 0; i < sg_used; i++) { | 
 | 1659 | 			if (copy_to_user(ptr, buff[i], buff_size[i])) { | 
 | 1660 | 				cmd_special_free(h, c); | 
 | 1661 | 				status = -EFAULT; | 
 | 1662 | 				goto cleanup1; | 
 | 1663 | 			} | 
 | 1664 | 			ptr += buff_size[i]; | 
 | 1665 | 		} | 
 | 1666 | 	} | 
 | 1667 | 	cmd_special_free(h, c); | 
 | 1668 | 	status = 0; | 
 | 1669 | cleanup1: | 
 | 1670 | 	if (buff) { | 
 | 1671 | 		for (i = 0; i < sg_used; i++) | 
 | 1672 | 			kfree(buff[i]); | 
 | 1673 | 		kfree(buff); | 
 | 1674 | 	} | 
 | 1675 | 	kfree(buff_size); | 
 | 1676 | 	kfree(ioc); | 
 | 1677 | 	return status; | 
 | 1678 | } | 
 | 1679 |  | 
| Al Viro | ef7822c | 2008-03-02 09:26:41 -0500 | [diff] [blame] | 1680 | static int cciss_ioctl(struct block_device *bdev, fmode_t mode, | 
| Stephen M. Cameron | c525919 | 2010-08-26 13:56:15 -0500 | [diff] [blame] | 1681 | 	unsigned int cmd, unsigned long arg) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1682 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1683 | 	struct gendisk *disk = bdev->bd_disk; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1684 | 	ctlr_info_t *h = get_host(disk); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1685 | 	void __user *argp = (void __user *)arg; | 
 | 1686 |  | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 1687 | 	dev_dbg(&h->pdev->dev, "cciss_ioctl: Called with cmd=%x %lx\n", | 
 | 1688 | 		cmd, arg); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1689 | 	switch (cmd) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1690 | 	case CCISS_GETPCIINFO: | 
| Stephen M. Cameron | 0a25a5a | 2010-08-26 13:55:34 -0500 | [diff] [blame] | 1691 | 		return cciss_getpciinfo(h, argp); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1692 | 	case CCISS_GETINTINFO: | 
| Stephen M. Cameron | 576e661 | 2010-08-26 13:55:39 -0500 | [diff] [blame] | 1693 | 		return cciss_getintinfo(h, argp); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1694 | 	case CCISS_SETINTINFO: | 
| Stephen M. Cameron | 4c800ee | 2010-08-26 13:55:44 -0500 | [diff] [blame] | 1695 | 		return cciss_setintinfo(h, argp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1696 | 	case CCISS_GETNODENAME: | 
| Stephen M. Cameron | 2521610 | 2010-08-26 13:55:49 -0500 | [diff] [blame] | 1697 | 		return cciss_getnodename(h, argp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1698 | 	case CCISS_SETNODENAME: | 
| Stephen M. Cameron | 4f43f32 | 2010-08-26 13:55:54 -0500 | [diff] [blame] | 1699 | 		return cciss_setnodename(h, argp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1700 | 	case CCISS_GETHEARTBEAT: | 
| Stephen M. Cameron | 93c7493 | 2010-08-26 13:55:59 -0500 | [diff] [blame] | 1701 | 		return cciss_getheartbeat(h, argp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1702 | 	case CCISS_GETBUSTYPES: | 
| Stephen M. Cameron | d18dfad | 2010-08-26 13:56:05 -0500 | [diff] [blame] | 1703 | 		return cciss_getbustypes(h, argp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1704 | 	case CCISS_GETFIRMVER: | 
| Stephen M. Cameron | 8a4f7fb | 2010-08-26 13:56:10 -0500 | [diff] [blame] | 1705 | 		return cciss_getfirmver(h, argp); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1706 | 	case CCISS_GETDRIVVER: | 
| Stephen M. Cameron | c525919 | 2010-08-26 13:56:15 -0500 | [diff] [blame] | 1707 | 		return cciss_getdrivver(h, argp); | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 1708 | 	case CCISS_DEREGDISK: | 
 | 1709 | 	case CCISS_REGNEWD: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1710 | 	case CCISS_REVALIDVOLS: | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1711 | 		return rebuild_lun_table(h, 0, 1); | 
| Stephen M. Cameron | 0894b32 | 2010-08-26 13:56:20 -0500 | [diff] [blame] | 1712 | 	case CCISS_GETLUNINFO: | 
 | 1713 | 		return cciss_getluninfo(h, disk, argp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1714 | 	case CCISS_PASSTHRU: | 
| Stephen M. Cameron | f32f125 | 2010-08-26 13:56:25 -0500 | [diff] [blame] | 1715 | 		return cciss_passthru(h, argp); | 
| Stephen M. Cameron | 0c9f5ba | 2010-08-26 13:56:30 -0500 | [diff] [blame] | 1716 | 	case CCISS_BIG_PASSTHRU: | 
 | 1717 | 		return cciss_bigpassthru(h, argp); | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 1718 |  | 
| Paolo Bonzini | 3b8373b | 2012-01-12 16:01:27 +0100 | [diff] [blame] | 1719 | 	/* scsi_cmd_blk_ioctl handles these, below, though some are not */ | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 1720 | 	/* very meaningful for cciss.  SG_IO is the main one people want. */ | 
 | 1721 |  | 
 | 1722 | 	case SG_GET_VERSION_NUM: | 
 | 1723 | 	case SG_SET_TIMEOUT: | 
 | 1724 | 	case SG_GET_TIMEOUT: | 
 | 1725 | 	case SG_GET_RESERVED_SIZE: | 
 | 1726 | 	case SG_SET_RESERVED_SIZE: | 
 | 1727 | 	case SG_EMULATED_HOST: | 
 | 1728 | 	case SG_IO: | 
 | 1729 | 	case SCSI_IOCTL_SEND_COMMAND: | 
| Paolo Bonzini | 3b8373b | 2012-01-12 16:01:27 +0100 | [diff] [blame] | 1730 | 		return scsi_cmd_blk_ioctl(bdev, mode, cmd, argp); | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 1731 |  | 
| Paolo Bonzini | 3b8373b | 2012-01-12 16:01:27 +0100 | [diff] [blame] | 1732 | 	/* scsi_cmd_blk_ioctl would normally handle these, below, but */ | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 1733 | 	/* they aren't a good fit for cciss, as CD-ROMs are */ | 
 | 1734 | 	/* not supported, and we don't have any bus/target/lun */ | 
 | 1735 | 	/* which we present to the kernel. */ | 
 | 1736 |  | 
 | 1737 | 	case CDROM_SEND_PACKET: | 
 | 1738 | 	case CDROMCLOSETRAY: | 
 | 1739 | 	case CDROMEJECT: | 
 | 1740 | 	case SCSI_IOCTL_GET_IDLUN: | 
 | 1741 | 	case SCSI_IOCTL_GET_BUS_NUMBER: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1742 | 	default: | 
 | 1743 | 		return -ENOTTY; | 
 | 1744 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1745 | } | 
 | 1746 |  | 
| Jens Axboe | 7b30f09 | 2006-07-25 15:02:48 +0200 | [diff] [blame] | 1747 | static void cciss_check_queues(ctlr_info_t *h) | 
 | 1748 | { | 
 | 1749 | 	int start_queue = h->next_to_run; | 
 | 1750 | 	int i; | 
 | 1751 |  | 
 | 1752 | 	/* check to see if we have maxed out the number of commands that can | 
 | 1753 | 	 * be placed on the queue.  If so then exit.  We do this check here | 
 | 1754 | 	 * in case the interrupt we serviced was from an ioctl and did not | 
 | 1755 | 	 * free any new commands. | 
 | 1756 | 	 */ | 
| Mike Miller | f880632 | 2006-12-06 20:35:01 -0800 | [diff] [blame] | 1757 | 	if ((find_first_zero_bit(h->cmd_pool_bits, h->nr_cmds)) == h->nr_cmds) | 
| Jens Axboe | 7b30f09 | 2006-07-25 15:02:48 +0200 | [diff] [blame] | 1758 | 		return; | 
 | 1759 |  | 
 | 1760 | 	/* We have room on the queue for more commands.  Now we need to queue | 
 | 1761 | 	 * them up.  We will also keep track of the next queue to run so | 
 | 1762 | 	 * that every queue gets a chance to be started first. | 
 | 1763 | 	 */ | 
 | 1764 | 	for (i = 0; i < h->highest_lun + 1; i++) { | 
 | 1765 | 		int curr_queue = (start_queue + i) % (h->highest_lun + 1); | 
 | 1766 | 		/* make sure the disk has been added and the drive is real | 
 | 1767 | 		 * because this can be called from the middle of init_one. | 
 | 1768 | 		 */ | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 1769 | 		if (!h->drv[curr_queue]) | 
 | 1770 | 			continue; | 
 | 1771 | 		if (!(h->drv[curr_queue]->queue) || | 
 | 1772 | 			!(h->drv[curr_queue]->heads)) | 
| Jens Axboe | 7b30f09 | 2006-07-25 15:02:48 +0200 | [diff] [blame] | 1773 | 			continue; | 
 | 1774 | 		blk_start_queue(h->gendisk[curr_queue]->queue); | 
 | 1775 |  | 
 | 1776 | 		/* check to see if we have maxed out the number of commands | 
 | 1777 | 		 * that can be placed on the queue. | 
 | 1778 | 		 */ | 
| Mike Miller | f880632 | 2006-12-06 20:35:01 -0800 | [diff] [blame] | 1779 | 		if ((find_first_zero_bit(h->cmd_pool_bits, h->nr_cmds)) == h->nr_cmds) { | 
| Jens Axboe | 7b30f09 | 2006-07-25 15:02:48 +0200 | [diff] [blame] | 1780 | 			if (curr_queue == start_queue) { | 
 | 1781 | 				h->next_to_run = | 
 | 1782 | 				    (start_queue + 1) % (h->highest_lun + 1); | 
 | 1783 | 				break; | 
 | 1784 | 			} else { | 
 | 1785 | 				h->next_to_run = curr_queue; | 
 | 1786 | 				break; | 
 | 1787 | 			} | 
| Jens Axboe | 7b30f09 | 2006-07-25 15:02:48 +0200 | [diff] [blame] | 1788 | 		} | 
 | 1789 | 	} | 
 | 1790 | } | 
 | 1791 |  | 
| Mike Miller | ca1e048 | 2006-04-10 15:38:07 -0700 | [diff] [blame] | 1792 | static void cciss_softirq_done(struct request *rq) | 
 | 1793 | { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1794 | 	CommandList_struct *c = rq->completion_data; | 
 | 1795 | 	ctlr_info_t *h = hba[c->ctlr]; | 
 | 1796 | 	SGDescriptor_struct *curr_sg = c->SG; | 
| Mike Miller | ca1e048 | 2006-04-10 15:38:07 -0700 | [diff] [blame] | 1797 | 	u64bit temp64; | 
| Mike Miller | 664a717 | 2010-06-02 12:57:58 -0700 | [diff] [blame] | 1798 | 	unsigned long flags; | 
| Mike Miller | ca1e048 | 2006-04-10 15:38:07 -0700 | [diff] [blame] | 1799 | 	int i, ddir; | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 1800 | 	int sg_index = 0; | 
| Mike Miller | ca1e048 | 2006-04-10 15:38:07 -0700 | [diff] [blame] | 1801 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1802 | 	if (c->Request.Type.Direction == XFER_READ) | 
| Mike Miller | ca1e048 | 2006-04-10 15:38:07 -0700 | [diff] [blame] | 1803 | 		ddir = PCI_DMA_FROMDEVICE; | 
 | 1804 | 	else | 
 | 1805 | 		ddir = PCI_DMA_TODEVICE; | 
 | 1806 |  | 
 | 1807 | 	/* command did not need to be retried */ | 
 | 1808 | 	/* unmap the DMA mapping for all the scatter gather elements */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1809 | 	for (i = 0; i < c->Header.SGList; i++) { | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 1810 | 		if (curr_sg[sg_index].Ext == CCISS_SG_CHAIN) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1811 | 			cciss_unmap_sg_chain_block(h, c); | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 1812 | 			/* Point to the next block */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1813 | 			curr_sg = h->cmd_sg_list[c->cmdindex]; | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 1814 | 			sg_index = 0; | 
 | 1815 | 		} | 
 | 1816 | 		temp64.val32.lower = curr_sg[sg_index].Addr.lower; | 
 | 1817 | 		temp64.val32.upper = curr_sg[sg_index].Addr.upper; | 
 | 1818 | 		pci_unmap_page(h->pdev, temp64.val, curr_sg[sg_index].Len, | 
 | 1819 | 				ddir); | 
 | 1820 | 		++sg_index; | 
| Mike Miller | ca1e048 | 2006-04-10 15:38:07 -0700 | [diff] [blame] | 1821 | 	} | 
 | 1822 |  | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 1823 | 	dev_dbg(&h->pdev->dev, "Done with %p\n", rq); | 
| Mike Miller | ca1e048 | 2006-04-10 15:38:07 -0700 | [diff] [blame] | 1824 |  | 
| Tejun Heo | c3a4d78 | 2009-05-07 22:24:37 +0900 | [diff] [blame] | 1825 | 	/* set the residual count for pc requests */ | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 1826 | 	if (rq->cmd_type == REQ_TYPE_BLOCK_PC) | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1827 | 		rq->resid_len = c->err_info->ResidualCnt; | 
| Jens Axboe | ac44e5b | 2009-03-27 10:43:52 +0100 | [diff] [blame] | 1828 |  | 
| Tejun Heo | c3a4d78 | 2009-05-07 22:24:37 +0900 | [diff] [blame] | 1829 | 	blk_end_request_all(rq, (rq->errors == 0) ? 0 : -EIO); | 
| Kiyoshi Ueda | 3daeea2 | 2007-12-11 17:50:03 -0500 | [diff] [blame] | 1830 |  | 
| Mike Miller | ca1e048 | 2006-04-10 15:38:07 -0700 | [diff] [blame] | 1831 | 	spin_lock_irqsave(&h->lock, flags); | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 1832 | 	cmd_free(h, c); | 
| Jens Axboe | 7b30f09 | 2006-07-25 15:02:48 +0200 | [diff] [blame] | 1833 | 	cciss_check_queues(h); | 
| Mike Miller | ca1e048 | 2006-04-10 15:38:07 -0700 | [diff] [blame] | 1834 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 1835 | } | 
 | 1836 |  | 
| Stephen M. Cameron | 39ccf9a | 2009-09-17 13:48:00 -0500 | [diff] [blame] | 1837 | static inline void log_unit_to_scsi3addr(ctlr_info_t *h, | 
 | 1838 | 	unsigned char scsi3addr[], uint32_t log_unit) | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 1839 | { | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 1840 | 	memcpy(scsi3addr, h->drv[log_unit]->LunID, | 
 | 1841 | 		sizeof(h->drv[log_unit]->LunID)); | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 1842 | } | 
 | 1843 |  | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 1844 | /* This function gets the SCSI vendor, model, and revision of a logical drive | 
 | 1845 |  * via the inquiry page 0.  Model, vendor, and rev are set to empty strings if | 
 | 1846 |  * they cannot be read. | 
 | 1847 |  */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1848 | static void cciss_get_device_descr(ctlr_info_t *h, int logvol, | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 1849 | 				   char *vendor, char *model, char *rev) | 
 | 1850 | { | 
 | 1851 | 	int rc; | 
 | 1852 | 	InquiryData_struct *inq_buf; | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 1853 | 	unsigned char scsi3addr[8]; | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 1854 |  | 
 | 1855 | 	*vendor = '\0'; | 
 | 1856 | 	*model = '\0'; | 
 | 1857 | 	*rev = '\0'; | 
 | 1858 |  | 
 | 1859 | 	inq_buf = kzalloc(sizeof(InquiryData_struct), GFP_KERNEL); | 
 | 1860 | 	if (!inq_buf) | 
 | 1861 | 		return; | 
 | 1862 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1863 | 	log_unit_to_scsi3addr(h, scsi3addr, logvol); | 
 | 1864 | 	rc = sendcmd_withirq(h, CISS_INQUIRY, inq_buf, sizeof(*inq_buf), 0, | 
| Stephen M. Cameron | 7b838bd | 2009-11-12 12:49:30 -0600 | [diff] [blame] | 1865 | 			scsi3addr, TYPE_CMD); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 1866 | 	if (rc == IO_OK) { | 
 | 1867 | 		memcpy(vendor, &inq_buf->data_byte[8], VENDOR_LEN); | 
 | 1868 | 		vendor[VENDOR_LEN] = '\0'; | 
 | 1869 | 		memcpy(model, &inq_buf->data_byte[16], MODEL_LEN); | 
 | 1870 | 		model[MODEL_LEN] = '\0'; | 
 | 1871 | 		memcpy(rev, &inq_buf->data_byte[32], REV_LEN); | 
 | 1872 | 		rev[REV_LEN] = '\0'; | 
 | 1873 | 	} | 
 | 1874 |  | 
 | 1875 | 	kfree(inq_buf); | 
 | 1876 | 	return; | 
 | 1877 | } | 
 | 1878 |  | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 1879 | /* This function gets the serial number of a logical drive via | 
 | 1880 |  * inquiry page 0x83.  Serial no. is 16 bytes.  If the serial | 
 | 1881 |  * number cannot be had, for whatever reason, 16 bytes of 0xff | 
 | 1882 |  * are returned instead. | 
 | 1883 |  */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1884 | static void cciss_get_serial_no(ctlr_info_t *h, int logvol, | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 1885 | 				unsigned char *serial_no, int buflen) | 
 | 1886 | { | 
 | 1887 | #define PAGE_83_INQ_BYTES 64 | 
 | 1888 | 	int rc; | 
 | 1889 | 	unsigned char *buf; | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 1890 | 	unsigned char scsi3addr[8]; | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 1891 |  | 
 | 1892 | 	if (buflen > 16) | 
 | 1893 | 		buflen = 16; | 
 | 1894 | 	memset(serial_no, 0xff, buflen); | 
 | 1895 | 	buf = kzalloc(PAGE_83_INQ_BYTES, GFP_KERNEL); | 
 | 1896 | 	if (!buf) | 
 | 1897 | 		return; | 
 | 1898 | 	memset(serial_no, 0, buflen); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1899 | 	log_unit_to_scsi3addr(h, scsi3addr, logvol); | 
 | 1900 | 	rc = sendcmd_withirq(h, CISS_INQUIRY, buf, | 
| Stephen M. Cameron | 7b838bd | 2009-11-12 12:49:30 -0600 | [diff] [blame] | 1901 | 		PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD); | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 1902 | 	if (rc == IO_OK) | 
 | 1903 | 		memcpy(serial_no, &buf[8], buflen); | 
 | 1904 | 	kfree(buf); | 
 | 1905 | 	return; | 
 | 1906 | } | 
 | 1907 |  | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 1908 | /* | 
 | 1909 |  * cciss_add_disk sets up the block device queue for a logical drive | 
 | 1910 |  */ | 
 | 1911 | static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk, | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 1912 | 				int drv_index) | 
 | 1913 | { | 
 | 1914 | 	disk->queue = blk_init_queue(do_cciss_request, &h->lock); | 
| Stephen M. Cameron | e8074f7 | 2009-09-17 13:47:24 -0500 | [diff] [blame] | 1915 | 	if (!disk->queue) | 
 | 1916 | 		goto init_queue_failure; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 1917 | 	sprintf(disk->disk_name, "cciss/c%dd%d", h->ctlr, drv_index); | 
 | 1918 | 	disk->major = h->major; | 
 | 1919 | 	disk->first_minor = drv_index << NWD_SHIFT; | 
 | 1920 | 	disk->fops = &cciss_fops; | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 1921 | 	if (cciss_create_ld_sysfs_entry(h, drv_index)) | 
 | 1922 | 		goto cleanup_queue; | 
 | 1923 | 	disk->private_data = h->drv[drv_index]; | 
 | 1924 | 	disk->driverfs_dev = &h->drv[drv_index]->dev; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 1925 |  | 
 | 1926 | 	/* Set up queue information */ | 
 | 1927 | 	blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask); | 
 | 1928 |  | 
 | 1929 | 	/* This is a hardware imposed limit. */ | 
| Martin K. Petersen | 8a78362 | 2010-02-26 00:20:39 -0500 | [diff] [blame] | 1930 | 	blk_queue_max_segments(disk->queue, h->maxsgentries); | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 1931 |  | 
| Martin K. Petersen | 086fa5f | 2010-02-26 00:20:38 -0500 | [diff] [blame] | 1932 | 	blk_queue_max_hw_sectors(disk->queue, h->cciss_max_sectors); | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 1933 |  | 
 | 1934 | 	blk_queue_softirq_done(disk->queue, cciss_softirq_done); | 
 | 1935 |  | 
 | 1936 | 	disk->queue->queuedata = h; | 
 | 1937 |  | 
| Martin K. Petersen | e1defc4 | 2009-05-22 17:17:49 -0400 | [diff] [blame] | 1938 | 	blk_queue_logical_block_size(disk->queue, | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 1939 | 				     h->drv[drv_index]->block_size); | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 1940 |  | 
 | 1941 | 	/* Make sure all queue data is written out before */ | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 1942 | 	/* setting h->drv[drv_index]->queue, as setting this */ | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 1943 | 	/* allows the interrupt handler to start the queue */ | 
 | 1944 | 	wmb(); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 1945 | 	h->drv[drv_index]->queue = disk->queue; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 1946 | 	add_disk(disk); | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 1947 | 	return 0; | 
 | 1948 |  | 
 | 1949 | cleanup_queue: | 
 | 1950 | 	blk_cleanup_queue(disk->queue); | 
 | 1951 | 	disk->queue = NULL; | 
| Stephen M. Cameron | e8074f7 | 2009-09-17 13:47:24 -0500 | [diff] [blame] | 1952 | init_queue_failure: | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 1953 | 	return -1; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 1954 | } | 
 | 1955 |  | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 1956 | /* This function will check the usage_count of the drive to be updated/added. | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 1957 |  * If the usage_count is zero and it is a heretofore unknown drive, or, | 
 | 1958 |  * the drive's capacity, geometry, or serial number has changed, | 
 | 1959 |  * then the drive information will be updated and the disk will be | 
 | 1960 |  * re-registered with the kernel.  If these conditions don't hold, | 
 | 1961 |  * then it will be left alone for the next reboot.  The exception to this | 
 | 1962 |  * is disk 0 which will always be left registered with the kernel since it | 
 | 1963 |  * is also the controller node.  Any changes to disk 0 will show up on | 
 | 1964 |  * the next reboot. | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1965 |  */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1966 | static void cciss_update_drive_info(ctlr_info_t *h, int drv_index, | 
 | 1967 | 	int first_time, int via_ioctl) | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 1968 | { | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 1969 | 	struct gendisk *disk; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 1970 | 	InquiryData_struct *inq_buff = NULL; | 
 | 1971 | 	unsigned int block_size; | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 1972 | 	sector_t total_size; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 1973 | 	unsigned long flags = 0; | 
 | 1974 | 	int ret = 0; | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 1975 | 	drive_info_struct *drvinfo; | 
 | 1976 |  | 
 | 1977 | 	/* Get information about the disk and modify the driver structure */ | 
 | 1978 | 	inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 1979 | 	drvinfo = kzalloc(sizeof(*drvinfo), GFP_KERNEL); | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 1980 | 	if (inq_buff == NULL || drvinfo == NULL) | 
 | 1981 | 		goto mem_msg; | 
 | 1982 |  | 
 | 1983 | 	/* testing to see if 16-byte CDBs are already being used */ | 
 | 1984 | 	if (h->cciss_read == CCISS_READ_16) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1985 | 		cciss_read_capacity_16(h, drv_index, | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 1986 | 			&total_size, &block_size); | 
 | 1987 |  | 
 | 1988 | 	} else { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1989 | 		cciss_read_capacity(h, drv_index, &total_size, &block_size); | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 1990 | 		/* if read_capacity returns all F's this volume is >2TB */ | 
 | 1991 | 		/* in size so we switch to 16-byte CDB's for all */ | 
 | 1992 | 		/* read/write ops */ | 
 | 1993 | 		if (total_size == 0xFFFFFFFFULL) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 1994 | 			cciss_read_capacity_16(h, drv_index, | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 1995 | 			&total_size, &block_size); | 
 | 1996 | 			h->cciss_read = CCISS_READ_16; | 
 | 1997 | 			h->cciss_write = CCISS_WRITE_16; | 
 | 1998 | 		} else { | 
 | 1999 | 			h->cciss_read = CCISS_READ_10; | 
 | 2000 | 			h->cciss_write = CCISS_WRITE_10; | 
 | 2001 | 		} | 
 | 2002 | 	} | 
 | 2003 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2004 | 	cciss_geometry_inquiry(h, drv_index, total_size, block_size, | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2005 | 			       inq_buff, drvinfo); | 
 | 2006 | 	drvinfo->block_size = block_size; | 
 | 2007 | 	drvinfo->nr_blocks = total_size + 1; | 
 | 2008 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2009 | 	cciss_get_device_descr(h, drv_index, drvinfo->vendor, | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 2010 | 				drvinfo->model, drvinfo->rev); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2011 | 	cciss_get_serial_no(h, drv_index, drvinfo->serial_no, | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2012 | 			sizeof(drvinfo->serial_no)); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2013 | 	/* Save the lunid in case we deregister the disk, below. */ | 
 | 2014 | 	memcpy(drvinfo->LunID, h->drv[drv_index]->LunID, | 
 | 2015 | 		sizeof(drvinfo->LunID)); | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2016 |  | 
 | 2017 | 	/* Is it the same disk we already know, and nothing's changed? */ | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2018 | 	if (h->drv[drv_index]->raid_level != -1 && | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2019 | 		((memcmp(drvinfo->serial_no, | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2020 | 				h->drv[drv_index]->serial_no, 16) == 0) && | 
 | 2021 | 		drvinfo->block_size == h->drv[drv_index]->block_size && | 
 | 2022 | 		drvinfo->nr_blocks == h->drv[drv_index]->nr_blocks && | 
 | 2023 | 		drvinfo->heads == h->drv[drv_index]->heads && | 
 | 2024 | 		drvinfo->sectors == h->drv[drv_index]->sectors && | 
 | 2025 | 		drvinfo->cylinders == h->drv[drv_index]->cylinders)) | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2026 | 			/* The disk is unchanged, nothing to update */ | 
 | 2027 | 			goto freeret; | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2028 |  | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2029 | 	/* If we get here it's not the same disk, or something's changed, | 
 | 2030 | 	 * so we need to * deregister it, and re-register it, if it's not | 
 | 2031 | 	 * in use. | 
 | 2032 | 	 * If the disk already exists then deregister it before proceeding | 
 | 2033 | 	 * (unless it's the first disk (for the controller node). | 
 | 2034 | 	 */ | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2035 | 	if (h->drv[drv_index]->raid_level != -1 && drv_index != 0) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2036 | 		dev_warn(&h->pdev->dev, "disk %d has changed.\n", drv_index); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2037 | 		spin_lock_irqsave(&h->lock, flags); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2038 | 		h->drv[drv_index]->busy_configuring = 1; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2039 | 		spin_unlock_irqrestore(&h->lock, flags); | 
| scameron@beardog.cca.cpqcorp.net | e14ac67 | 2008-04-17 13:19:03 +0200 | [diff] [blame] | 2040 |  | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2041 | 		/* deregister_disk sets h->drv[drv_index]->queue = NULL | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2042 | 		 * which keeps the interrupt handler from starting | 
 | 2043 | 		 * the queue. | 
 | 2044 | 		 */ | 
| Stephen M. Cameron | 2d11d99 | 2009-09-17 13:47:44 -0500 | [diff] [blame] | 2045 | 		ret = deregister_disk(h, drv_index, 0, via_ioctl); | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2046 | 	} | 
 | 2047 |  | 
 | 2048 | 	/* If the disk is in use return */ | 
 | 2049 | 	if (ret) | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2050 | 		goto freeret; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2051 |  | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2052 | 	/* Save the new information from cciss_geometry_inquiry | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2053 | 	 * and serial number inquiry.  If the disk was deregistered | 
 | 2054 | 	 * above, then h->drv[drv_index] will be NULL. | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2055 | 	 */ | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2056 | 	if (h->drv[drv_index] == NULL) { | 
 | 2057 | 		drvinfo->device_initialized = 0; | 
 | 2058 | 		h->drv[drv_index] = drvinfo; | 
 | 2059 | 		drvinfo = NULL; /* so it won't be freed below. */ | 
 | 2060 | 	} else { | 
 | 2061 | 		/* special case for cxd0 */ | 
 | 2062 | 		h->drv[drv_index]->block_size = drvinfo->block_size; | 
 | 2063 | 		h->drv[drv_index]->nr_blocks = drvinfo->nr_blocks; | 
 | 2064 | 		h->drv[drv_index]->heads = drvinfo->heads; | 
 | 2065 | 		h->drv[drv_index]->sectors = drvinfo->sectors; | 
 | 2066 | 		h->drv[drv_index]->cylinders = drvinfo->cylinders; | 
 | 2067 | 		h->drv[drv_index]->raid_level = drvinfo->raid_level; | 
 | 2068 | 		memcpy(h->drv[drv_index]->serial_no, drvinfo->serial_no, 16); | 
 | 2069 | 		memcpy(h->drv[drv_index]->vendor, drvinfo->vendor, | 
 | 2070 | 			VENDOR_LEN + 1); | 
 | 2071 | 		memcpy(h->drv[drv_index]->model, drvinfo->model, MODEL_LEN + 1); | 
 | 2072 | 		memcpy(h->drv[drv_index]->rev, drvinfo->rev, REV_LEN + 1); | 
 | 2073 | 	} | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2074 |  | 
 | 2075 | 	++h->num_luns; | 
 | 2076 | 	disk = h->gendisk[drv_index]; | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2077 | 	set_capacity(disk, h->drv[drv_index]->nr_blocks); | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2078 |  | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2079 | 	/* If it's not disk 0 (drv_index != 0) | 
 | 2080 | 	 * or if it was disk 0, but there was previously | 
 | 2081 | 	 * no actual corresponding configured logical drive | 
 | 2082 | 	 * (raid_leve == -1) then we want to update the | 
 | 2083 | 	 * logical drive's information. | 
 | 2084 | 	 */ | 
| Stephen M. Cameron | 361e9b0 | 2009-09-17 13:47:29 -0500 | [diff] [blame] | 2085 | 	if (drv_index || first_time) { | 
 | 2086 | 		if (cciss_add_disk(h, disk, drv_index) != 0) { | 
 | 2087 | 			cciss_free_gendisk(h, drv_index); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2088 | 			cciss_free_drive_info(h, drv_index); | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2089 | 			dev_warn(&h->pdev->dev, "could not update disk %d\n", | 
 | 2090 | 				drv_index); | 
| Stephen M. Cameron | 361e9b0 | 2009-09-17 13:47:29 -0500 | [diff] [blame] | 2091 | 			--h->num_luns; | 
 | 2092 | 		} | 
 | 2093 | 	} | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2094 |  | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2095 | freeret: | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2096 | 	kfree(inq_buff); | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2097 | 	kfree(drvinfo); | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2098 | 	return; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2099 | mem_msg: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2100 | 	dev_err(&h->pdev->dev, "out of memory\n"); | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2101 | 	goto freeret; | 
 | 2102 | } | 
 | 2103 |  | 
 | 2104 | /* This function will find the first index of the controllers drive array | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2105 |  * that has a null drv pointer and allocate the drive info struct and | 
 | 2106 |  * will return that index   This is where new drives will be added. | 
 | 2107 |  * If the index to be returned is greater than the highest_lun index for | 
 | 2108 |  * the controller then highest_lun is set * to this new index. | 
 | 2109 |  * If there are no available indexes or if tha allocation fails, then -1 | 
 | 2110 |  * is returned.  * "controller_node" is used to know if this is a real | 
 | 2111 |  * logical drive, or just the controller node, which determines if this | 
 | 2112 |  * counts towards highest_lun. | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2113 |  */ | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2114 | static int cciss_alloc_drive_info(ctlr_info_t *h, int controller_node) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2115 | { | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2116 | 	int i; | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2117 | 	drive_info_struct *drv; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2118 |  | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2119 | 	/* Search for an empty slot for our drive info */ | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2120 | 	for (i = 0; i < CISS_MAX_LUN; i++) { | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2121 |  | 
 | 2122 | 		/* if not cxd0 case, and it's occupied, skip it. */ | 
 | 2123 | 		if (h->drv[i] && i != 0) | 
 | 2124 | 			continue; | 
 | 2125 | 		/* | 
 | 2126 | 		 * If it's cxd0 case, and drv is alloc'ed already, and a | 
 | 2127 | 		 * disk is configured there, skip it. | 
 | 2128 | 		 */ | 
 | 2129 | 		if (i == 0 && h->drv[i] && h->drv[i]->raid_level != -1) | 
 | 2130 | 			continue; | 
 | 2131 |  | 
 | 2132 | 		/* | 
 | 2133 | 		 * We've found an empty slot.  Update highest_lun | 
 | 2134 | 		 * provided this isn't just the fake cxd0 controller node. | 
 | 2135 | 		 */ | 
 | 2136 | 		if (i > h->highest_lun && !controller_node) | 
 | 2137 | 			h->highest_lun = i; | 
 | 2138 |  | 
 | 2139 | 		/* If adding a real disk at cxd0, and it's already alloc'ed */ | 
 | 2140 | 		if (i == 0 && h->drv[i] != NULL) | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2141 | 			return i; | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2142 |  | 
 | 2143 | 		/* | 
 | 2144 | 		 * Found an empty slot, not already alloc'ed.  Allocate it. | 
 | 2145 | 		 * Mark it with raid_level == -1, so we know it's new later on. | 
 | 2146 | 		 */ | 
 | 2147 | 		drv = kzalloc(sizeof(*drv), GFP_KERNEL); | 
 | 2148 | 		if (!drv) | 
 | 2149 | 			return -1; | 
 | 2150 | 		drv->raid_level = -1; /* so we know it's new */ | 
 | 2151 | 		h->drv[i] = drv; | 
 | 2152 | 		return i; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2153 | 	} | 
 | 2154 | 	return -1; | 
 | 2155 | } | 
 | 2156 |  | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2157 | static void cciss_free_drive_info(ctlr_info_t *h, int drv_index) | 
 | 2158 | { | 
 | 2159 | 	kfree(h->drv[drv_index]); | 
 | 2160 | 	h->drv[drv_index] = NULL; | 
 | 2161 | } | 
 | 2162 |  | 
| Stephen M. Cameron | 361e9b0 | 2009-09-17 13:47:29 -0500 | [diff] [blame] | 2163 | static void cciss_free_gendisk(ctlr_info_t *h, int drv_index) | 
 | 2164 | { | 
 | 2165 | 	put_disk(h->gendisk[drv_index]); | 
 | 2166 | 	h->gendisk[drv_index] = NULL; | 
 | 2167 | } | 
 | 2168 |  | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2169 | /* cciss_add_gendisk finds a free hba[]->drv structure | 
 | 2170 |  * and allocates a gendisk if needed, and sets the lunid | 
 | 2171 |  * in the drvinfo structure.   It returns the index into | 
 | 2172 |  * the ->drv[] array, or -1 if none are free. | 
 | 2173 |  * is_controller_node indicates whether highest_lun should | 
 | 2174 |  * count this disk, or if it's only being added to provide | 
 | 2175 |  * a means to talk to the controller in case no logical | 
 | 2176 |  * drives have yet been configured. | 
 | 2177 |  */ | 
| Stephen M. Cameron | 39ccf9a | 2009-09-17 13:48:00 -0500 | [diff] [blame] | 2178 | static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[], | 
 | 2179 | 	int controller_node) | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2180 | { | 
 | 2181 | 	int drv_index; | 
 | 2182 |  | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2183 | 	drv_index = cciss_alloc_drive_info(h, controller_node); | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2184 | 	if (drv_index == -1) | 
 | 2185 | 		return -1; | 
| Stephen M. Cameron | 8ce5196 | 2009-09-17 13:47:34 -0500 | [diff] [blame] | 2186 |  | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2187 | 	/*Check if the gendisk needs to be allocated */ | 
 | 2188 | 	if (!h->gendisk[drv_index]) { | 
 | 2189 | 		h->gendisk[drv_index] = | 
 | 2190 | 			alloc_disk(1 << NWD_SHIFT); | 
 | 2191 | 		if (!h->gendisk[drv_index]) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2192 | 			dev_err(&h->pdev->dev, | 
 | 2193 | 				"could not allocate a new disk %d\n", | 
 | 2194 | 				drv_index); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2195 | 			goto err_free_drive_info; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2196 | 		} | 
 | 2197 | 	} | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2198 | 	memcpy(h->drv[drv_index]->LunID, lunid, | 
 | 2199 | 		sizeof(h->drv[drv_index]->LunID)); | 
 | 2200 | 	if (cciss_create_ld_sysfs_entry(h, drv_index)) | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 2201 | 		goto err_free_disk; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2202 | 	/* Don't need to mark this busy because nobody */ | 
 | 2203 | 	/* else knows about this disk yet to contend */ | 
 | 2204 | 	/* for access to it. */ | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2205 | 	h->drv[drv_index]->busy_configuring = 0; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2206 | 	wmb(); | 
 | 2207 | 	return drv_index; | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 2208 |  | 
 | 2209 | err_free_disk: | 
| Stephen M. Cameron | 361e9b0 | 2009-09-17 13:47:29 -0500 | [diff] [blame] | 2210 | 	cciss_free_gendisk(h, drv_index); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2211 | err_free_drive_info: | 
 | 2212 | 	cciss_free_drive_info(h, drv_index); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 2213 | 	return -1; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2214 | } | 
 | 2215 |  | 
 | 2216 | /* This is for the special case of a controller which | 
 | 2217 |  * has no logical drives.  In this case, we still need | 
 | 2218 |  * to register a disk so the controller can be accessed | 
 | 2219 |  * by the Array Config Utility. | 
 | 2220 |  */ | 
 | 2221 | static void cciss_add_controller_node(ctlr_info_t *h) | 
 | 2222 | { | 
 | 2223 | 	struct gendisk *disk; | 
 | 2224 | 	int drv_index; | 
 | 2225 |  | 
 | 2226 | 	if (h->gendisk[0] != NULL) /* already did this? Then bail. */ | 
 | 2227 | 		return; | 
 | 2228 |  | 
| Stephen M. Cameron | 39ccf9a | 2009-09-17 13:48:00 -0500 | [diff] [blame] | 2229 | 	drv_index = cciss_add_gendisk(h, CTLR_LUNID, 1); | 
| Stephen M. Cameron | 361e9b0 | 2009-09-17 13:47:29 -0500 | [diff] [blame] | 2230 | 	if (drv_index == -1) | 
 | 2231 | 		goto error; | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2232 | 	h->drv[drv_index]->block_size = 512; | 
 | 2233 | 	h->drv[drv_index]->nr_blocks = 0; | 
 | 2234 | 	h->drv[drv_index]->heads = 0; | 
 | 2235 | 	h->drv[drv_index]->sectors = 0; | 
 | 2236 | 	h->drv[drv_index]->cylinders = 0; | 
 | 2237 | 	h->drv[drv_index]->raid_level = -1; | 
 | 2238 | 	memset(h->drv[drv_index]->serial_no, 0, 16); | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2239 | 	disk = h->gendisk[drv_index]; | 
| Stephen M. Cameron | 361e9b0 | 2009-09-17 13:47:29 -0500 | [diff] [blame] | 2240 | 	if (cciss_add_disk(h, disk, drv_index) == 0) | 
 | 2241 | 		return; | 
 | 2242 | 	cciss_free_gendisk(h, drv_index); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2243 | 	cciss_free_drive_info(h, drv_index); | 
| Stephen M. Cameron | 361e9b0 | 2009-09-17 13:47:29 -0500 | [diff] [blame] | 2244 | error: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2245 | 	dev_warn(&h->pdev->dev, "could not add disk 0.\n"); | 
| Stephen M. Cameron | 361e9b0 | 2009-09-17 13:47:29 -0500 | [diff] [blame] | 2246 | 	return; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2247 | } | 
 | 2248 |  | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2249 | /* This function will add and remove logical drives from the Logical | 
| Bjorn Helgaas | d14c4ab | 2006-06-25 05:49:04 -0700 | [diff] [blame] | 2250 |  * drive array of the controller and maintain persistency of ordering | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2251 |  * so that mount points are preserved until the next reboot.  This allows | 
 | 2252 |  * for the removal of logical drives in the middle of the drive array | 
 | 2253 |  * without a re-ordering of those drives. | 
 | 2254 |  * INPUT | 
 | 2255 |  * h		= The controller to perform the operations on | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2256 |  */ | 
| Stephen M. Cameron | 2d11d99 | 2009-09-17 13:47:44 -0500 | [diff] [blame] | 2257 | static int rebuild_lun_table(ctlr_info_t *h, int first_time, | 
 | 2258 | 	int via_ioctl) | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2259 | { | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2260 | 	int num_luns; | 
 | 2261 | 	ReportLunData_struct *ld_buff = NULL; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2262 | 	int return_code; | 
 | 2263 | 	int listlength = 0; | 
 | 2264 | 	int i; | 
 | 2265 | 	int drv_found; | 
 | 2266 | 	int drv_index = 0; | 
| Stephen M. Cameron | 39ccf9a | 2009-09-17 13:48:00 -0500 | [diff] [blame] | 2267 | 	unsigned char lunid[8] = CTLR_LUNID; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2268 | 	unsigned long flags; | 
 | 2269 |  | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2270 | 	if (!capable(CAP_SYS_RAWIO)) | 
 | 2271 | 		return -EPERM; | 
 | 2272 |  | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2273 | 	/* Set busy_configuring flag for this operation */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2274 | 	spin_lock_irqsave(&h->lock, flags); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2275 | 	if (h->busy_configuring) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2276 | 		spin_unlock_irqrestore(&h->lock, flags); | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2277 | 		return -EBUSY; | 
 | 2278 | 	} | 
 | 2279 | 	h->busy_configuring = 1; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2280 | 	spin_unlock_irqrestore(&h->lock, flags); | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2281 |  | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2282 | 	ld_buff = kzalloc(sizeof(ReportLunData_struct), GFP_KERNEL); | 
 | 2283 | 	if (ld_buff == NULL) | 
 | 2284 | 		goto mem_msg; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2285 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2286 | 	return_code = sendcmd_withirq(h, CISS_REPORT_LOG, ld_buff, | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 2287 | 				      sizeof(ReportLunData_struct), | 
 | 2288 | 				      0, CTLR_LUNID, TYPE_CMD); | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2289 |  | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2290 | 	if (return_code == IO_OK) | 
 | 2291 | 		listlength = be32_to_cpu(*(__be32 *) ld_buff->LUNListLength); | 
 | 2292 | 	else {	/* reading number of logical volumes failed */ | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2293 | 		dev_warn(&h->pdev->dev, | 
 | 2294 | 			"report logical volume command failed\n"); | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2295 | 		listlength = 0; | 
 | 2296 | 		goto freeret; | 
 | 2297 | 	} | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2298 |  | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2299 | 	num_luns = listlength / 8;	/* 8 bytes per entry */ | 
 | 2300 | 	if (num_luns > CISS_MAX_LUN) { | 
 | 2301 | 		num_luns = CISS_MAX_LUN; | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2302 | 		dev_warn(&h->pdev->dev, "more luns configured" | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2303 | 		       " on controller than can be handled by" | 
 | 2304 | 		       " this driver.\n"); | 
 | 2305 | 	} | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2306 |  | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2307 | 	if (num_luns == 0) | 
 | 2308 | 		cciss_add_controller_node(h); | 
 | 2309 |  | 
 | 2310 | 	/* Compare controller drive array to driver's drive array | 
 | 2311 | 	 * to see if any drives are missing on the controller due | 
 | 2312 | 	 * to action of Array Config Utility (user deletes drive) | 
 | 2313 | 	 * and deregister logical drives which have disappeared. | 
 | 2314 | 	 */ | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2315 | 	for (i = 0; i <= h->highest_lun; i++) { | 
 | 2316 | 		int j; | 
 | 2317 | 		drv_found = 0; | 
| Stephen M. Cameron | d8a0be6 | 2008-12-18 14:55:11 +0100 | [diff] [blame] | 2318 |  | 
 | 2319 | 		/* skip holes in the array from already deleted drives */ | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2320 | 		if (h->drv[i] == NULL) | 
| Stephen M. Cameron | d8a0be6 | 2008-12-18 14:55:11 +0100 | [diff] [blame] | 2321 | 			continue; | 
 | 2322 |  | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2323 | 		for (j = 0; j < num_luns; j++) { | 
| Stephen M. Cameron | 39ccf9a | 2009-09-17 13:48:00 -0500 | [diff] [blame] | 2324 | 			memcpy(lunid, &ld_buff->LUN[j][0], sizeof(lunid)); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2325 | 			if (memcmp(h->drv[i]->LunID, lunid, | 
| Stephen M. Cameron | 39ccf9a | 2009-09-17 13:48:00 -0500 | [diff] [blame] | 2326 | 				sizeof(lunid)) == 0) { | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2327 | 				drv_found = 1; | 
 | 2328 | 				break; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2329 | 			} | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2330 | 		} | 
 | 2331 | 		if (!drv_found) { | 
 | 2332 | 			/* Deregister it from the OS, it's gone. */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2333 | 			spin_lock_irqsave(&h->lock, flags); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2334 | 			h->drv[i]->busy_configuring = 1; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2335 | 			spin_unlock_irqrestore(&h->lock, flags); | 
| Stephen M. Cameron | 2d11d99 | 2009-09-17 13:47:44 -0500 | [diff] [blame] | 2336 | 			return_code = deregister_disk(h, i, 1, via_ioctl); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2337 | 			if (h->drv[i] != NULL) | 
 | 2338 | 				h->drv[i]->busy_configuring = 0; | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2339 | 		} | 
 | 2340 | 	} | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2341 |  | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2342 | 	/* Compare controller drive array to driver's drive array. | 
 | 2343 | 	 * Check for updates in the drive information and any new drives | 
 | 2344 | 	 * on the controller due to ACU adding logical drives, or changing | 
 | 2345 | 	 * a logical drive's size, etc.  Reregister any new/changed drives | 
 | 2346 | 	 */ | 
 | 2347 | 	for (i = 0; i < num_luns; i++) { | 
 | 2348 | 		int j; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2349 |  | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2350 | 		drv_found = 0; | 
 | 2351 |  | 
| Stephen M. Cameron | 39ccf9a | 2009-09-17 13:48:00 -0500 | [diff] [blame] | 2352 | 		memcpy(lunid, &ld_buff->LUN[i][0], sizeof(lunid)); | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2353 | 		/* Find if the LUN is already in the drive array | 
 | 2354 | 		 * of the driver.  If so then update its info | 
 | 2355 | 		 * if not in use.  If it does not exist then find | 
 | 2356 | 		 * the first free index and add it. | 
 | 2357 | 		 */ | 
 | 2358 | 		for (j = 0; j <= h->highest_lun; j++) { | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2359 | 			if (h->drv[j] != NULL && | 
 | 2360 | 				memcmp(h->drv[j]->LunID, lunid, | 
 | 2361 | 					sizeof(h->drv[j]->LunID)) == 0) { | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2362 | 				drv_index = j; | 
 | 2363 | 				drv_found = 1; | 
 | 2364 | 				break; | 
 | 2365 | 			} | 
 | 2366 | 		} | 
 | 2367 |  | 
 | 2368 | 		/* check if the drive was found already in the array */ | 
 | 2369 | 		if (!drv_found) { | 
| Mike Miller | eece695 | 2008-08-04 11:54:53 +0200 | [diff] [blame] | 2370 | 			drv_index = cciss_add_gendisk(h, lunid, 0); | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2371 | 			if (drv_index == -1) | 
 | 2372 | 				goto freeret; | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2373 | 		} | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2374 | 		cciss_update_drive_info(h, drv_index, first_time, via_ioctl); | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2375 | 	}		/* end for */ | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2376 |  | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2377 | freeret: | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2378 | 	kfree(ld_buff); | 
 | 2379 | 	h->busy_configuring = 0; | 
 | 2380 | 	/* We return -1 here to tell the ACU that we have registered/updated | 
 | 2381 | 	 * all of the drives that we can and to keep it from calling us | 
 | 2382 | 	 * additional times. | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2383 | 	 */ | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2384 | 	return -1; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 2385 | mem_msg: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2386 | 	dev_err(&h->pdev->dev, "out of memory\n"); | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2387 | 	h->busy_configuring = 0; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2388 | 	goto freeret; | 
 | 2389 | } | 
 | 2390 |  | 
| Stephen M. Cameron | 9ddb27b | 2009-09-17 13:47:39 -0500 | [diff] [blame] | 2391 | static void cciss_clear_drive_info(drive_info_struct *drive_info) | 
 | 2392 | { | 
 | 2393 | 	/* zero out the disk size info */ | 
 | 2394 | 	drive_info->nr_blocks = 0; | 
 | 2395 | 	drive_info->block_size = 0; | 
 | 2396 | 	drive_info->heads = 0; | 
 | 2397 | 	drive_info->sectors = 0; | 
 | 2398 | 	drive_info->cylinders = 0; | 
 | 2399 | 	drive_info->raid_level = -1; | 
 | 2400 | 	memset(drive_info->serial_no, 0, sizeof(drive_info->serial_no)); | 
 | 2401 | 	memset(drive_info->model, 0, sizeof(drive_info->model)); | 
 | 2402 | 	memset(drive_info->rev, 0, sizeof(drive_info->rev)); | 
 | 2403 | 	memset(drive_info->vendor, 0, sizeof(drive_info->vendor)); | 
 | 2404 | 	/* | 
 | 2405 | 	 * don't clear the LUNID though, we need to remember which | 
 | 2406 | 	 * one this one is. | 
 | 2407 | 	 */ | 
 | 2408 | } | 
 | 2409 |  | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2410 | /* This function will deregister the disk and it's queue from the | 
 | 2411 |  * kernel.  It must be called with the controller lock held and the | 
 | 2412 |  * drv structures busy_configuring flag set.  It's parameters are: | 
 | 2413 |  * | 
 | 2414 |  * disk = This is the disk to be deregistered | 
 | 2415 |  * drv  = This is the drive_info_struct associated with the disk to be | 
 | 2416 |  *        deregistered.  It contains information about the disk used | 
 | 2417 |  *        by the driver. | 
 | 2418 |  * clear_all = This flag determines whether or not the disk information | 
 | 2419 |  *             is going to be completely cleared out and the highest_lun | 
 | 2420 |  *             reset.  Sometimes we want to clear out information about | 
| Bjorn Helgaas | d14c4ab | 2006-06-25 05:49:04 -0700 | [diff] [blame] | 2421 |  *             the disk in preparation for re-adding it.  In this case | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2422 |  *             the highest_lun should be left unchanged and the LunID | 
 | 2423 |  *             should not be cleared. | 
| Stephen M. Cameron | 2d11d99 | 2009-09-17 13:47:44 -0500 | [diff] [blame] | 2424 |  * via_ioctl | 
 | 2425 |  *    This indicates whether we've reached this path via ioctl. | 
 | 2426 |  *    This affects the maximum usage count allowed for c0d0 to be messed with. | 
 | 2427 |  *    If this path is reached via ioctl(), then the max_usage_count will | 
 | 2428 |  *    be 1, as the process calling ioctl() has got to have the device open. | 
 | 2429 |  *    If we get here via sysfs, then the max usage count will be zero. | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2430 | */ | 
| Stephen M. Cameron | a0ea862 | 2008-12-18 14:55:51 +0100 | [diff] [blame] | 2431 | static int deregister_disk(ctlr_info_t *h, int drv_index, | 
| Stephen M. Cameron | 2d11d99 | 2009-09-17 13:47:44 -0500 | [diff] [blame] | 2432 | 			   int clear_all, int via_ioctl) | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2433 | { | 
| Mike Miller | 799202c | 2006-12-06 20:35:12 -0800 | [diff] [blame] | 2434 | 	int i; | 
| Stephen M. Cameron | a0ea862 | 2008-12-18 14:55:51 +0100 | [diff] [blame] | 2435 | 	struct gendisk *disk; | 
 | 2436 | 	drive_info_struct *drv; | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2437 | 	int recalculate_highest_lun; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2438 |  | 
 | 2439 | 	if (!capable(CAP_SYS_RAWIO)) | 
 | 2440 | 		return -EPERM; | 
 | 2441 |  | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2442 | 	drv = h->drv[drv_index]; | 
| Stephen M. Cameron | a0ea862 | 2008-12-18 14:55:51 +0100 | [diff] [blame] | 2443 | 	disk = h->gendisk[drv_index]; | 
 | 2444 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2445 | 	/* make sure logical volume is NOT is use */ | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2446 | 	if (clear_all || (h->gendisk[0] == disk)) { | 
| Stephen M. Cameron | 2d11d99 | 2009-09-17 13:47:44 -0500 | [diff] [blame] | 2447 | 		if (drv->usage_count > via_ioctl) | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2448 | 			return -EBUSY; | 
 | 2449 | 	} else if (drv->usage_count > 0) | 
 | 2450 | 		return -EBUSY; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2451 |  | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2452 | 	recalculate_highest_lun = (drv == h->drv[h->highest_lun]); | 
 | 2453 |  | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2454 | 	/* invalidate the devices and deregister the disk.  If it is disk | 
 | 2455 | 	 * zero do not deregister it but just zero out it's values.  This | 
 | 2456 | 	 * allows us to delete disk zero but keep the controller registered. | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2457 | 	 */ | 
 | 2458 | 	if (h->gendisk[0] != disk) { | 
| Adrian Bunk | 5a9df73 | 2007-10-16 23:29:26 -0700 | [diff] [blame] | 2459 | 		struct request_queue *q = disk->queue; | 
| Stephen M. Cameron | 097d026 | 2009-09-17 13:47:19 -0500 | [diff] [blame] | 2460 | 		if (disk->flags & GENHD_FL_UP) { | 
| Stephen M. Cameron | 8ce5196 | 2009-09-17 13:47:34 -0500 | [diff] [blame] | 2461 | 			cciss_destroy_ld_sysfs_entry(h, drv_index, 0); | 
| Adrian Bunk | 5a9df73 | 2007-10-16 23:29:26 -0700 | [diff] [blame] | 2462 | 			del_gendisk(disk); | 
| Adrian Bunk | 5a9df73 | 2007-10-16 23:29:26 -0700 | [diff] [blame] | 2463 | 		} | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2464 | 		if (q) | 
| Adrian Bunk | 5a9df73 | 2007-10-16 23:29:26 -0700 | [diff] [blame] | 2465 | 			blk_cleanup_queue(q); | 
| Adrian Bunk | 5a9df73 | 2007-10-16 23:29:26 -0700 | [diff] [blame] | 2466 | 		/* If clear_all is set then we are deleting the logical | 
 | 2467 | 		 * drive, not just refreshing its info.  For drives | 
 | 2468 | 		 * other than disk 0 we will call put_disk.  We do not | 
 | 2469 | 		 * do this for disk 0 as we need it to be able to | 
 | 2470 | 		 * configure the controller. | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2471 | 		 */ | 
| Adrian Bunk | 5a9df73 | 2007-10-16 23:29:26 -0700 | [diff] [blame] | 2472 | 		if (clear_all){ | 
 | 2473 | 			/* This isn't pretty, but we need to find the | 
 | 2474 | 			 * disk in our array and NULL our the pointer. | 
 | 2475 | 			 * This is so that we will call alloc_disk if | 
 | 2476 | 			 * this index is used again later. | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2477 | 			 */ | 
| Adrian Bunk | 5a9df73 | 2007-10-16 23:29:26 -0700 | [diff] [blame] | 2478 | 			for (i=0; i < CISS_MAX_LUN; i++){ | 
| Mike Miller | a72da29 | 2008-08-04 11:54:51 +0200 | [diff] [blame] | 2479 | 				if (h->gendisk[i] == disk) { | 
| Adrian Bunk | 5a9df73 | 2007-10-16 23:29:26 -0700 | [diff] [blame] | 2480 | 					h->gendisk[i] = NULL; | 
 | 2481 | 					break; | 
| Mike Miller | 799202c | 2006-12-06 20:35:12 -0800 | [diff] [blame] | 2482 | 				} | 
| Mike Miller | 799202c | 2006-12-06 20:35:12 -0800 | [diff] [blame] | 2483 | 			} | 
| Adrian Bunk | 5a9df73 | 2007-10-16 23:29:26 -0700 | [diff] [blame] | 2484 | 			put_disk(disk); | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2485 | 		} | 
| Mike Miller | 799202c | 2006-12-06 20:35:12 -0800 | [diff] [blame] | 2486 | 	} else { | 
 | 2487 | 		set_capacity(disk, 0); | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2488 | 		cciss_clear_drive_info(drv); | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2489 | 	} | 
 | 2490 |  | 
 | 2491 | 	--h->num_luns; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2492 |  | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2493 | 	/* if it was the last disk, find the new hightest lun */ | 
 | 2494 | 	if (clear_all && recalculate_highest_lun) { | 
| Bill Pemberton | c2d45b4 | 2010-04-30 09:34:32 -0400 | [diff] [blame] | 2495 | 		int newhighest = -1; | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2496 | 		for (i = 0; i <= h->highest_lun; i++) { | 
 | 2497 | 			/* if the disk has size > 0, it is available */ | 
 | 2498 | 			if (h->drv[i] && h->drv[i]->heads) | 
 | 2499 | 				newhighest = i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2500 | 		} | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2501 | 		h->highest_lun = newhighest; | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2502 | 	} | 
| Bjorn Helgaas | e2019b5 | 2006-06-25 05:49:05 -0700 | [diff] [blame] | 2503 | 	return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2504 | } | 
| Mike Miller | ddd4744 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 2505 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2506 | static int fill_cmd(ctlr_info_t *h, CommandList_struct *c, __u8 cmd, void *buff, | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 2507 | 		size_t size, __u8 page_code, unsigned char *scsi3addr, | 
 | 2508 | 		int cmd_type) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2509 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2510 | 	u64bit buff_dma_handle; | 
 | 2511 | 	int status = IO_OK; | 
 | 2512 |  | 
 | 2513 | 	c->cmd_type = CMD_IOCTL_PEND; | 
 | 2514 | 	c->Header.ReplyQueue = 0; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2515 | 	if (buff != NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2516 | 		c->Header.SGList = 1; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2517 | 		c->Header.SGTotal = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2518 | 	} else { | 
 | 2519 | 		c->Header.SGList = 0; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2520 | 		c->Header.SGTotal = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2521 | 	} | 
 | 2522 | 	c->Header.Tag.lower = c->busaddr; | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 2523 | 	memcpy(c->Header.LUN.LunAddrBytes, scsi3addr, 8); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2524 |  | 
 | 2525 | 	c->Request.Type.Type = cmd_type; | 
 | 2526 | 	if (cmd_type == TYPE_CMD) { | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2527 | 		switch (cmd) { | 
 | 2528 | 		case CISS_INQUIRY: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2529 | 			/* are we trying to read a vital product page */ | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2530 | 			if (page_code != 0) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2531 | 				c->Request.CDB[1] = 0x01; | 
 | 2532 | 				c->Request.CDB[2] = page_code; | 
 | 2533 | 			} | 
 | 2534 | 			c->Request.CDBLen = 6; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2535 | 			c->Request.Type.Attribute = ATTR_SIMPLE; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2536 | 			c->Request.Type.Direction = XFER_READ; | 
 | 2537 | 			c->Request.Timeout = 0; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2538 | 			c->Request.CDB[0] = CISS_INQUIRY; | 
 | 2539 | 			c->Request.CDB[4] = size & 0xFF; | 
 | 2540 | 			break; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2541 | 		case CISS_REPORT_LOG: | 
 | 2542 | 		case CISS_REPORT_PHYS: | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2543 | 			/* Talking to controller so It's a physical command | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2544 | 			   mode = 00 target = 0.  Nothing to write. | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2545 | 			 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2546 | 			c->Request.CDBLen = 12; | 
 | 2547 | 			c->Request.Type.Attribute = ATTR_SIMPLE; | 
 | 2548 | 			c->Request.Type.Direction = XFER_READ; | 
 | 2549 | 			c->Request.Timeout = 0; | 
 | 2550 | 			c->Request.CDB[0] = cmd; | 
| dann frazier | b028461 | 2010-02-17 16:53:31 -0700 | [diff] [blame] | 2551 | 			c->Request.CDB[6] = (size >> 24) & 0xFF; /* MSB */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2552 | 			c->Request.CDB[7] = (size >> 16) & 0xFF; | 
 | 2553 | 			c->Request.CDB[8] = (size >> 8) & 0xFF; | 
 | 2554 | 			c->Request.CDB[9] = size & 0xFF; | 
 | 2555 | 			break; | 
 | 2556 |  | 
 | 2557 | 		case CCISS_READ_CAPACITY: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2558 | 			c->Request.CDBLen = 10; | 
 | 2559 | 			c->Request.Type.Attribute = ATTR_SIMPLE; | 
 | 2560 | 			c->Request.Type.Direction = XFER_READ; | 
 | 2561 | 			c->Request.Timeout = 0; | 
 | 2562 | 			c->Request.CDB[0] = cmd; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2563 | 			break; | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2564 | 		case CCISS_READ_CAPACITY_16: | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2565 | 			c->Request.CDBLen = 16; | 
 | 2566 | 			c->Request.Type.Attribute = ATTR_SIMPLE; | 
 | 2567 | 			c->Request.Type.Direction = XFER_READ; | 
 | 2568 | 			c->Request.Timeout = 0; | 
 | 2569 | 			c->Request.CDB[0] = cmd; | 
 | 2570 | 			c->Request.CDB[1] = 0x10; | 
 | 2571 | 			c->Request.CDB[10] = (size >> 24) & 0xFF; | 
 | 2572 | 			c->Request.CDB[11] = (size >> 16) & 0xFF; | 
 | 2573 | 			c->Request.CDB[12] = (size >> 8) & 0xFF; | 
 | 2574 | 			c->Request.CDB[13] = size & 0xFF; | 
 | 2575 | 			c->Request.Timeout = 0; | 
 | 2576 | 			c->Request.CDB[0] = cmd; | 
 | 2577 | 			break; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2578 | 		case CCISS_CACHE_FLUSH: | 
 | 2579 | 			c->Request.CDBLen = 12; | 
 | 2580 | 			c->Request.Type.Attribute = ATTR_SIMPLE; | 
 | 2581 | 			c->Request.Type.Direction = XFER_WRITE; | 
 | 2582 | 			c->Request.Timeout = 0; | 
 | 2583 | 			c->Request.CDB[0] = BMIC_WRITE; | 
 | 2584 | 			c->Request.CDB[6] = BMIC_CACHE_FLUSH; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2585 | 			break; | 
| Stephen M. Cameron | 88f627a | 2009-06-02 14:48:11 +0200 | [diff] [blame] | 2586 | 		case TEST_UNIT_READY: | 
| Stephen M. Cameron | 88f627a | 2009-06-02 14:48:11 +0200 | [diff] [blame] | 2587 | 			c->Request.CDBLen = 6; | 
 | 2588 | 			c->Request.Type.Attribute = ATTR_SIMPLE; | 
 | 2589 | 			c->Request.Type.Direction = XFER_NONE; | 
 | 2590 | 			c->Request.Timeout = 0; | 
 | 2591 | 			break; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2592 | 		default: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2593 | 			dev_warn(&h->pdev->dev, "Unknown Command 0x%c\n", cmd); | 
| Bjorn Helgaas | e2019b5 | 2006-06-25 05:49:05 -0700 | [diff] [blame] | 2594 | 			return IO_ERROR; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2595 | 		} | 
 | 2596 | 	} else if (cmd_type == TYPE_MSG) { | 
 | 2597 | 		switch (cmd) { | 
| Stephen M. Cameron | 8f71bb8 | 2011-05-03 14:53:26 -0500 | [diff] [blame] | 2598 | 		case CCISS_ABORT_MSG: | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 2599 | 			c->Request.CDBLen = 12; | 
 | 2600 | 			c->Request.Type.Attribute = ATTR_SIMPLE; | 
 | 2601 | 			c->Request.Type.Direction = XFER_WRITE; | 
 | 2602 | 			c->Request.Timeout = 0; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2603 | 			c->Request.CDB[0] = cmd;	/* abort */ | 
 | 2604 | 			c->Request.CDB[1] = 0;	/* abort a command */ | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 2605 | 			/* buff contains the tag of the command to abort */ | 
 | 2606 | 			memcpy(&c->Request.CDB[4], buff, 8); | 
 | 2607 | 			break; | 
| Stephen M. Cameron | 8f71bb8 | 2011-05-03 14:53:26 -0500 | [diff] [blame] | 2608 | 		case CCISS_RESET_MSG: | 
| Stephen M. Cameron | 88f627a | 2009-06-02 14:48:11 +0200 | [diff] [blame] | 2609 | 			c->Request.CDBLen = 16; | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 2610 | 			c->Request.Type.Attribute = ATTR_SIMPLE; | 
| Stephen M. Cameron | 88f627a | 2009-06-02 14:48:11 +0200 | [diff] [blame] | 2611 | 			c->Request.Type.Direction = XFER_NONE; | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 2612 | 			c->Request.Timeout = 0; | 
 | 2613 | 			memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB)); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2614 | 			c->Request.CDB[0] = cmd;	/* reset */ | 
| Stephen M. Cameron | 8f71bb8 | 2011-05-03 14:53:26 -0500 | [diff] [blame] | 2615 | 			c->Request.CDB[1] = CCISS_RESET_TYPE_TARGET; | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2616 | 			break; | 
| Stephen M. Cameron | 8f71bb8 | 2011-05-03 14:53:26 -0500 | [diff] [blame] | 2617 | 		case CCISS_NOOP_MSG: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2618 | 			c->Request.CDBLen = 1; | 
 | 2619 | 			c->Request.Type.Attribute = ATTR_SIMPLE; | 
 | 2620 | 			c->Request.Type.Direction = XFER_WRITE; | 
 | 2621 | 			c->Request.Timeout = 0; | 
 | 2622 | 			c->Request.CDB[0] = cmd; | 
 | 2623 | 			break; | 
 | 2624 | 		default: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2625 | 			dev_warn(&h->pdev->dev, | 
 | 2626 | 				"unknown message type %d\n", cmd); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2627 | 			return IO_ERROR; | 
 | 2628 | 		} | 
 | 2629 | 	} else { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2630 | 		dev_warn(&h->pdev->dev, "unknown command type %d\n", cmd_type); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2631 | 		return IO_ERROR; | 
 | 2632 | 	} | 
 | 2633 | 	/* Fill in the scatter gather information */ | 
 | 2634 | 	if (size > 0) { | 
 | 2635 | 		buff_dma_handle.val = (__u64) pci_map_single(h->pdev, | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2636 | 							     buff, size, | 
 | 2637 | 							     PCI_DMA_BIDIRECTIONAL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2638 | 		c->SG[0].Addr.lower = buff_dma_handle.val32.lower; | 
 | 2639 | 		c->SG[0].Addr.upper = buff_dma_handle.val32.upper; | 
 | 2640 | 		c->SG[0].Len = size; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2641 | 		c->SG[0].Ext = 0;	/* we are not chaining */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2642 | 	} | 
 | 2643 | 	return status; | 
 | 2644 | } | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2645 |  | 
| Jens Axboe | edc83d47 | 2011-05-06 08:27:00 -0600 | [diff] [blame] | 2646 | static int __devinit cciss_send_reset(ctlr_info_t *h, unsigned char *scsi3addr, | 
 | 2647 | 	u8 reset_type) | 
 | 2648 | { | 
 | 2649 | 	CommandList_struct *c; | 
 | 2650 | 	int return_status; | 
 | 2651 |  | 
 | 2652 | 	c = cmd_alloc(h); | 
 | 2653 | 	if (!c) | 
 | 2654 | 		return -ENOMEM; | 
 | 2655 | 	return_status = fill_cmd(h, c, CCISS_RESET_MSG, NULL, 0, 0, | 
 | 2656 | 		CTLR_LUNID, TYPE_MSG); | 
 | 2657 | 	c->Request.CDB[1] = reset_type; /* fill_cmd defaults to target reset */ | 
 | 2658 | 	if (return_status != IO_OK) { | 
 | 2659 | 		cmd_special_free(h, c); | 
 | 2660 | 		return return_status; | 
 | 2661 | 	} | 
 | 2662 | 	c->waiting = NULL; | 
 | 2663 | 	enqueue_cmd_and_start_io(h, c); | 
 | 2664 | 	/* Don't wait for completion, the reset won't complete.  Don't free | 
 | 2665 | 	 * the command either.  This is the last command we will send before | 
 | 2666 | 	 * re-initializing everything, so it doesn't matter and won't leak. | 
 | 2667 | 	 */ | 
 | 2668 | 	return 0; | 
 | 2669 | } | 
 | 2670 |  | 
| scameron@beardog.cca.cpqcorp.net | 3c2ab40 | 2009-06-08 16:04:35 -0500 | [diff] [blame] | 2671 | static int check_target_status(ctlr_info_t *h, CommandList_struct *c) | 
 | 2672 | { | 
 | 2673 | 	switch (c->err_info->ScsiStatus) { | 
 | 2674 | 	case SAM_STAT_GOOD: | 
 | 2675 | 		return IO_OK; | 
 | 2676 | 	case SAM_STAT_CHECK_CONDITION: | 
 | 2677 | 		switch (0xf & c->err_info->SenseInfo[2]) { | 
 | 2678 | 		case 0: return IO_OK; /* no sense */ | 
 | 2679 | 		case 1: return IO_OK; /* recovered error */ | 
 | 2680 | 		default: | 
| Stephen M. Cameron | c08fac6 | 2009-11-12 12:49:25 -0600 | [diff] [blame] | 2681 | 			if (check_for_unit_attention(h, c)) | 
 | 2682 | 				return IO_NEEDS_RETRY; | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2683 | 			dev_warn(&h->pdev->dev, "cmd 0x%02x " | 
| scameron@beardog.cca.cpqcorp.net | 3c2ab40 | 2009-06-08 16:04:35 -0500 | [diff] [blame] | 2684 | 				"check condition, sense key = 0x%02x\n", | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2685 | 				c->Request.CDB[0], c->err_info->SenseInfo[2]); | 
| scameron@beardog.cca.cpqcorp.net | 3c2ab40 | 2009-06-08 16:04:35 -0500 | [diff] [blame] | 2686 | 		} | 
 | 2687 | 		break; | 
 | 2688 | 	default: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2689 | 		dev_warn(&h->pdev->dev, "cmd 0x%02x" | 
 | 2690 | 			"scsi status = 0x%02x\n", | 
| scameron@beardog.cca.cpqcorp.net | 3c2ab40 | 2009-06-08 16:04:35 -0500 | [diff] [blame] | 2691 | 			c->Request.CDB[0], c->err_info->ScsiStatus); | 
 | 2692 | 		break; | 
 | 2693 | 	} | 
 | 2694 | 	return IO_ERROR; | 
 | 2695 | } | 
 | 2696 |  | 
| scameron@beardog.cca.cpqcorp.net | 789a424 | 2009-06-08 16:05:56 -0500 | [diff] [blame] | 2697 | static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c) | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2698 | { | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2699 | 	int return_status = IO_OK; | 
 | 2700 |  | 
| scameron@beardog.cca.cpqcorp.net | 789a424 | 2009-06-08 16:05:56 -0500 | [diff] [blame] | 2701 | 	if (c->err_info->CommandStatus == CMD_SUCCESS) | 
 | 2702 | 		return IO_OK; | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2703 |  | 
 | 2704 | 	switch (c->err_info->CommandStatus) { | 
 | 2705 | 	case CMD_TARGET_STATUS: | 
| scameron@beardog.cca.cpqcorp.net | 3c2ab40 | 2009-06-08 16:04:35 -0500 | [diff] [blame] | 2706 | 		return_status = check_target_status(h, c); | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2707 | 		break; | 
 | 2708 | 	case CMD_DATA_UNDERRUN: | 
 | 2709 | 	case CMD_DATA_OVERRUN: | 
 | 2710 | 		/* expected for inquiry and report lun commands */ | 
 | 2711 | 		break; | 
 | 2712 | 	case CMD_INVALID: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2713 | 		dev_warn(&h->pdev->dev, "cmd 0x%02x is " | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2714 | 		       "reported invalid\n", c->Request.CDB[0]); | 
 | 2715 | 		return_status = IO_ERROR; | 
 | 2716 | 		break; | 
 | 2717 | 	case CMD_PROTOCOL_ERR: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2718 | 		dev_warn(&h->pdev->dev, "cmd 0x%02x has " | 
 | 2719 | 		       "protocol error\n", c->Request.CDB[0]); | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2720 | 		return_status = IO_ERROR; | 
 | 2721 | 		break; | 
 | 2722 | 	case CMD_HARDWARE_ERR: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2723 | 		dev_warn(&h->pdev->dev, "cmd 0x%02x had " | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2724 | 		       " hardware error\n", c->Request.CDB[0]); | 
 | 2725 | 		return_status = IO_ERROR; | 
 | 2726 | 		break; | 
 | 2727 | 	case CMD_CONNECTION_LOST: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2728 | 		dev_warn(&h->pdev->dev, "cmd 0x%02x had " | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2729 | 		       "connection lost\n", c->Request.CDB[0]); | 
 | 2730 | 		return_status = IO_ERROR; | 
 | 2731 | 		break; | 
 | 2732 | 	case CMD_ABORTED: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2733 | 		dev_warn(&h->pdev->dev, "cmd 0x%02x was " | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2734 | 		       "aborted\n", c->Request.CDB[0]); | 
 | 2735 | 		return_status = IO_ERROR; | 
 | 2736 | 		break; | 
 | 2737 | 	case CMD_ABORT_FAILED: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2738 | 		dev_warn(&h->pdev->dev, "cmd 0x%02x reports " | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2739 | 		       "abort failed\n", c->Request.CDB[0]); | 
 | 2740 | 		return_status = IO_ERROR; | 
 | 2741 | 		break; | 
 | 2742 | 	case CMD_UNSOLICITED_ABORT: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2743 | 		dev_warn(&h->pdev->dev, "unsolicited abort 0x%02x\n", | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2744 | 			c->Request.CDB[0]); | 
| scameron@beardog.cca.cpqcorp.net | 789a424 | 2009-06-08 16:05:56 -0500 | [diff] [blame] | 2745 | 		return_status = IO_NEEDS_RETRY; | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2746 | 		break; | 
| Stephen M. Cameron | 6d9a4f9 | 2011-03-12 10:02:30 +0100 | [diff] [blame] | 2747 | 	case CMD_UNABORTABLE: | 
 | 2748 | 		dev_warn(&h->pdev->dev, "cmd unabortable\n"); | 
 | 2749 | 		return_status = IO_ERROR; | 
 | 2750 | 		break; | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2751 | 	default: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2752 | 		dev_warn(&h->pdev->dev, "cmd 0x%02x returned " | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2753 | 		       "unknown status %x\n", c->Request.CDB[0], | 
 | 2754 | 		       c->err_info->CommandStatus); | 
 | 2755 | 		return_status = IO_ERROR; | 
 | 2756 | 	} | 
| scameron@beardog.cca.cpqcorp.net | 789a424 | 2009-06-08 16:05:56 -0500 | [diff] [blame] | 2757 | 	return return_status; | 
 | 2758 | } | 
 | 2759 |  | 
 | 2760 | static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c, | 
 | 2761 | 	int attempt_retry) | 
 | 2762 | { | 
 | 2763 | 	DECLARE_COMPLETION_ONSTACK(wait); | 
 | 2764 | 	u64bit buff_dma_handle; | 
| scameron@beardog.cca.cpqcorp.net | 789a424 | 2009-06-08 16:05:56 -0500 | [diff] [blame] | 2765 | 	int return_status = IO_OK; | 
 | 2766 |  | 
 | 2767 | resend_cmd2: | 
 | 2768 | 	c->waiting = &wait; | 
| Mike Miller | 664a717 | 2010-06-02 12:57:58 -0700 | [diff] [blame] | 2769 | 	enqueue_cmd_and_start_io(h, c); | 
| scameron@beardog.cca.cpqcorp.net | 789a424 | 2009-06-08 16:05:56 -0500 | [diff] [blame] | 2770 |  | 
 | 2771 | 	wait_for_completion(&wait); | 
 | 2772 |  | 
 | 2773 | 	if (c->err_info->CommandStatus == 0 || !attempt_retry) | 
 | 2774 | 		goto command_done; | 
 | 2775 |  | 
 | 2776 | 	return_status = process_sendcmd_error(h, c); | 
 | 2777 |  | 
 | 2778 | 	if (return_status == IO_NEEDS_RETRY && | 
 | 2779 | 		c->retry_count < MAX_CMD_RETRIES) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2780 | 		dev_warn(&h->pdev->dev, "retrying 0x%02x\n", | 
| scameron@beardog.cca.cpqcorp.net | 789a424 | 2009-06-08 16:05:56 -0500 | [diff] [blame] | 2781 | 			c->Request.CDB[0]); | 
 | 2782 | 		c->retry_count++; | 
 | 2783 | 		/* erase the old error information */ | 
 | 2784 | 		memset(c->err_info, 0, sizeof(ErrorInfo_struct)); | 
 | 2785 | 		return_status = IO_OK; | 
 | 2786 | 		INIT_COMPLETION(wait); | 
 | 2787 | 		goto resend_cmd2; | 
 | 2788 | 	} | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2789 |  | 
 | 2790 | command_done: | 
 | 2791 | 	/* unlock the buffers from DMA */ | 
 | 2792 | 	buff_dma_handle.val32.lower = c->SG[0].Addr.lower; | 
 | 2793 | 	buff_dma_handle.val32.upper = c->SG[0].Addr.upper; | 
 | 2794 | 	pci_unmap_single(h->pdev, (dma_addr_t) buff_dma_handle.val, | 
 | 2795 | 			 c->SG[0].Len, PCI_DMA_BIDIRECTIONAL); | 
 | 2796 | 	return return_status; | 
 | 2797 | } | 
 | 2798 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2799 | static int sendcmd_withirq(ctlr_info_t *h, __u8 cmd, void *buff, size_t size, | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 2800 | 			   __u8 page_code, unsigned char scsi3addr[], | 
 | 2801 | 			int cmd_type) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2802 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2803 | 	CommandList_struct *c; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2804 | 	int return_status; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2805 |  | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 2806 | 	c = cmd_special_alloc(h); | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2807 | 	if (!c) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2808 | 		return -ENOMEM; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2809 | 	return_status = fill_cmd(h, c, cmd, buff, size, page_code, | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 2810 | 		scsi3addr, cmd_type); | 
| scameron@beardog.cca.cpqcorp.net | 5390cfc | 2009-06-08 16:01:11 -0500 | [diff] [blame] | 2811 | 	if (return_status == IO_OK) | 
| scameron@beardog.cca.cpqcorp.net | 789a424 | 2009-06-08 16:05:56 -0500 | [diff] [blame] | 2812 | 		return_status = sendcmd_withirq_core(h, c, 1); | 
 | 2813 |  | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 2814 | 	cmd_special_free(h, c); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2815 | 	return return_status; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2816 | } | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2817 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2818 | static void cciss_geometry_inquiry(ctlr_info_t *h, int logvol, | 
| Stephen M. Cameron | 7b838bd | 2009-11-12 12:49:30 -0600 | [diff] [blame] | 2819 | 				   sector_t total_size, | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2820 | 				   unsigned int block_size, | 
 | 2821 | 				   InquiryData_struct *inq_buff, | 
 | 2822 | 				   drive_info_struct *drv) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2823 | { | 
 | 2824 | 	int return_code; | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2825 | 	unsigned long t; | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 2826 | 	unsigned char scsi3addr[8]; | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2827 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2828 | 	memset(inq_buff, 0, sizeof(InquiryData_struct)); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2829 | 	log_unit_to_scsi3addr(h, scsi3addr, logvol); | 
 | 2830 | 	return_code = sendcmd_withirq(h, CISS_INQUIRY, inq_buff, | 
| Stephen M. Cameron | 7b838bd | 2009-11-12 12:49:30 -0600 | [diff] [blame] | 2831 | 			sizeof(*inq_buff), 0xC1, scsi3addr, TYPE_CMD); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2832 | 	if (return_code == IO_OK) { | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2833 | 		if (inq_buff->data_byte[8] == 0xFF) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2834 | 			dev_warn(&h->pdev->dev, | 
 | 2835 | 			       "reading geometry failed, volume " | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2836 | 			       "does not support reading geometry\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2837 | 			drv->heads = 255; | 
| dann frazier | b028461 | 2010-02-17 16:53:31 -0700 | [diff] [blame] | 2838 | 			drv->sectors = 32;	/* Sectors per track */ | 
| Mike Miller (OS Dev) | 7f42d3b | 2007-04-04 19:08:23 -0700 | [diff] [blame] | 2839 | 			drv->cylinders = total_size + 1; | 
| Mike Miller | 89f97ad | 2006-12-18 10:59:39 +0100 | [diff] [blame] | 2840 | 			drv->raid_level = RAID_UNKNOWN; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2841 | 		} else { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2842 | 			drv->heads = inq_buff->data_byte[6]; | 
 | 2843 | 			drv->sectors = inq_buff->data_byte[7]; | 
 | 2844 | 			drv->cylinders = (inq_buff->data_byte[4] & 0xff) << 8; | 
 | 2845 | 			drv->cylinders += inq_buff->data_byte[5]; | 
 | 2846 | 			drv->raid_level = inq_buff->data_byte[8]; | 
| Matthew Wilcox | 3f7705e | 2006-10-21 10:24:19 -0700 | [diff] [blame] | 2847 | 		} | 
 | 2848 | 		drv->block_size = block_size; | 
| Mike Miller (OS Dev) | 97c0697 | 2007-03-06 01:42:14 -0800 | [diff] [blame] | 2849 | 		drv->nr_blocks = total_size + 1; | 
| Matthew Wilcox | 3f7705e | 2006-10-21 10:24:19 -0700 | [diff] [blame] | 2850 | 		t = drv->heads * drv->sectors; | 
 | 2851 | 		if (t > 1) { | 
| Mike Miller (OS Dev) | 97c0697 | 2007-03-06 01:42:14 -0800 | [diff] [blame] | 2852 | 			sector_t real_size = total_size + 1; | 
 | 2853 | 			unsigned long rem = sector_div(real_size, t); | 
| Matthew Wilcox | 3f7705e | 2006-10-21 10:24:19 -0700 | [diff] [blame] | 2854 | 			if (rem) | 
| Mike Miller (OS Dev) | 97c0697 | 2007-03-06 01:42:14 -0800 | [diff] [blame] | 2855 | 				real_size++; | 
 | 2856 | 			drv->cylinders = real_size; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2857 | 		} | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2858 | 	} else {		/* Get geometry failed */ | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2859 | 		dev_warn(&h->pdev->dev, "reading geometry failed\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2860 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2861 | } | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2862 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2863 | static void | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2864 | cciss_read_capacity(ctlr_info_t *h, int logvol, sector_t *total_size, | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2865 | 		    unsigned int *block_size) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2866 | { | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2867 | 	ReadCapdata_struct *buf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2868 | 	int return_code; | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 2869 | 	unsigned char scsi3addr[8]; | 
| Mariusz Kozlowski | 1aebe18 | 2007-08-11 22:34:29 +0200 | [diff] [blame] | 2870 |  | 
 | 2871 | 	buf = kzalloc(sizeof(ReadCapdata_struct), GFP_KERNEL); | 
 | 2872 | 	if (!buf) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2873 | 		dev_warn(&h->pdev->dev, "out of memory\n"); | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2874 | 		return; | 
 | 2875 | 	} | 
| Mariusz Kozlowski | 1aebe18 | 2007-08-11 22:34:29 +0200 | [diff] [blame] | 2876 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2877 | 	log_unit_to_scsi3addr(h, scsi3addr, logvol); | 
 | 2878 | 	return_code = sendcmd_withirq(h, CCISS_READ_CAPACITY, buf, | 
| Stephen M. Cameron | 7b838bd | 2009-11-12 12:49:30 -0600 | [diff] [blame] | 2879 | 		sizeof(ReadCapdata_struct), 0, scsi3addr, TYPE_CMD); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2880 | 	if (return_code == IO_OK) { | 
| Al Viro | 4c1f2b3 | 2007-03-14 09:19:10 +0000 | [diff] [blame] | 2881 | 		*total_size = be32_to_cpu(*(__be32 *) buf->total_size); | 
 | 2882 | 		*block_size = be32_to_cpu(*(__be32 *) buf->block_size); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2883 | 	} else {		/* read capacity command failed */ | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2884 | 		dev_warn(&h->pdev->dev, "read capacity failed\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2885 | 		*total_size = 0; | 
 | 2886 | 		*block_size = BLOCK_SIZE; | 
 | 2887 | 	} | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2888 | 	kfree(buf); | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2889 | } | 
 | 2890 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2891 | static void cciss_read_capacity_16(ctlr_info_t *h, int logvol, | 
| Stephen M. Cameron | 7b838bd | 2009-11-12 12:49:30 -0600 | [diff] [blame] | 2892 | 	sector_t *total_size, unsigned int *block_size) | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2893 | { | 
 | 2894 | 	ReadCapdata_struct_16 *buf; | 
 | 2895 | 	int return_code; | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 2896 | 	unsigned char scsi3addr[8]; | 
| Mariusz Kozlowski | 1aebe18 | 2007-08-11 22:34:29 +0200 | [diff] [blame] | 2897 |  | 
 | 2898 | 	buf = kzalloc(sizeof(ReadCapdata_struct_16), GFP_KERNEL); | 
 | 2899 | 	if (!buf) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2900 | 		dev_warn(&h->pdev->dev, "out of memory\n"); | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2901 | 		return; | 
 | 2902 | 	} | 
| Mariusz Kozlowski | 1aebe18 | 2007-08-11 22:34:29 +0200 | [diff] [blame] | 2903 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2904 | 	log_unit_to_scsi3addr(h, scsi3addr, logvol); | 
 | 2905 | 	return_code = sendcmd_withirq(h, CCISS_READ_CAPACITY_16, | 
 | 2906 | 		buf, sizeof(ReadCapdata_struct_16), | 
| Stephen M. Cameron | 7b838bd | 2009-11-12 12:49:30 -0600 | [diff] [blame] | 2907 | 			0, scsi3addr, TYPE_CMD); | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2908 | 	if (return_code == IO_OK) { | 
| Al Viro | 4c1f2b3 | 2007-03-14 09:19:10 +0000 | [diff] [blame] | 2909 | 		*total_size = be64_to_cpu(*(__be64 *) buf->total_size); | 
 | 2910 | 		*block_size = be32_to_cpu(*(__be32 *) buf->block_size); | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2911 | 	} else {		/* read capacity command failed */ | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2912 | 		dev_warn(&h->pdev->dev, "read capacity failed\n"); | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2913 | 		*total_size = 0; | 
 | 2914 | 		*block_size = BLOCK_SIZE; | 
 | 2915 | 	} | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2916 | 	dev_info(&h->pdev->dev, "      blocks= %llu block_size= %d\n", | 
| Mike Miller (OS Dev) | 97c0697 | 2007-03-06 01:42:14 -0800 | [diff] [blame] | 2917 | 	       (unsigned long long)*total_size+1, *block_size); | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2918 | 	kfree(buf); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2919 | } | 
 | 2920 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2921 | static int cciss_revalidate(struct gendisk *disk) | 
 | 2922 | { | 
 | 2923 | 	ctlr_info_t *h = get_host(disk); | 
 | 2924 | 	drive_info_struct *drv = get_drv(disk); | 
 | 2925 | 	int logvol; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2926 | 	int FOUND = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2927 | 	unsigned int block_size; | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2928 | 	sector_t total_size; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2929 | 	InquiryData_struct *inq_buff = NULL; | 
 | 2930 |  | 
| Stephen M. Cameron | 68264e9 | 2011-01-19 08:25:02 -0700 | [diff] [blame] | 2931 | 	for (logvol = 0; logvol <= h->highest_lun; logvol++) { | 
| Stephen M. Cameron | 0fc13c8 | 2010-12-17 09:01:37 +0100 | [diff] [blame] | 2932 | 		if (!h->drv[logvol]) | 
| Linus Torvalds | 453434c | 2010-12-20 21:21:49 -0800 | [diff] [blame] | 2933 | 			continue; | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 2934 | 		if (memcmp(h->drv[logvol]->LunID, drv->LunID, | 
| Stephen M. Cameron | 39ccf9a | 2009-09-17 13:48:00 -0500 | [diff] [blame] | 2935 | 			sizeof(drv->LunID)) == 0) { | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2936 | 			FOUND = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2937 | 			break; | 
 | 2938 | 		} | 
 | 2939 | 	} | 
 | 2940 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2941 | 	if (!FOUND) | 
 | 2942 | 		return 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2943 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2944 | 	inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL); | 
 | 2945 | 	if (inq_buff == NULL) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2946 | 		dev_warn(&h->pdev->dev, "out of memory\n"); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2947 | 		return 1; | 
 | 2948 | 	} | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2949 | 	if (h->cciss_read == CCISS_READ_10) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2950 | 		cciss_read_capacity(h, logvol, | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2951 | 					&total_size, &block_size); | 
 | 2952 | 	} else { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2953 | 		cciss_read_capacity_16(h, logvol, | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 2954 | 					&total_size, &block_size); | 
 | 2955 | 	} | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 2956 | 	cciss_geometry_inquiry(h, logvol, total_size, block_size, | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2957 | 			       inq_buff, drv); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2958 |  | 
| Martin K. Petersen | e1defc4 | 2009-05-22 17:17:49 -0400 | [diff] [blame] | 2959 | 	blk_queue_logical_block_size(drv->queue, drv->block_size); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2960 | 	set_capacity(disk, drv->nr_blocks); | 
 | 2961 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2962 | 	kfree(inq_buff); | 
 | 2963 | 	return 0; | 
 | 2964 | } | 
 | 2965 |  | 
 | 2966 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2967 |  * Map (physical) PCI mem into (virtual) kernel space | 
 | 2968 |  */ | 
 | 2969 | static void __iomem *remap_pci_mem(ulong base, ulong size) | 
 | 2970 | { | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2971 | 	ulong page_base = ((ulong) base) & PAGE_MASK; | 
 | 2972 | 	ulong page_offs = ((ulong) base) - page_base; | 
 | 2973 | 	void __iomem *page_remapped = ioremap(page_base, page_offs + size); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2974 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2975 | 	return page_remapped ? (page_remapped + page_offs) : NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2976 | } | 
 | 2977 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2978 | /* | 
 | 2979 |  * Takes jobs of the Q and sends them to the hardware, then puts it on | 
 | 2980 |  * the Q to wait for completion. | 
 | 2981 |  */ | 
 | 2982 | static void start_io(ctlr_info_t *h) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2983 | { | 
 | 2984 | 	CommandList_struct *c; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2985 |  | 
| Jens Axboe | e6e1ee9 | 2011-01-10 21:50:33 +0100 | [diff] [blame] | 2986 | 	while (!list_empty(&h->reqQ)) { | 
 | 2987 | 		c = list_entry(h->reqQ.next, CommandList_struct, list); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2988 | 		/* can't do anything if fifo is full */ | 
 | 2989 | 		if ((h->access.fifo_full(h))) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 2990 | 			dev_warn(&h->pdev->dev, "fifo full\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2991 | 			break; | 
 | 2992 | 		} | 
 | 2993 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2994 | 		/* Get the first entry from the Request Q */ | 
| Jens Axboe | 8a3173d | 2008-11-20 09:46:09 +0100 | [diff] [blame] | 2995 | 		removeQ(c); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2996 | 		h->Qdepth--; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 2997 |  | 
 | 2998 | 		/* Tell the controller execute command */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2999 | 		h->access.submit_command(h, c); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3000 |  | 
 | 3001 | 		/* Put job onto the completed Q */ | 
| Jens Axboe | 8a3173d | 2008-11-20 09:46:09 +0100 | [diff] [blame] | 3002 | 		addQ(&h->cmpQ, c); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3003 | 	} | 
 | 3004 | } | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3005 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 3006 | /* Assumes that h->lock is held. */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3007 | /* Zeros out the error record and then resends the command back */ | 
 | 3008 | /* to the controller */ | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3009 | static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3010 | { | 
 | 3011 | 	/* erase the old error information */ | 
 | 3012 | 	memset(c->err_info, 0, sizeof(ErrorInfo_struct)); | 
 | 3013 |  | 
 | 3014 | 	/* add it to software queue and then send it to the controller */ | 
| Jens Axboe | 8a3173d | 2008-11-20 09:46:09 +0100 | [diff] [blame] | 3015 | 	addQ(&h->reqQ, c); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3016 | 	h->Qdepth++; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3017 | 	if (h->Qdepth > h->maxQsinceinit) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3018 | 		h->maxQsinceinit = h->Qdepth; | 
 | 3019 |  | 
 | 3020 | 	start_io(h); | 
 | 3021 | } | 
| Jens Axboe | a9925a0 | 2006-01-09 16:04:06 +0100 | [diff] [blame] | 3022 |  | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3023 | static inline unsigned int make_status_bytes(unsigned int scsi_status_byte, | 
 | 3024 | 	unsigned int msg_byte, unsigned int host_byte, | 
 | 3025 | 	unsigned int driver_byte) | 
 | 3026 | { | 
 | 3027 | 	/* inverse of macros in scsi.h */ | 
 | 3028 | 	return (scsi_status_byte & 0xff) | | 
 | 3029 | 		((msg_byte & 0xff) << 8) | | 
 | 3030 | 		((host_byte & 0xff) << 16) | | 
 | 3031 | 		((driver_byte & 0xff) << 24); | 
 | 3032 | } | 
 | 3033 |  | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3034 | static inline int evaluate_target_status(ctlr_info_t *h, | 
 | 3035 | 			CommandList_struct *cmd, int *retry_cmd) | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3036 | { | 
 | 3037 | 	unsigned char sense_key; | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3038 | 	unsigned char status_byte, msg_byte, host_byte, driver_byte; | 
 | 3039 | 	int error_value; | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3040 |  | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3041 | 	*retry_cmd = 0; | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3042 | 	/* If we get in here, it means we got "target status", that is, scsi status */ | 
 | 3043 | 	status_byte = cmd->err_info->ScsiStatus; | 
 | 3044 | 	driver_byte = DRIVER_OK; | 
 | 3045 | 	msg_byte = cmd->err_info->CommandStatus; /* correct?  seems too device specific */ | 
 | 3046 |  | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3047 | 	if (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3048 | 		host_byte = DID_PASSTHROUGH; | 
 | 3049 | 	else | 
 | 3050 | 		host_byte = DID_OK; | 
 | 3051 |  | 
 | 3052 | 	error_value = make_status_bytes(status_byte, msg_byte, | 
 | 3053 | 		host_byte, driver_byte); | 
 | 3054 |  | 
 | 3055 | 	if (cmd->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION) { | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3056 | 		if (cmd->rq->cmd_type != REQ_TYPE_BLOCK_PC) | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3057 | 			dev_warn(&h->pdev->dev, "cmd %p " | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3058 | 			       "has SCSI Status 0x%x\n", | 
 | 3059 | 			       cmd, cmd->err_info->ScsiStatus); | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3060 | 		return error_value; | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3061 | 	} | 
 | 3062 |  | 
 | 3063 | 	/* check the sense key */ | 
 | 3064 | 	sense_key = 0xf & cmd->err_info->SenseInfo[2]; | 
 | 3065 | 	/* no status or recovered error */ | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3066 | 	if (((sense_key == 0x0) || (sense_key == 0x1)) && | 
 | 3067 | 	    (cmd->rq->cmd_type != REQ_TYPE_BLOCK_PC)) | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3068 | 		error_value = 0; | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3069 |  | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3070 | 	if (check_for_unit_attention(h, cmd)) { | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3071 | 		*retry_cmd = !(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC); | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3072 | 		return 0; | 
 | 3073 | 	} | 
 | 3074 |  | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3075 | 	/* Not SG_IO or similar? */ | 
 | 3076 | 	if (cmd->rq->cmd_type != REQ_TYPE_BLOCK_PC) { | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3077 | 		if (error_value != 0) | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3078 | 			dev_warn(&h->pdev->dev, "cmd %p has CHECK CONDITION" | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3079 | 			       " sense key = 0x%x\n", cmd, sense_key); | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3080 | 		return error_value; | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3081 | 	} | 
 | 3082 |  | 
 | 3083 | 	/* SG_IO or similar, copy sense data back */ | 
 | 3084 | 	if (cmd->rq->sense) { | 
 | 3085 | 		if (cmd->rq->sense_len > cmd->err_info->SenseLen) | 
 | 3086 | 			cmd->rq->sense_len = cmd->err_info->SenseLen; | 
 | 3087 | 		memcpy(cmd->rq->sense, cmd->err_info->SenseInfo, | 
 | 3088 | 			cmd->rq->sense_len); | 
 | 3089 | 	} else | 
 | 3090 | 		cmd->rq->sense_len = 0; | 
 | 3091 |  | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3092 | 	return error_value; | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3093 | } | 
 | 3094 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3095 | /* checks the status of the job and calls complete buffers to mark all | 
| Jens Axboe | a9925a0 | 2006-01-09 16:04:06 +0100 | [diff] [blame] | 3096 |  * buffers for the completed job. Note that this function does not need | 
 | 3097 |  * to hold the hba/queue lock. | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3098 |  */ | 
 | 3099 | static inline void complete_command(ctlr_info_t *h, CommandList_struct *cmd, | 
 | 3100 | 				    int timeout) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3101 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3102 | 	int retry_cmd = 0; | 
| Mike Miller (OS Dev) | 198b766 | 2007-05-08 00:29:34 -0700 | [diff] [blame] | 3103 | 	struct request *rq = cmd->rq; | 
 | 3104 |  | 
 | 3105 | 	rq->errors = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3106 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3107 | 	if (timeout) | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3108 | 		rq->errors = make_status_bytes(0, 0, 0, DRIVER_TIMEOUT); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3109 |  | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3110 | 	if (cmd->err_info->CommandStatus == 0)	/* no error has occurred */ | 
 | 3111 | 		goto after_error_processing; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3112 |  | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3113 | 	switch (cmd->err_info->CommandStatus) { | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3114 | 	case CMD_TARGET_STATUS: | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3115 | 		rq->errors = evaluate_target_status(h, cmd, &retry_cmd); | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3116 | 		break; | 
 | 3117 | 	case CMD_DATA_UNDERRUN: | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3118 | 		if (cmd->rq->cmd_type == REQ_TYPE_FS) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3119 | 			dev_warn(&h->pdev->dev, "cmd %p has" | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3120 | 			       " completed with data underrun " | 
 | 3121 | 			       "reported\n", cmd); | 
| Tejun Heo | c3a4d78 | 2009-05-07 22:24:37 +0900 | [diff] [blame] | 3122 | 			cmd->rq->resid_len = cmd->err_info->ResidualCnt; | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3123 | 		} | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3124 | 		break; | 
 | 3125 | 	case CMD_DATA_OVERRUN: | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3126 | 		if (cmd->rq->cmd_type == REQ_TYPE_FS) | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3127 | 			dev_warn(&h->pdev->dev, "cciss: cmd %p has" | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3128 | 			       " completed with data overrun " | 
 | 3129 | 			       "reported\n", cmd); | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3130 | 		break; | 
 | 3131 | 	case CMD_INVALID: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3132 | 		dev_warn(&h->pdev->dev, "cciss: cmd %p is " | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3133 | 		       "reported invalid\n", cmd); | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3134 | 		rq->errors = make_status_bytes(SAM_STAT_GOOD, | 
 | 3135 | 			cmd->err_info->CommandStatus, DRIVER_OK, | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3136 | 			(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ? | 
 | 3137 | 				DID_PASSTHROUGH : DID_ERROR); | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3138 | 		break; | 
 | 3139 | 	case CMD_PROTOCOL_ERR: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3140 | 		dev_warn(&h->pdev->dev, "cciss: cmd %p has " | 
 | 3141 | 		       "protocol error\n", cmd); | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3142 | 		rq->errors = make_status_bytes(SAM_STAT_GOOD, | 
 | 3143 | 			cmd->err_info->CommandStatus, DRIVER_OK, | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3144 | 			(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ? | 
 | 3145 | 				DID_PASSTHROUGH : DID_ERROR); | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3146 | 		break; | 
 | 3147 | 	case CMD_HARDWARE_ERR: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3148 | 		dev_warn(&h->pdev->dev, "cciss: cmd %p had " | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3149 | 		       " hardware error\n", cmd); | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3150 | 		rq->errors = make_status_bytes(SAM_STAT_GOOD, | 
 | 3151 | 			cmd->err_info->CommandStatus, DRIVER_OK, | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3152 | 			(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ? | 
 | 3153 | 				DID_PASSTHROUGH : DID_ERROR); | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3154 | 		break; | 
 | 3155 | 	case CMD_CONNECTION_LOST: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3156 | 		dev_warn(&h->pdev->dev, "cciss: cmd %p had " | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3157 | 		       "connection lost\n", cmd); | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3158 | 		rq->errors = make_status_bytes(SAM_STAT_GOOD, | 
 | 3159 | 			cmd->err_info->CommandStatus, DRIVER_OK, | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3160 | 			(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ? | 
 | 3161 | 				DID_PASSTHROUGH : DID_ERROR); | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3162 | 		break; | 
 | 3163 | 	case CMD_ABORTED: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3164 | 		dev_warn(&h->pdev->dev, "cciss: cmd %p was " | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3165 | 		       "aborted\n", cmd); | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3166 | 		rq->errors = make_status_bytes(SAM_STAT_GOOD, | 
 | 3167 | 			cmd->err_info->CommandStatus, DRIVER_OK, | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3168 | 			(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ? | 
 | 3169 | 				DID_PASSTHROUGH : DID_ABORT); | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3170 | 		break; | 
 | 3171 | 	case CMD_ABORT_FAILED: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3172 | 		dev_warn(&h->pdev->dev, "cciss: cmd %p reports " | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3173 | 		       "abort failed\n", cmd); | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3174 | 		rq->errors = make_status_bytes(SAM_STAT_GOOD, | 
 | 3175 | 			cmd->err_info->CommandStatus, DRIVER_OK, | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3176 | 			(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ? | 
 | 3177 | 				DID_PASSTHROUGH : DID_ERROR); | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3178 | 		break; | 
 | 3179 | 	case CMD_UNSOLICITED_ABORT: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3180 | 		dev_warn(&h->pdev->dev, "cciss%d: unsolicited " | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3181 | 		       "abort %p\n", h->ctlr, cmd); | 
 | 3182 | 		if (cmd->retry_count < MAX_CMD_RETRIES) { | 
 | 3183 | 			retry_cmd = 1; | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3184 | 			dev_warn(&h->pdev->dev, "retrying %p\n", cmd); | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3185 | 			cmd->retry_count++; | 
 | 3186 | 		} else | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3187 | 			dev_warn(&h->pdev->dev, | 
 | 3188 | 				"%p retried too many times\n", cmd); | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3189 | 		rq->errors = make_status_bytes(SAM_STAT_GOOD, | 
 | 3190 | 			cmd->err_info->CommandStatus, DRIVER_OK, | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3191 | 			(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ? | 
 | 3192 | 				DID_PASSTHROUGH : DID_ABORT); | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3193 | 		break; | 
 | 3194 | 	case CMD_TIMEOUT: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3195 | 		dev_warn(&h->pdev->dev, "cmd %p timedout\n", cmd); | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3196 | 		rq->errors = make_status_bytes(SAM_STAT_GOOD, | 
 | 3197 | 			cmd->err_info->CommandStatus, DRIVER_OK, | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3198 | 			(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ? | 
 | 3199 | 				DID_PASSTHROUGH : DID_ERROR); | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3200 | 		break; | 
| Stephen M. Cameron | 6d9a4f9 | 2011-03-12 10:02:30 +0100 | [diff] [blame] | 3201 | 	case CMD_UNABORTABLE: | 
 | 3202 | 		dev_warn(&h->pdev->dev, "cmd %p unabortable\n", cmd); | 
 | 3203 | 		rq->errors = make_status_bytes(SAM_STAT_GOOD, | 
 | 3204 | 			cmd->err_info->CommandStatus, DRIVER_OK, | 
 | 3205 | 			cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC ? | 
 | 3206 | 				DID_PASSTHROUGH : DID_ERROR); | 
 | 3207 | 		break; | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3208 | 	default: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3209 | 		dev_warn(&h->pdev->dev, "cmd %p returned " | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3210 | 		       "unknown status %x\n", cmd, | 
 | 3211 | 		       cmd->err_info->CommandStatus); | 
| Steve Cameron | 1a614f5 | 2007-10-16 23:27:37 -0700 | [diff] [blame] | 3212 | 		rq->errors = make_status_bytes(SAM_STAT_GOOD, | 
 | 3213 | 			cmd->err_info->CommandStatus, DRIVER_OK, | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3214 | 			(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ? | 
 | 3215 | 				DID_PASSTHROUGH : DID_ERROR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3216 | 	} | 
| Mike Miller (OS Dev) | d38ae16 | 2007-05-08 00:29:29 -0700 | [diff] [blame] | 3217 |  | 
 | 3218 | after_error_processing: | 
 | 3219 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3220 | 	/* We need to return this command */ | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3221 | 	if (retry_cmd) { | 
 | 3222 | 		resend_cciss_cmd(h, cmd); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3223 | 		return; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3224 | 	} | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3225 | 	cmd->rq->completion_data = cmd; | 
| Jens Axboe | a9925a0 | 2006-01-09 16:04:06 +0100 | [diff] [blame] | 3226 | 	blk_complete_request(cmd->rq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3227 | } | 
 | 3228 |  | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3229 | static inline u32 cciss_tag_contains_index(u32 tag) | 
 | 3230 | { | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3231 | #define DIRECT_LOOKUP_BIT 0x10 | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3232 | 	return tag & DIRECT_LOOKUP_BIT; | 
 | 3233 | } | 
 | 3234 |  | 
 | 3235 | static inline u32 cciss_tag_to_index(u32 tag) | 
 | 3236 | { | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3237 | #define DIRECT_LOOKUP_SHIFT 5 | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3238 | 	return tag >> DIRECT_LOOKUP_SHIFT; | 
 | 3239 | } | 
 | 3240 |  | 
| Stephen M. Cameron | 0498cc2 | 2011-03-12 10:02:16 +0100 | [diff] [blame] | 3241 | static inline u32 cciss_tag_discard_error_bits(ctlr_info_t *h, u32 tag) | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3242 | { | 
| Stephen M. Cameron | 0498cc2 | 2011-03-12 10:02:16 +0100 | [diff] [blame] | 3243 | #define CCISS_PERF_ERROR_BITS ((1 << DIRECT_LOOKUP_SHIFT) - 1) | 
 | 3244 | #define CCISS_SIMPLE_ERROR_BITS 0x03 | 
 | 3245 | 	if (likely(h->transMethod & CFGTBL_Trans_Performant)) | 
 | 3246 | 		return tag & ~CCISS_PERF_ERROR_BITS; | 
 | 3247 | 	return tag & ~CCISS_SIMPLE_ERROR_BITS; | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3248 | } | 
 | 3249 |  | 
 | 3250 | static inline void cciss_mark_tag_indexed(u32 *tag) | 
 | 3251 | { | 
 | 3252 | 	*tag |= DIRECT_LOOKUP_BIT; | 
 | 3253 | } | 
 | 3254 |  | 
 | 3255 | static inline void cciss_set_tag_index(u32 *tag, u32 index) | 
 | 3256 | { | 
 | 3257 | 	*tag |= (index << DIRECT_LOOKUP_SHIFT); | 
 | 3258 | } | 
 | 3259 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3260 | /* | 
 | 3261 |  * Get a request and submit it to the controller. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3262 |  */ | 
| Jens Axboe | 165125e | 2007-07-24 09:28:11 +0200 | [diff] [blame] | 3263 | static void do_cciss_request(struct request_queue *q) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3264 | { | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3265 | 	ctlr_info_t *h = q->queuedata; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3266 | 	CommandList_struct *c; | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 3267 | 	sector_t start_blk; | 
 | 3268 | 	int seg; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3269 | 	struct request *creq; | 
 | 3270 | 	u64bit temp64; | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3271 | 	struct scatterlist *tmp_sg; | 
 | 3272 | 	SGDescriptor_struct *curr_sg; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3273 | 	drive_info_struct *drv; | 
 | 3274 | 	int i, dir; | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3275 | 	int sg_index = 0; | 
 | 3276 | 	int chained = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3277 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3278 |       queue: | 
| Tejun Heo | 9934c8c | 2009-05-08 11:54:16 +0900 | [diff] [blame] | 3279 | 	creq = blk_peek_request(q); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3280 | 	if (!creq) | 
 | 3281 | 		goto startio; | 
 | 3282 |  | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3283 | 	BUG_ON(creq->nr_phys_segments > h->maxsgentries); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3284 |  | 
| Stephen M. Cameron | 6b4d96b | 2010-07-19 13:46:43 -0500 | [diff] [blame] | 3285 | 	c = cmd_alloc(h); | 
 | 3286 | 	if (!c) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3287 | 		goto full; | 
 | 3288 |  | 
| Tejun Heo | 9934c8c | 2009-05-08 11:54:16 +0900 | [diff] [blame] | 3289 | 	blk_start_request(creq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3290 |  | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3291 | 	tmp_sg = h->scatter_list[c->cmdindex]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3292 | 	spin_unlock_irq(q->queue_lock); | 
 | 3293 |  | 
 | 3294 | 	c->cmd_type = CMD_RWREQ; | 
 | 3295 | 	c->rq = creq; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3296 |  | 
 | 3297 | 	/* fill in the request */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3298 | 	drv = creq->rq_disk->private_data; | 
| dann frazier | b028461 | 2010-02-17 16:53:31 -0700 | [diff] [blame] | 3299 | 	c->Header.ReplyQueue = 0;	/* unused in simple mode */ | 
| Mike Miller | 33079b2 | 2005-09-13 01:25:22 -0700 | [diff] [blame] | 3300 | 	/* got command from pool, so use the command block index instead */ | 
 | 3301 | 	/* for direct lookups. */ | 
 | 3302 | 	/* The first 2 bits are reserved for controller error reporting. */ | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3303 | 	cciss_set_tag_index(&c->Header.Tag.lower, c->cmdindex); | 
 | 3304 | 	cciss_mark_tag_indexed(&c->Header.Tag.lower); | 
| Stephen M. Cameron | 39ccf9a | 2009-09-17 13:48:00 -0500 | [diff] [blame] | 3305 | 	memcpy(&c->Header.LUN, drv->LunID, sizeof(drv->LunID)); | 
| dann frazier | b028461 | 2010-02-17 16:53:31 -0700 | [diff] [blame] | 3306 | 	c->Request.CDBLen = 10;	/* 12 byte commands not in FW yet; */ | 
 | 3307 | 	c->Request.Type.Type = TYPE_CMD;	/* It is a command. */ | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3308 | 	c->Request.Type.Attribute = ATTR_SIMPLE; | 
 | 3309 | 	c->Request.Type.Direction = | 
| Mike Miller | a52de24 | 2006-12-18 11:00:14 +0100 | [diff] [blame] | 3310 | 	    (rq_data_dir(creq) == READ) ? XFER_READ : XFER_WRITE; | 
| dann frazier | b028461 | 2010-02-17 16:53:31 -0700 | [diff] [blame] | 3311 | 	c->Request.Timeout = 0;	/* Don't time out */ | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3312 | 	c->Request.CDB[0] = | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 3313 | 	    (rq_data_dir(creq) == READ) ? h->cciss_read : h->cciss_write; | 
| Tejun Heo | 83096eb | 2009-05-07 22:24:39 +0900 | [diff] [blame] | 3314 | 	start_blk = blk_rq_pos(creq); | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3315 | 	dev_dbg(&h->pdev->dev, "sector =%d nr_sectors=%d\n", | 
| Tejun Heo | 83096eb | 2009-05-07 22:24:39 +0900 | [diff] [blame] | 3316 | 	       (int)blk_rq_pos(creq), (int)blk_rq_sectors(creq)); | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3317 | 	sg_init_table(tmp_sg, h->maxsgentries); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3318 | 	seg = blk_rq_map_sg(q, creq, tmp_sg); | 
 | 3319 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3320 | 	/* get the DMA records for the setup */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3321 | 	if (c->Request.Type.Direction == XFER_READ) | 
 | 3322 | 		dir = PCI_DMA_FROMDEVICE; | 
 | 3323 | 	else | 
 | 3324 | 		dir = PCI_DMA_TODEVICE; | 
 | 3325 |  | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3326 | 	curr_sg = c->SG; | 
 | 3327 | 	sg_index = 0; | 
 | 3328 | 	chained = 0; | 
 | 3329 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3330 | 	for (i = 0; i < seg; i++) { | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3331 | 		if (((sg_index+1) == (h->max_cmd_sgentries)) && | 
 | 3332 | 			!chained && ((seg - i) > 1)) { | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3333 | 			/* Point to next chain block. */ | 
| Stephen M. Cameron | dccc9b5 | 2010-02-26 16:01:27 -0600 | [diff] [blame] | 3334 | 			curr_sg = h->cmd_sg_list[c->cmdindex]; | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3335 | 			sg_index = 0; | 
 | 3336 | 			chained = 1; | 
 | 3337 | 		} | 
 | 3338 | 		curr_sg[sg_index].Len = tmp_sg[i].length; | 
| Jens Axboe | 45711f1 | 2007-10-22 21:19:53 +0200 | [diff] [blame] | 3339 | 		temp64.val = (__u64) pci_map_page(h->pdev, sg_page(&tmp_sg[i]), | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3340 | 						tmp_sg[i].offset, | 
 | 3341 | 						tmp_sg[i].length, dir); | 
 | 3342 | 		curr_sg[sg_index].Addr.lower = temp64.val32.lower; | 
 | 3343 | 		curr_sg[sg_index].Addr.upper = temp64.val32.upper; | 
 | 3344 | 		curr_sg[sg_index].Ext = 0;  /* we are not chaining */ | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3345 | 		++sg_index; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3346 | 	} | 
| Stephen M. Cameron | d45033e | 2010-02-26 16:01:37 -0600 | [diff] [blame] | 3347 | 	if (chained) | 
 | 3348 | 		cciss_map_sg_chain_block(h, c, h->cmd_sg_list[c->cmdindex], | 
 | 3349 | 			(seg - (h->max_cmd_sgentries - 1)) * | 
 | 3350 | 				sizeof(SGDescriptor_struct)); | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3351 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3352 | 	/* track how many SG entries we are using */ | 
 | 3353 | 	if (seg > h->maxSG) | 
 | 3354 | 		h->maxSG = seg; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3355 |  | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3356 | 	dev_dbg(&h->pdev->dev, "Submitting %u sectors in %d segments " | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3357 | 			"chained[%d]\n", | 
 | 3358 | 			blk_rq_sectors(creq), seg, chained); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3359 |  | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3360 | 	c->Header.SGTotal = seg + chained; | 
 | 3361 | 	if (seg <= h->max_cmd_sgentries) | 
 | 3362 | 		c->Header.SGList = c->Header.SGTotal; | 
 | 3363 | 	else | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3364 | 		c->Header.SGList = h->max_cmd_sgentries; | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3365 | 	set_performant_mode(h, c); | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 3366 |  | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3367 | 	if (likely(creq->cmd_type == REQ_TYPE_FS)) { | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3368 | 		if(h->cciss_read == CCISS_READ_10) { | 
 | 3369 | 			c->Request.CDB[1] = 0; | 
| dann frazier | b028461 | 2010-02-17 16:53:31 -0700 | [diff] [blame] | 3370 | 			c->Request.CDB[2] = (start_blk >> 24) & 0xff; /* MSB */ | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3371 | 			c->Request.CDB[3] = (start_blk >> 16) & 0xff; | 
 | 3372 | 			c->Request.CDB[4] = (start_blk >> 8) & 0xff; | 
 | 3373 | 			c->Request.CDB[5] = start_blk & 0xff; | 
| dann frazier | b028461 | 2010-02-17 16:53:31 -0700 | [diff] [blame] | 3374 | 			c->Request.CDB[6] = 0; /* (sect >> 24) & 0xff; MSB */ | 
| Tejun Heo | 83096eb | 2009-05-07 22:24:39 +0900 | [diff] [blame] | 3375 | 			c->Request.CDB[7] = (blk_rq_sectors(creq) >> 8) & 0xff; | 
 | 3376 | 			c->Request.CDB[8] = blk_rq_sectors(creq) & 0xff; | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3377 | 			c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0; | 
 | 3378 | 		} else { | 
| Randy Dunlap | 582539e | 2008-02-06 01:36:54 -0800 | [diff] [blame] | 3379 | 			u32 upper32 = upper_32_bits(start_blk); | 
 | 3380 |  | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3381 | 			c->Request.CDBLen = 16; | 
 | 3382 | 			c->Request.CDB[1]= 0; | 
| dann frazier | b028461 | 2010-02-17 16:53:31 -0700 | [diff] [blame] | 3383 | 			c->Request.CDB[2]= (upper32 >> 24) & 0xff; /* MSB */ | 
| Randy Dunlap | 582539e | 2008-02-06 01:36:54 -0800 | [diff] [blame] | 3384 | 			c->Request.CDB[3]= (upper32 >> 16) & 0xff; | 
 | 3385 | 			c->Request.CDB[4]= (upper32 >>  8) & 0xff; | 
 | 3386 | 			c->Request.CDB[5]= upper32 & 0xff; | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3387 | 			c->Request.CDB[6]= (start_blk >> 24) & 0xff; | 
 | 3388 | 			c->Request.CDB[7]= (start_blk >> 16) & 0xff; | 
 | 3389 | 			c->Request.CDB[8]= (start_blk >>  8) & 0xff; | 
 | 3390 | 			c->Request.CDB[9]= start_blk & 0xff; | 
| Tejun Heo | 83096eb | 2009-05-07 22:24:39 +0900 | [diff] [blame] | 3391 | 			c->Request.CDB[10]= (blk_rq_sectors(creq) >> 24) & 0xff; | 
 | 3392 | 			c->Request.CDB[11]= (blk_rq_sectors(creq) >> 16) & 0xff; | 
 | 3393 | 			c->Request.CDB[12]= (blk_rq_sectors(creq) >>  8) & 0xff; | 
 | 3394 | 			c->Request.CDB[13]= blk_rq_sectors(creq) & 0xff; | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3395 | 			c->Request.CDB[14] = c->Request.CDB[15] = 0; | 
 | 3396 | 		} | 
| Christoph Hellwig | 33659eb | 2010-08-07 18:17:56 +0200 | [diff] [blame] | 3397 | 	} else if (creq->cmd_type == REQ_TYPE_BLOCK_PC) { | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3398 | 		c->Request.CDBLen = creq->cmd_len; | 
 | 3399 | 		memcpy(c->Request.CDB, creq->cmd, BLK_MAX_CDB); | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 3400 | 	} else { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3401 | 		dev_warn(&h->pdev->dev, "bad request type %d\n", | 
 | 3402 | 			creq->cmd_type); | 
| Mike Miller (OS Dev) | 03bbfee | 2007-05-08 00:29:32 -0700 | [diff] [blame] | 3403 | 		BUG(); | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 3404 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3405 |  | 
 | 3406 | 	spin_lock_irq(q->queue_lock); | 
 | 3407 |  | 
| Jens Axboe | 8a3173d | 2008-11-20 09:46:09 +0100 | [diff] [blame] | 3408 | 	addQ(&h->reqQ, c); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3409 | 	h->Qdepth++; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3410 | 	if (h->Qdepth > h->maxQsinceinit) | 
 | 3411 | 		h->maxQsinceinit = h->Qdepth; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3412 |  | 
 | 3413 | 	goto queue; | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 3414 | full: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3415 | 	blk_stop_queue(q); | 
| Mike Miller (OS Dev) | 00988a3 | 2006-09-30 23:27:23 -0700 | [diff] [blame] | 3416 | startio: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3417 | 	/* We will already have the driver lock here so not need | 
 | 3418 | 	 * to lock it. | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3419 | 	 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3420 | 	start_io(h); | 
 | 3421 | } | 
 | 3422 |  | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 3423 | static inline unsigned long get_next_completion(ctlr_info_t *h) | 
 | 3424 | { | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 3425 | 	return h->access.command_completed(h); | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 3426 | } | 
 | 3427 |  | 
 | 3428 | static inline int interrupt_pending(ctlr_info_t *h) | 
 | 3429 | { | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 3430 | 	return h->access.intr_pending(h); | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 3431 | } | 
 | 3432 |  | 
 | 3433 | static inline long interrupt_not_for_us(ctlr_info_t *h) | 
 | 3434 | { | 
| Stephen M. Cameron | 8112586 | 2010-07-19 13:46:54 -0500 | [diff] [blame] | 3435 | 	return ((h->access.intr_pending(h) == 0) || | 
| Mike Miller | 2cf3af1 | 2010-06-02 12:58:02 -0700 | [diff] [blame] | 3436 | 		(h->interrupts_enabled == 0)); | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 3437 | } | 
 | 3438 |  | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3439 | static inline int bad_tag(ctlr_info_t *h, u32 tag_index, | 
 | 3440 | 			u32 raw_tag) | 
 | 3441 | { | 
 | 3442 | 	if (unlikely(tag_index >= h->nr_cmds)) { | 
 | 3443 | 		dev_warn(&h->pdev->dev, "bad tag 0x%08x ignored.\n", raw_tag); | 
 | 3444 | 		return 1; | 
 | 3445 | 	} | 
 | 3446 | 	return 0; | 
 | 3447 | } | 
 | 3448 |  | 
 | 3449 | static inline void finish_cmd(ctlr_info_t *h, CommandList_struct *c, | 
 | 3450 | 				u32 raw_tag) | 
 | 3451 | { | 
 | 3452 | 	removeQ(c); | 
 | 3453 | 	if (likely(c->cmd_type == CMD_RWREQ)) | 
 | 3454 | 		complete_command(h, c, 0); | 
 | 3455 | 	else if (c->cmd_type == CMD_IOCTL_PEND) | 
 | 3456 | 		complete(c->waiting); | 
 | 3457 | #ifdef CONFIG_CISS_SCSI_TAPE | 
 | 3458 | 	else if (c->cmd_type == CMD_SCSI) | 
 | 3459 | 		complete_scsi_command(c, 0, raw_tag); | 
 | 3460 | #endif | 
 | 3461 | } | 
 | 3462 |  | 
| Mike Miller | 29979a7 | 2010-06-11 13:13:35 +0200 | [diff] [blame] | 3463 | static inline u32 next_command(ctlr_info_t *h) | 
 | 3464 | { | 
 | 3465 | 	u32 a; | 
 | 3466 |  | 
| Stephen M. Cameron | 0498cc2 | 2011-03-12 10:02:16 +0100 | [diff] [blame] | 3467 | 	if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant))) | 
| Mike Miller | 29979a7 | 2010-06-11 13:13:35 +0200 | [diff] [blame] | 3468 | 		return h->access.command_completed(h); | 
 | 3469 |  | 
 | 3470 | 	if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) { | 
 | 3471 | 		a = *(h->reply_pool_head); /* Next cmd in ring buffer */ | 
 | 3472 | 		(h->reply_pool_head)++; | 
 | 3473 | 		h->commands_outstanding--; | 
 | 3474 | 	} else { | 
 | 3475 | 		a = FIFO_EMPTY; | 
 | 3476 | 	} | 
 | 3477 | 	/* Check for wraparound */ | 
 | 3478 | 	if (h->reply_pool_head == (h->reply_pool + h->max_commands)) { | 
 | 3479 | 		h->reply_pool_head = h->reply_pool; | 
 | 3480 | 		h->reply_pool_wraparound ^= 1; | 
 | 3481 | 	} | 
 | 3482 | 	return a; | 
 | 3483 | } | 
 | 3484 |  | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3485 | /* process completion of an indexed ("direct lookup") command */ | 
 | 3486 | static inline u32 process_indexed_cmd(ctlr_info_t *h, u32 raw_tag) | 
 | 3487 | { | 
 | 3488 | 	u32 tag_index; | 
 | 3489 | 	CommandList_struct *c; | 
 | 3490 |  | 
 | 3491 | 	tag_index = cciss_tag_to_index(raw_tag); | 
 | 3492 | 	if (bad_tag(h, tag_index, raw_tag)) | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3493 | 		return next_command(h); | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3494 | 	c = h->cmd_pool + tag_index; | 
 | 3495 | 	finish_cmd(h, c, raw_tag); | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3496 | 	return next_command(h); | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3497 | } | 
 | 3498 |  | 
 | 3499 | /* process completion of a non-indexed command */ | 
 | 3500 | static inline u32 process_nonindexed_cmd(ctlr_info_t *h, u32 raw_tag) | 
 | 3501 | { | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3502 | 	CommandList_struct *c = NULL; | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3503 | 	__u32 busaddr_masked, tag_masked; | 
 | 3504 |  | 
| Stephen M. Cameron | 0498cc2 | 2011-03-12 10:02:16 +0100 | [diff] [blame] | 3505 | 	tag_masked = cciss_tag_discard_error_bits(h, raw_tag); | 
| Jens Axboe | e6e1ee9 | 2011-01-10 21:50:33 +0100 | [diff] [blame] | 3506 | 	list_for_each_entry(c, &h->cmpQ, list) { | 
| Stephen M. Cameron | 0498cc2 | 2011-03-12 10:02:16 +0100 | [diff] [blame] | 3507 | 		busaddr_masked = cciss_tag_discard_error_bits(h, c->busaddr); | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3508 | 		if (busaddr_masked == tag_masked) { | 
 | 3509 | 			finish_cmd(h, c, raw_tag); | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3510 | 			return next_command(h); | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3511 | 		} | 
 | 3512 | 	} | 
 | 3513 | 	bad_tag(h, h->nr_cmds + 1, raw_tag); | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3514 | 	return next_command(h); | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3515 | } | 
 | 3516 |  | 
| Stephen M. Cameron | 5afe278 | 2011-05-03 14:53:52 -0500 | [diff] [blame] | 3517 | /* Some controllers, like p400, will give us one interrupt | 
 | 3518 |  * after a soft reset, even if we turned interrupts off. | 
 | 3519 |  * Only need to check for this in the cciss_xxx_discard_completions | 
 | 3520 |  * functions. | 
 | 3521 |  */ | 
 | 3522 | static int ignore_bogus_interrupt(ctlr_info_t *h) | 
 | 3523 | { | 
 | 3524 | 	if (likely(!reset_devices)) | 
 | 3525 | 		return 0; | 
 | 3526 |  | 
 | 3527 | 	if (likely(h->interrupts_enabled)) | 
 | 3528 | 		return 0; | 
 | 3529 |  | 
 | 3530 | 	dev_info(&h->pdev->dev, "Received interrupt while interrupts disabled " | 
 | 3531 | 		"(known firmware bug.)  Ignoring.\n"); | 
 | 3532 |  | 
 | 3533 | 	return 1; | 
 | 3534 | } | 
 | 3535 |  | 
 | 3536 | static irqreturn_t cciss_intx_discard_completions(int irq, void *dev_id) | 
 | 3537 | { | 
 | 3538 | 	ctlr_info_t *h = dev_id; | 
 | 3539 | 	unsigned long flags; | 
 | 3540 | 	u32 raw_tag; | 
 | 3541 |  | 
 | 3542 | 	if (ignore_bogus_interrupt(h)) | 
 | 3543 | 		return IRQ_NONE; | 
 | 3544 |  | 
 | 3545 | 	if (interrupt_not_for_us(h)) | 
 | 3546 | 		return IRQ_NONE; | 
 | 3547 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 3548 | 	while (interrupt_pending(h)) { | 
 | 3549 | 		raw_tag = get_next_completion(h); | 
 | 3550 | 		while (raw_tag != FIFO_EMPTY) | 
 | 3551 | 			raw_tag = next_command(h); | 
 | 3552 | 	} | 
 | 3553 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 3554 | 	return IRQ_HANDLED; | 
 | 3555 | } | 
 | 3556 |  | 
 | 3557 | static irqreturn_t cciss_msix_discard_completions(int irq, void *dev_id) | 
 | 3558 | { | 
 | 3559 | 	ctlr_info_t *h = dev_id; | 
 | 3560 | 	unsigned long flags; | 
 | 3561 | 	u32 raw_tag; | 
 | 3562 |  | 
 | 3563 | 	if (ignore_bogus_interrupt(h)) | 
 | 3564 | 		return IRQ_NONE; | 
 | 3565 |  | 
 | 3566 | 	spin_lock_irqsave(&h->lock, flags); | 
 | 3567 | 	raw_tag = get_next_completion(h); | 
 | 3568 | 	while (raw_tag != FIFO_EMPTY) | 
 | 3569 | 		raw_tag = next_command(h); | 
 | 3570 | 	spin_unlock_irqrestore(&h->lock, flags); | 
 | 3571 | 	return IRQ_HANDLED; | 
 | 3572 | } | 
 | 3573 |  | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3574 | static irqreturn_t do_cciss_intx(int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3575 | { | 
 | 3576 | 	ctlr_info_t *h = dev_id; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3577 | 	unsigned long flags; | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3578 | 	u32 raw_tag; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3579 |  | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 3580 | 	if (interrupt_not_for_us(h)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3581 | 		return IRQ_NONE; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 3582 | 	spin_lock_irqsave(&h->lock, flags); | 
| mike.miller@hp.com | 3da8b71 | 2005-11-04 12:30:37 -0600 | [diff] [blame] | 3583 | 	while (interrupt_pending(h)) { | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3584 | 		raw_tag = get_next_completion(h); | 
 | 3585 | 		while (raw_tag != FIFO_EMPTY) { | 
 | 3586 | 			if (cciss_tag_contains_index(raw_tag)) | 
 | 3587 | 				raw_tag = process_indexed_cmd(h, raw_tag); | 
 | 3588 | 			else | 
 | 3589 | 				raw_tag = process_nonindexed_cmd(h, raw_tag); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3590 | 		} | 
 | 3591 | 	} | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 3592 | 	spin_unlock_irqrestore(&h->lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3593 | 	return IRQ_HANDLED; | 
 | 3594 | } | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3595 |  | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3596 | /* Add a second interrupt handler for MSI/MSI-X mode. In this mode we never | 
 | 3597 |  * check the interrupt pending register because it is not set. | 
 | 3598 |  */ | 
 | 3599 | static irqreturn_t do_cciss_msix_intr(int irq, void *dev_id) | 
 | 3600 | { | 
 | 3601 | 	ctlr_info_t *h = dev_id; | 
 | 3602 | 	unsigned long flags; | 
 | 3603 | 	u32 raw_tag; | 
 | 3604 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 3605 | 	spin_lock_irqsave(&h->lock, flags); | 
| Mike Miller | 0c2b390 | 2010-06-02 12:58:00 -0700 | [diff] [blame] | 3606 | 	raw_tag = get_next_completion(h); | 
 | 3607 | 	while (raw_tag != FIFO_EMPTY) { | 
 | 3608 | 		if (cciss_tag_contains_index(raw_tag)) | 
 | 3609 | 			raw_tag = process_indexed_cmd(h, raw_tag); | 
 | 3610 | 		else | 
 | 3611 | 			raw_tag = process_nonindexed_cmd(h, raw_tag); | 
 | 3612 | 	} | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 3613 | 	spin_unlock_irqrestore(&h->lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3614 | 	return IRQ_HANDLED; | 
 | 3615 | } | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3616 |  | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 3617 | /** | 
 | 3618 |  * add_to_scan_list() - add controller to rescan queue | 
 | 3619 |  * @h:		      Pointer to the controller. | 
 | 3620 |  * | 
 | 3621 |  * Adds the controller to the rescan queue if not already on the queue. | 
 | 3622 |  * | 
 | 3623 |  * returns 1 if added to the queue, 0 if skipped (could be on the | 
 | 3624 |  * queue already, or the controller could be initializing or shutting | 
 | 3625 |  * down). | 
 | 3626 |  **/ | 
 | 3627 | static int add_to_scan_list(struct ctlr_info *h) | 
 | 3628 | { | 
 | 3629 | 	struct ctlr_info *test_h; | 
 | 3630 | 	int found = 0; | 
 | 3631 | 	int ret = 0; | 
 | 3632 |  | 
 | 3633 | 	if (h->busy_initializing) | 
 | 3634 | 		return 0; | 
 | 3635 |  | 
 | 3636 | 	if (!mutex_trylock(&h->busy_shutting_down)) | 
 | 3637 | 		return 0; | 
 | 3638 |  | 
 | 3639 | 	mutex_lock(&scan_mutex); | 
 | 3640 | 	list_for_each_entry(test_h, &scan_q, scan_list) { | 
 | 3641 | 		if (test_h == h) { | 
 | 3642 | 			found = 1; | 
 | 3643 | 			break; | 
 | 3644 | 		} | 
 | 3645 | 	} | 
 | 3646 | 	if (!found && !h->busy_scanning) { | 
 | 3647 | 		INIT_COMPLETION(h->scan_wait); | 
 | 3648 | 		list_add_tail(&h->scan_list, &scan_q); | 
 | 3649 | 		ret = 1; | 
 | 3650 | 	} | 
 | 3651 | 	mutex_unlock(&scan_mutex); | 
 | 3652 | 	mutex_unlock(&h->busy_shutting_down); | 
 | 3653 |  | 
 | 3654 | 	return ret; | 
 | 3655 | } | 
 | 3656 |  | 
 | 3657 | /** | 
 | 3658 |  * remove_from_scan_list() - remove controller from rescan queue | 
 | 3659 |  * @h:			   Pointer to the controller. | 
 | 3660 |  * | 
 | 3661 |  * Removes the controller from the rescan queue if present. Blocks if | 
| Stephen M. Cameron | fd8489c | 2009-11-12 12:49:19 -0600 | [diff] [blame] | 3662 |  * the controller is currently conducting a rescan.  The controller | 
 | 3663 |  * can be in one of three states: | 
 | 3664 |  * 1. Doesn't need a scan | 
 | 3665 |  * 2. On the scan list, but not scanning yet (we remove it) | 
 | 3666 |  * 3. Busy scanning (and not on the list). In this case we want to wait for | 
 | 3667 |  *    the scan to complete to make sure the scanning thread for this | 
 | 3668 |  *    controller is completely idle. | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 3669 |  **/ | 
 | 3670 | static void remove_from_scan_list(struct ctlr_info *h) | 
 | 3671 | { | 
 | 3672 | 	struct ctlr_info *test_h, *tmp_h; | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 3673 |  | 
 | 3674 | 	mutex_lock(&scan_mutex); | 
 | 3675 | 	list_for_each_entry_safe(test_h, tmp_h, &scan_q, scan_list) { | 
| Stephen M. Cameron | fd8489c | 2009-11-12 12:49:19 -0600 | [diff] [blame] | 3676 | 		if (test_h == h) { /* state 2. */ | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 3677 | 			list_del(&h->scan_list); | 
 | 3678 | 			complete_all(&h->scan_wait); | 
 | 3679 | 			mutex_unlock(&scan_mutex); | 
 | 3680 | 			return; | 
 | 3681 | 		} | 
 | 3682 | 	} | 
| Stephen M. Cameron | fd8489c | 2009-11-12 12:49:19 -0600 | [diff] [blame] | 3683 | 	if (h->busy_scanning) { /* state 3. */ | 
 | 3684 | 		mutex_unlock(&scan_mutex); | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 3685 | 		wait_for_completion(&h->scan_wait); | 
| Stephen M. Cameron | fd8489c | 2009-11-12 12:49:19 -0600 | [diff] [blame] | 3686 | 	} else { /* state 1, nothing to do. */ | 
 | 3687 | 		mutex_unlock(&scan_mutex); | 
 | 3688 | 	} | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 3689 | } | 
 | 3690 |  | 
 | 3691 | /** | 
 | 3692 |  * scan_thread() - kernel thread used to rescan controllers | 
 | 3693 |  * @data:	 Ignored. | 
 | 3694 |  * | 
 | 3695 |  * A kernel thread used scan for drive topology changes on | 
 | 3696 |  * controllers. The thread processes only one controller at a time | 
 | 3697 |  * using a queue.  Controllers are added to the queue using | 
 | 3698 |  * add_to_scan_list() and removed from the queue either after done | 
 | 3699 |  * processing or using remove_from_scan_list(). | 
 | 3700 |  * | 
 | 3701 |  * returns 0. | 
 | 3702 |  **/ | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3703 | static int scan_thread(void *data) | 
 | 3704 | { | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 3705 | 	struct ctlr_info *h; | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3706 |  | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 3707 | 	while (1) { | 
 | 3708 | 		set_current_state(TASK_INTERRUPTIBLE); | 
 | 3709 | 		schedule(); | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3710 | 		if (kthread_should_stop()) | 
 | 3711 | 			break; | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 3712 |  | 
 | 3713 | 		while (1) { | 
 | 3714 | 			mutex_lock(&scan_mutex); | 
 | 3715 | 			if (list_empty(&scan_q)) { | 
 | 3716 | 				mutex_unlock(&scan_mutex); | 
 | 3717 | 				break; | 
 | 3718 | 			} | 
 | 3719 |  | 
 | 3720 | 			h = list_entry(scan_q.next, | 
 | 3721 | 				       struct ctlr_info, | 
 | 3722 | 				       scan_list); | 
 | 3723 | 			list_del(&h->scan_list); | 
 | 3724 | 			h->busy_scanning = 1; | 
 | 3725 | 			mutex_unlock(&scan_mutex); | 
 | 3726 |  | 
| Stephen M. Cameron | d06dfbd | 2009-11-12 12:49:50 -0600 | [diff] [blame] | 3727 | 			rebuild_lun_table(h, 0, 0); | 
 | 3728 | 			complete_all(&h->scan_wait); | 
 | 3729 | 			mutex_lock(&scan_mutex); | 
 | 3730 | 			h->busy_scanning = 0; | 
 | 3731 | 			mutex_unlock(&scan_mutex); | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 3732 | 		} | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3733 | 	} | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 3734 |  | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3735 | 	return 0; | 
 | 3736 | } | 
 | 3737 |  | 
 | 3738 | static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c) | 
 | 3739 | { | 
 | 3740 | 	if (c->err_info->SenseInfo[2] != UNIT_ATTENTION) | 
 | 3741 | 		return 0; | 
 | 3742 |  | 
 | 3743 | 	switch (c->err_info->SenseInfo[12]) { | 
 | 3744 | 	case STATE_CHANGED: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3745 | 		dev_warn(&h->pdev->dev, "a state change " | 
 | 3746 | 			"detected, command retried\n"); | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3747 | 		return 1; | 
 | 3748 | 	break; | 
 | 3749 | 	case LUN_FAILED: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3750 | 		dev_warn(&h->pdev->dev, "LUN failure " | 
 | 3751 | 			"detected, action required\n"); | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3752 | 		return 1; | 
 | 3753 | 	break; | 
 | 3754 | 	case REPORT_LUNS_CHANGED: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3755 | 		dev_warn(&h->pdev->dev, "report LUN data changed\n"); | 
| Stephen M. Cameron | da00218 | 2009-11-12 12:49:55 -0600 | [diff] [blame] | 3756 | 	/* | 
 | 3757 | 	 * Here, we could call add_to_scan_list and wake up the scan thread, | 
 | 3758 | 	 * except that it's quite likely that we will get more than one | 
 | 3759 | 	 * REPORT_LUNS_CHANGED condition in quick succession, which means | 
 | 3760 | 	 * that those which occur after the first one will likely happen | 
 | 3761 | 	 * *during* the scan_thread's rescan.  And the rescan code is not | 
 | 3762 | 	 * robust enough to restart in the middle, undoing what it has already | 
 | 3763 | 	 * done, and it's not clear that it's even possible to do this, since | 
 | 3764 | 	 * part of what it does is notify the block layer, which starts | 
 | 3765 | 	 * doing it's own i/o to read partition tables and so on, and the | 
 | 3766 | 	 * driver doesn't have visibility to know what might need undoing. | 
 | 3767 | 	 * In any event, if possible, it is horribly complicated to get right | 
 | 3768 | 	 * so we just don't do it for now. | 
 | 3769 | 	 * | 
 | 3770 | 	 * Note: this REPORT_LUNS_CHANGED condition only occurs on the MSA2012. | 
 | 3771 | 	 */ | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3772 | 		return 1; | 
 | 3773 | 	break; | 
 | 3774 | 	case POWER_OR_RESET: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3775 | 		dev_warn(&h->pdev->dev, | 
 | 3776 | 			"a power on or device reset detected\n"); | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3777 | 		return 1; | 
 | 3778 | 	break; | 
 | 3779 | 	case UNIT_ATTENTION_CLEARED: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3780 | 		dev_warn(&h->pdev->dev, | 
 | 3781 | 			"unit attention cleared by another initiator\n"); | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3782 | 		return 1; | 
 | 3783 | 	break; | 
 | 3784 | 	default: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3785 | 		dev_warn(&h->pdev->dev, "unknown unit attention detected\n"); | 
 | 3786 | 		return 1; | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 3787 | 	} | 
 | 3788 | } | 
 | 3789 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3790 | /* | 
| Bjorn Helgaas | d14c4ab | 2006-06-25 05:49:04 -0700 | [diff] [blame] | 3791 |  *  We cannot read the structure directly, for portability we must use | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3792 |  *   the io functions. | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3793 |  *   This is for debug only. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3794 |  */ | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3795 | static void print_cfg_table(ctlr_info_t *h) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3796 | { | 
 | 3797 | 	int i; | 
 | 3798 | 	char temp_name[17]; | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3799 | 	CfgTable_struct *tb = h->cfgtable; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3800 |  | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3801 | 	dev_dbg(&h->pdev->dev, "Controller Configuration information\n"); | 
 | 3802 | 	dev_dbg(&h->pdev->dev, "------------------------------------\n"); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3803 | 	for (i = 0; i < 4; i++) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3804 | 		temp_name[i] = readb(&(tb->Signature[i])); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3805 | 	temp_name[4] = '\0'; | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3806 | 	dev_dbg(&h->pdev->dev, "   Signature = %s\n", temp_name); | 
 | 3807 | 	dev_dbg(&h->pdev->dev, "   Spec Number = %d\n", | 
 | 3808 | 		readl(&(tb->SpecValence))); | 
 | 3809 | 	dev_dbg(&h->pdev->dev, "   Transport methods supported = 0x%x\n", | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3810 | 	       readl(&(tb->TransportSupport))); | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3811 | 	dev_dbg(&h->pdev->dev, "   Transport methods active = 0x%x\n", | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3812 | 	       readl(&(tb->TransportActive))); | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3813 | 	dev_dbg(&h->pdev->dev, "   Requested transport Method = 0x%x\n", | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3814 | 	       readl(&(tb->HostWrite.TransportRequest))); | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3815 | 	dev_dbg(&h->pdev->dev, "   Coalesce Interrupt Delay = 0x%x\n", | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3816 | 	       readl(&(tb->HostWrite.CoalIntDelay))); | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3817 | 	dev_dbg(&h->pdev->dev, "   Coalesce Interrupt Count = 0x%x\n", | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3818 | 	       readl(&(tb->HostWrite.CoalIntCount))); | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3819 | 	dev_dbg(&h->pdev->dev, "   Max outstanding commands = 0x%d\n", | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3820 | 	       readl(&(tb->CmdsOutMax))); | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3821 | 	dev_dbg(&h->pdev->dev, "   Bus Types = 0x%x\n", | 
 | 3822 | 		readl(&(tb->BusTypes))); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3823 | 	for (i = 0; i < 16; i++) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3824 | 		temp_name[i] = readb(&(tb->ServerName[i])); | 
 | 3825 | 	temp_name[16] = '\0'; | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3826 | 	dev_dbg(&h->pdev->dev, "   Server Name = %s\n", temp_name); | 
 | 3827 | 	dev_dbg(&h->pdev->dev, "   Heartbeat Counter = 0x%x\n\n\n", | 
 | 3828 | 		readl(&(tb->HeartBeat))); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3829 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3830 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3831 | static int find_PCI_BAR_index(struct pci_dev *pdev, unsigned long pci_bar_addr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3832 | { | 
 | 3833 | 	int i, offset, mem_type, bar_type; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3834 | 	if (pci_bar_addr == PCI_BASE_ADDRESS_0)	/* looking for BAR zero? */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3835 | 		return 0; | 
 | 3836 | 	offset = 0; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3837 | 	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | 
 | 3838 | 		bar_type = pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3839 | 		if (bar_type == PCI_BASE_ADDRESS_SPACE_IO) | 
 | 3840 | 			offset += 4; | 
 | 3841 | 		else { | 
 | 3842 | 			mem_type = pci_resource_flags(pdev, i) & | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3843 | 			    PCI_BASE_ADDRESS_MEM_TYPE_MASK; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3844 | 			switch (mem_type) { | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3845 | 			case PCI_BASE_ADDRESS_MEM_TYPE_32: | 
 | 3846 | 			case PCI_BASE_ADDRESS_MEM_TYPE_1M: | 
 | 3847 | 				offset += 4;	/* 32 bit */ | 
 | 3848 | 				break; | 
 | 3849 | 			case PCI_BASE_ADDRESS_MEM_TYPE_64: | 
 | 3850 | 				offset += 8; | 
 | 3851 | 				break; | 
 | 3852 | 			default:	/* reserved in PCI 2.2 */ | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3853 | 				dev_warn(&pdev->dev, | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3854 | 				       "Base address is invalid\n"); | 
 | 3855 | 				return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3856 | 				break; | 
 | 3857 | 			} | 
 | 3858 | 		} | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 3859 | 		if (offset == pci_bar_addr - PCI_BASE_ADDRESS_0) | 
 | 3860 | 			return i + 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3861 | 	} | 
 | 3862 | 	return -1; | 
 | 3863 | } | 
 | 3864 |  | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3865 | /* Fill in bucket_map[], given nsgs (the max number of | 
 | 3866 |  * scatter gather elements supported) and bucket[], | 
 | 3867 |  * which is an array of 8 integers.  The bucket[] array | 
 | 3868 |  * contains 8 different DMA transfer sizes (in 16 | 
 | 3869 |  * byte increments) which the controller uses to fetch | 
 | 3870 |  * commands.  This function fills in bucket_map[], which | 
 | 3871 |  * maps a given number of scatter gather elements to one of | 
 | 3872 |  * the 8 DMA transfer sizes.  The point of it is to allow the | 
 | 3873 |  * controller to only do as much DMA as needed to fetch the | 
 | 3874 |  * command, with the DMA transfer size encoded in the lower | 
 | 3875 |  * bits of the command address. | 
 | 3876 |  */ | 
 | 3877 | static void  calc_bucket_map(int bucket[], int num_buckets, | 
 | 3878 | 	int nsgs, int *bucket_map) | 
 | 3879 | { | 
 | 3880 | 	int i, j, b, size; | 
 | 3881 |  | 
 | 3882 | 	/* even a command with 0 SGs requires 4 blocks */ | 
 | 3883 | #define MINIMUM_TRANSFER_BLOCKS 4 | 
 | 3884 | #define NUM_BUCKETS 8 | 
 | 3885 | 	/* Note, bucket_map must have nsgs+1 entries. */ | 
 | 3886 | 	for (i = 0; i <= nsgs; i++) { | 
 | 3887 | 		/* Compute size of a command with i SG entries */ | 
 | 3888 | 		size = i + MINIMUM_TRANSFER_BLOCKS; | 
 | 3889 | 		b = num_buckets; /* Assume the biggest bucket */ | 
 | 3890 | 		/* Find the bucket that is just big enough */ | 
 | 3891 | 		for (j = 0; j < 8; j++) { | 
 | 3892 | 			if (bucket[j] >= size) { | 
 | 3893 | 				b = j; | 
 | 3894 | 				break; | 
 | 3895 | 			} | 
 | 3896 | 		} | 
 | 3897 | 		/* for a command with i SG entries, use bucket b. */ | 
 | 3898 | 		bucket_map[i] = b; | 
 | 3899 | 	} | 
 | 3900 | } | 
 | 3901 |  | 
| Stephen M. Cameron | 0f8a6a1 | 2010-07-19 13:46:01 -0500 | [diff] [blame] | 3902 | static void __devinit cciss_wait_for_mode_change_ack(ctlr_info_t *h) | 
 | 3903 | { | 
 | 3904 | 	int i; | 
 | 3905 |  | 
 | 3906 | 	/* under certain very rare conditions, this can take awhile. | 
 | 3907 | 	 * (e.g.: hot replace a failed 144GB drive in a RAID 5 set right | 
 | 3908 | 	 * as we enter this code.) */ | 
 | 3909 | 	for (i = 0; i < MAX_CONFIG_WAIT; i++) { | 
 | 3910 | 		if (!(readl(h->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq)) | 
 | 3911 | 			break; | 
| Stephen M. Cameron | 332c2f8 | 2010-10-22 14:21:22 -0500 | [diff] [blame] | 3912 | 		usleep_range(10000, 20000); | 
| Stephen M. Cameron | 0f8a6a1 | 2010-07-19 13:46:01 -0500 | [diff] [blame] | 3913 | 	} | 
 | 3914 | } | 
 | 3915 |  | 
| Stephen M. Cameron | 0498cc2 | 2011-03-12 10:02:16 +0100 | [diff] [blame] | 3916 | static __devinit void cciss_enter_performant_mode(ctlr_info_t *h, | 
 | 3917 | 	u32 use_short_tags) | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3918 | { | 
| Stephen M. Cameron | b993313 | 2010-07-19 13:46:07 -0500 | [diff] [blame] | 3919 | 	/* This is a bit complicated.  There are 8 registers on | 
 | 3920 | 	 * the controller which we write to to tell it 8 different | 
 | 3921 | 	 * sizes of commands which there may be.  It's a way of | 
 | 3922 | 	 * reducing the DMA done to fetch each command.  Encoded into | 
 | 3923 | 	 * each command's tag are 3 bits which communicate to the controller | 
 | 3924 | 	 * which of the eight sizes that command fits within.  The size of | 
 | 3925 | 	 * each command depends on how many scatter gather entries there are. | 
 | 3926 | 	 * Each SG entry requires 16 bytes.  The eight registers are programmed | 
 | 3927 | 	 * with the number of 16-byte blocks a command of that size requires. | 
 | 3928 | 	 * The smallest command possible requires 5 such 16 byte blocks. | 
 | 3929 | 	 * the largest command possible requires MAXSGENTRIES + 4 16-byte | 
 | 3930 | 	 * blocks.  Note, this only extends to the SG entries contained | 
 | 3931 | 	 * within the command block, and does not extend to chained blocks | 
 | 3932 | 	 * of SG elements.   bft[] contains the eight values we write to | 
 | 3933 | 	 * the registers.  They are not evenly distributed, but have more | 
 | 3934 | 	 * sizes for small commands, and fewer sizes for larger commands. | 
 | 3935 | 	 */ | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3936 | 	__u32 trans_offset; | 
| Stephen M. Cameron | b993313 | 2010-07-19 13:46:07 -0500 | [diff] [blame] | 3937 | 	int bft[8] = { 5, 6, 8, 10, 12, 20, 28, MAXSGENTRIES + 4}; | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3938 | 			/* | 
 | 3939 | 			 *  5 = 1 s/g entry or 4k | 
 | 3940 | 			 *  6 = 2 s/g entry or 8k | 
 | 3941 | 			 *  8 = 4 s/g entry or 16k | 
 | 3942 | 			 * 10 = 6 s/g entry or 24k | 
 | 3943 | 			 */ | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3944 | 	unsigned long register_value; | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3945 | 	BUILD_BUG_ON(28 > MAXSGENTRIES + 4); | 
 | 3946 |  | 
| Stephen M. Cameron | b993313 | 2010-07-19 13:46:07 -0500 | [diff] [blame] | 3947 | 	h->reply_pool_wraparound = 1; /* spec: init to 1 */ | 
 | 3948 |  | 
 | 3949 | 	/* Controller spec: zero out this buffer. */ | 
 | 3950 | 	memset(h->reply_pool, 0, h->max_commands * sizeof(__u64)); | 
 | 3951 | 	h->reply_pool_head = h->reply_pool; | 
 | 3952 |  | 
 | 3953 | 	trans_offset = readl(&(h->cfgtable->TransMethodOffset)); | 
 | 3954 | 	calc_bucket_map(bft, ARRAY_SIZE(bft), h->maxsgentries, | 
 | 3955 | 				h->blockFetchTable); | 
 | 3956 | 	writel(bft[0], &h->transtable->BlockFetch0); | 
 | 3957 | 	writel(bft[1], &h->transtable->BlockFetch1); | 
 | 3958 | 	writel(bft[2], &h->transtable->BlockFetch2); | 
 | 3959 | 	writel(bft[3], &h->transtable->BlockFetch3); | 
 | 3960 | 	writel(bft[4], &h->transtable->BlockFetch4); | 
 | 3961 | 	writel(bft[5], &h->transtable->BlockFetch5); | 
 | 3962 | 	writel(bft[6], &h->transtable->BlockFetch6); | 
 | 3963 | 	writel(bft[7], &h->transtable->BlockFetch7); | 
 | 3964 |  | 
 | 3965 | 	/* size of controller ring buffer */ | 
 | 3966 | 	writel(h->max_commands, &h->transtable->RepQSize); | 
 | 3967 | 	writel(1, &h->transtable->RepQCount); | 
 | 3968 | 	writel(0, &h->transtable->RepQCtrAddrLow32); | 
 | 3969 | 	writel(0, &h->transtable->RepQCtrAddrHigh32); | 
 | 3970 | 	writel(h->reply_pool_dhandle, &h->transtable->RepQAddr0Low32); | 
 | 3971 | 	writel(0, &h->transtable->RepQAddr0High32); | 
| Stephen M. Cameron | 0498cc2 | 2011-03-12 10:02:16 +0100 | [diff] [blame] | 3972 | 	writel(CFGTBL_Trans_Performant | use_short_tags, | 
| Stephen M. Cameron | b993313 | 2010-07-19 13:46:07 -0500 | [diff] [blame] | 3973 | 			&(h->cfgtable->HostWrite.TransportRequest)); | 
 | 3974 |  | 
 | 3975 | 	writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL); | 
 | 3976 | 	cciss_wait_for_mode_change_ack(h); | 
 | 3977 | 	register_value = readl(&(h->cfgtable->TransportActive)); | 
 | 3978 | 	if (!(register_value & CFGTBL_Trans_Performant)) | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3979 | 		dev_warn(&h->pdev->dev, "cciss: unable to get board into" | 
| Stephen M. Cameron | b993313 | 2010-07-19 13:46:07 -0500 | [diff] [blame] | 3980 | 					" performant mode\n"); | 
 | 3981 | } | 
 | 3982 |  | 
 | 3983 | static void __devinit cciss_put_controller_into_performant_mode(ctlr_info_t *h) | 
 | 3984 | { | 
 | 3985 | 	__u32 trans_support; | 
 | 3986 |  | 
| Stephen M. Cameron | ff5f58f | 2010-07-19 13:45:51 -0500 | [diff] [blame] | 3987 | 	dev_dbg(&h->pdev->dev, "Trying to put board into Performant mode\n"); | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3988 | 	/* Attempt to put controller into performant mode if supported */ | 
 | 3989 | 	/* Does board support performant mode? */ | 
 | 3990 | 	trans_support = readl(&(h->cfgtable->TransportSupport)); | 
 | 3991 | 	if (!(trans_support & PERFORMANT_MODE)) | 
 | 3992 | 		return; | 
 | 3993 |  | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 3994 | 	dev_dbg(&h->pdev->dev, "Placing controller into performant mode\n"); | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 3995 | 	/* Performant mode demands commands on a 32 byte boundary | 
 | 3996 | 	 * pci_alloc_consistent aligns on page boundarys already. | 
 | 3997 | 	 * Just need to check if divisible by 32 | 
 | 3998 | 	 */ | 
 | 3999 | 	if ((sizeof(CommandList_struct) % 32) != 0) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4000 | 		dev_warn(&h->pdev->dev, "%s %d %s\n", | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 4001 | 			"cciss info: command size[", | 
 | 4002 | 			(int)sizeof(CommandList_struct), | 
 | 4003 | 			"] not divisible by 32, no performant mode..\n"); | 
 | 4004 | 		return; | 
 | 4005 | 	} | 
 | 4006 |  | 
 | 4007 | 	/* Performant mode ring buffer and supporting data structures */ | 
 | 4008 | 	h->reply_pool = (__u64 *)pci_alloc_consistent( | 
 | 4009 | 		h->pdev, h->max_commands * sizeof(__u64), | 
 | 4010 | 		&(h->reply_pool_dhandle)); | 
 | 4011 |  | 
 | 4012 | 	/* Need a block fetch table for performant mode */ | 
 | 4013 | 	h->blockFetchTable = kmalloc(((h->maxsgentries+1) * | 
 | 4014 | 		sizeof(__u32)), GFP_KERNEL); | 
 | 4015 |  | 
 | 4016 | 	if ((h->reply_pool == NULL) || (h->blockFetchTable == NULL)) | 
 | 4017 | 		goto clean_up; | 
 | 4018 |  | 
| Stephen M. Cameron | 0498cc2 | 2011-03-12 10:02:16 +0100 | [diff] [blame] | 4019 | 	cciss_enter_performant_mode(h, | 
 | 4020 | 		trans_support & CFGTBL_Trans_use_short_tags); | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 4021 |  | 
 | 4022 | 	/* Change the access methods to the performant access methods */ | 
 | 4023 | 	h->access = SA5_performant_access; | 
| Stephen M. Cameron | b993313 | 2010-07-19 13:46:07 -0500 | [diff] [blame] | 4024 | 	h->transMethod = CFGTBL_Trans_Performant; | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 4025 |  | 
 | 4026 | 	return; | 
 | 4027 | clean_up: | 
 | 4028 | 	kfree(h->blockFetchTable); | 
 | 4029 | 	if (h->reply_pool) | 
 | 4030 | 		pci_free_consistent(h->pdev, | 
 | 4031 | 				h->max_commands * sizeof(__u64), | 
 | 4032 | 				h->reply_pool, | 
 | 4033 | 				h->reply_pool_dhandle); | 
 | 4034 | 	return; | 
 | 4035 |  | 
 | 4036 | } /* cciss_put_controller_into_performant_mode */ | 
 | 4037 |  | 
| Mike Miller | fb86a35 | 2006-01-08 01:03:50 -0800 | [diff] [blame] | 4038 | /* If MSI/MSI-X is supported by the kernel we will try to enable it on | 
 | 4039 |  * controllers that are capable. If not, we use IO-APIC mode. | 
 | 4040 |  */ | 
 | 4041 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4042 | static void __devinit cciss_interrupt_mode(ctlr_info_t *h) | 
| Mike Miller | fb86a35 | 2006-01-08 01:03:50 -0800 | [diff] [blame] | 4043 | { | 
 | 4044 | #ifdef CONFIG_PCI_MSI | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4045 | 	int err; | 
 | 4046 | 	struct msix_entry cciss_msix_entries[4] = { {0, 0}, {0, 1}, | 
 | 4047 | 	{0, 2}, {0, 3} | 
 | 4048 | 	}; | 
| Mike Miller | fb86a35 | 2006-01-08 01:03:50 -0800 | [diff] [blame] | 4049 |  | 
 | 4050 | 	/* Some boards advertise MSI but don't really support it */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4051 | 	if ((h->board_id == 0x40700E11) || (h->board_id == 0x40800E11) || | 
 | 4052 | 	    (h->board_id == 0x40820E11) || (h->board_id == 0x40830E11)) | 
| Mike Miller | fb86a35 | 2006-01-08 01:03:50 -0800 | [diff] [blame] | 4053 | 		goto default_int_mode; | 
 | 4054 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4055 | 	if (pci_find_capability(h->pdev, PCI_CAP_ID_MSIX)) { | 
 | 4056 | 		err = pci_enable_msix(h->pdev, cciss_msix_entries, 4); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4057 | 		if (!err) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4058 | 			h->intr[0] = cciss_msix_entries[0].vector; | 
 | 4059 | 			h->intr[1] = cciss_msix_entries[1].vector; | 
 | 4060 | 			h->intr[2] = cciss_msix_entries[2].vector; | 
 | 4061 | 			h->intr[3] = cciss_msix_entries[3].vector; | 
 | 4062 | 			h->msix_vector = 1; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4063 | 			return; | 
 | 4064 | 		} | 
 | 4065 | 		if (err > 0) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4066 | 			dev_warn(&h->pdev->dev, | 
 | 4067 | 				"only %d MSI-X vectors available\n", err); | 
| Mike Miller | 1ecb9c0 | 2006-12-06 20:35:13 -0800 | [diff] [blame] | 4068 | 			goto default_int_mode; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4069 | 		} else { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4070 | 			dev_warn(&h->pdev->dev, | 
 | 4071 | 				"MSI-X init failed %d\n", err); | 
| Mike Miller | 1ecb9c0 | 2006-12-06 20:35:13 -0800 | [diff] [blame] | 4072 | 			goto default_int_mode; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4073 | 		} | 
 | 4074 | 	} | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4075 | 	if (pci_find_capability(h->pdev, PCI_CAP_ID_MSI)) { | 
 | 4076 | 		if (!pci_enable_msi(h->pdev)) | 
 | 4077 | 			h->msi_vector = 1; | 
 | 4078 | 		else | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4079 | 			dev_warn(&h->pdev->dev, "MSI init failed\n"); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4080 | 	} | 
| Mike Miller | 1ecb9c0 | 2006-12-06 20:35:13 -0800 | [diff] [blame] | 4081 | default_int_mode: | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4082 | #endif				/* CONFIG_PCI_MSI */ | 
| Mike Miller | fb86a35 | 2006-01-08 01:03:50 -0800 | [diff] [blame] | 4083 | 	/* if we get here we're going to use the default interrupt mode */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4084 | 	h->intr[PERF_MODE_INT] = h->pdev->irq; | 
| Mike Miller | fb86a35 | 2006-01-08 01:03:50 -0800 | [diff] [blame] | 4085 | 	return; | 
 | 4086 | } | 
 | 4087 |  | 
| Stephen M. Cameron | 6539fa9 | 2010-07-19 13:44:55 -0500 | [diff] [blame] | 4088 | static int __devinit cciss_lookup_board_id(struct pci_dev *pdev, u32 *board_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4089 | { | 
| Stephen M. Cameron | 6539fa9 | 2010-07-19 13:44:55 -0500 | [diff] [blame] | 4090 | 	int i; | 
 | 4091 | 	u32 subsystem_vendor_id, subsystem_device_id; | 
| Stephen M. Cameron | 2ec24ff | 2009-10-13 09:18:22 +0200 | [diff] [blame] | 4092 |  | 
 | 4093 | 	subsystem_vendor_id = pdev->subsystem_vendor; | 
 | 4094 | 	subsystem_device_id = pdev->subsystem_device; | 
| Stephen M. Cameron | 6539fa9 | 2010-07-19 13:44:55 -0500 | [diff] [blame] | 4095 | 	*board_id = ((subsystem_device_id << 16) & 0xffff0000) | | 
 | 4096 | 			subsystem_vendor_id; | 
| Stephen M. Cameron | 2ec24ff | 2009-10-13 09:18:22 +0200 | [diff] [blame] | 4097 |  | 
| Stephen M. Cameron | 4205df3 | 2010-10-23 18:47:31 +0200 | [diff] [blame] | 4098 | 	for (i = 0; i < ARRAY_SIZE(products); i++) | 
| Stephen M. Cameron | 6539fa9 | 2010-07-19 13:44:55 -0500 | [diff] [blame] | 4099 | 		if (*board_id == products[i].board_id) | 
 | 4100 | 			return i; | 
| Stephen M. Cameron | 6539fa9 | 2010-07-19 13:44:55 -0500 | [diff] [blame] | 4101 | 	dev_warn(&pdev->dev, "unrecognized board ID: 0x%08x, ignoring.\n", | 
 | 4102 | 		*board_id); | 
 | 4103 | 	return -ENODEV; | 
 | 4104 | } | 
 | 4105 |  | 
| Stephen M. Cameron | dd9c426 | 2010-07-19 13:45:00 -0500 | [diff] [blame] | 4106 | static inline bool cciss_board_disabled(ctlr_info_t *h) | 
 | 4107 | { | 
 | 4108 | 	u16 command; | 
 | 4109 |  | 
 | 4110 | 	(void) pci_read_config_word(h->pdev, PCI_COMMAND, &command); | 
 | 4111 | 	return ((command & PCI_COMMAND_MEMORY) == 0); | 
 | 4112 | } | 
 | 4113 |  | 
| Stephen M. Cameron | d474830 | 2010-07-19 13:45:10 -0500 | [diff] [blame] | 4114 | static int __devinit cciss_pci_find_memory_BAR(struct pci_dev *pdev, | 
 | 4115 | 	unsigned long *memory_bar) | 
 | 4116 | { | 
 | 4117 | 	int i; | 
 | 4118 |  | 
 | 4119 | 	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) | 
 | 4120 | 		if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { | 
 | 4121 | 			/* addressing mode bits already removed */ | 
 | 4122 | 			*memory_bar = pci_resource_start(pdev, i); | 
 | 4123 | 			dev_dbg(&pdev->dev, "memory BAR = %lx\n", | 
 | 4124 | 				*memory_bar); | 
 | 4125 | 			return 0; | 
 | 4126 | 		} | 
 | 4127 | 	dev_warn(&pdev->dev, "no memory BAR found\n"); | 
 | 4128 | 	return -ENODEV; | 
 | 4129 | } | 
 | 4130 |  | 
| Stephen M. Cameron | afa842f | 2010-10-22 14:21:07 -0500 | [diff] [blame] | 4131 | static int __devinit cciss_wait_for_board_state(struct pci_dev *pdev, | 
 | 4132 | 	void __iomem *vaddr, int wait_for_ready) | 
 | 4133 | #define BOARD_READY 1 | 
 | 4134 | #define BOARD_NOT_READY 0 | 
| Stephen M. Cameron | e99ba13 | 2010-07-19 13:45:15 -0500 | [diff] [blame] | 4135 | { | 
| Stephen M. Cameron | afa842f | 2010-10-22 14:21:07 -0500 | [diff] [blame] | 4136 | 	int i, iterations; | 
| Stephen M. Cameron | e99ba13 | 2010-07-19 13:45:15 -0500 | [diff] [blame] | 4137 | 	u32 scratchpad; | 
 | 4138 |  | 
| Stephen M. Cameron | afa842f | 2010-10-22 14:21:07 -0500 | [diff] [blame] | 4139 | 	if (wait_for_ready) | 
 | 4140 | 		iterations = CCISS_BOARD_READY_ITERATIONS; | 
 | 4141 | 	else | 
 | 4142 | 		iterations = CCISS_BOARD_NOT_READY_ITERATIONS; | 
 | 4143 |  | 
 | 4144 | 	for (i = 0; i < iterations; i++) { | 
 | 4145 | 		scratchpad = readl(vaddr + SA5_SCRATCHPAD_OFFSET); | 
 | 4146 | 		if (wait_for_ready) { | 
 | 4147 | 			if (scratchpad == CCISS_FIRMWARE_READY) | 
 | 4148 | 				return 0; | 
 | 4149 | 		} else { | 
 | 4150 | 			if (scratchpad != CCISS_FIRMWARE_READY) | 
 | 4151 | 				return 0; | 
 | 4152 | 		} | 
| Stephen M. Cameron | e99ba13 | 2010-07-19 13:45:15 -0500 | [diff] [blame] | 4153 | 		msleep(CCISS_BOARD_READY_POLL_INTERVAL_MSECS); | 
 | 4154 | 	} | 
| Stephen M. Cameron | afa842f | 2010-10-22 14:21:07 -0500 | [diff] [blame] | 4155 | 	dev_warn(&pdev->dev, "board not ready, timed out.\n"); | 
| Stephen M. Cameron | e99ba13 | 2010-07-19 13:45:15 -0500 | [diff] [blame] | 4156 | 	return -ENODEV; | 
 | 4157 | } | 
 | 4158 |  | 
| Stephen M. Cameron | 8e93bf6 | 2010-07-19 13:46:12 -0500 | [diff] [blame] | 4159 | static int __devinit cciss_find_cfg_addrs(struct pci_dev *pdev, | 
 | 4160 | 	void __iomem *vaddr, u32 *cfg_base_addr, u64 *cfg_base_addr_index, | 
 | 4161 | 	u64 *cfg_offset) | 
 | 4162 | { | 
 | 4163 | 	*cfg_base_addr = readl(vaddr + SA5_CTCFG_OFFSET); | 
 | 4164 | 	*cfg_offset = readl(vaddr + SA5_CTMEM_OFFSET); | 
 | 4165 | 	*cfg_base_addr &= (u32) 0x0000ffff; | 
 | 4166 | 	*cfg_base_addr_index = find_PCI_BAR_index(pdev, *cfg_base_addr); | 
 | 4167 | 	if (*cfg_base_addr_index == -1) { | 
 | 4168 | 		dev_warn(&pdev->dev, "cannot find cfg_base_addr_index, " | 
 | 4169 | 			"*cfg_base_addr = 0x%08x\n", *cfg_base_addr); | 
| Stephen M. Cameron | 2ec24ff | 2009-10-13 09:18:22 +0200 | [diff] [blame] | 4170 | 		return -ENODEV; | 
 | 4171 | 	} | 
| Stephen M. Cameron | 8e93bf6 | 2010-07-19 13:46:12 -0500 | [diff] [blame] | 4172 | 	return 0; | 
 | 4173 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4174 |  | 
| Stephen M. Cameron | 4809d09 | 2010-07-19 13:45:21 -0500 | [diff] [blame] | 4175 | static int __devinit cciss_find_cfgtables(ctlr_info_t *h) | 
 | 4176 | { | 
 | 4177 | 	u64 cfg_offset; | 
 | 4178 | 	u32 cfg_base_addr; | 
 | 4179 | 	u64 cfg_base_addr_index; | 
 | 4180 | 	u32 trans_offset; | 
| Stephen M. Cameron | 8e93bf6 | 2010-07-19 13:46:12 -0500 | [diff] [blame] | 4181 | 	int rc; | 
| Stephen M. Cameron | 4809d09 | 2010-07-19 13:45:21 -0500 | [diff] [blame] | 4182 |  | 
| Stephen M. Cameron | 8e93bf6 | 2010-07-19 13:46:12 -0500 | [diff] [blame] | 4183 | 	rc = cciss_find_cfg_addrs(h->pdev, h->vaddr, &cfg_base_addr, | 
 | 4184 | 		&cfg_base_addr_index, &cfg_offset); | 
 | 4185 | 	if (rc) | 
 | 4186 | 		return rc; | 
| Stephen M. Cameron | 4809d09 | 2010-07-19 13:45:21 -0500 | [diff] [blame] | 4187 | 	h->cfgtable = remap_pci_mem(pci_resource_start(h->pdev, | 
| Stephen M. Cameron | 8e93bf6 | 2010-07-19 13:46:12 -0500 | [diff] [blame] | 4188 | 		cfg_base_addr_index) + cfg_offset, sizeof(h->cfgtable)); | 
| Stephen M. Cameron | 4809d09 | 2010-07-19 13:45:21 -0500 | [diff] [blame] | 4189 | 	if (!h->cfgtable) | 
 | 4190 | 		return -ENOMEM; | 
| Stephen M. Cameron | 62710ae | 2011-05-03 14:53:00 -0500 | [diff] [blame] | 4191 | 	rc = write_driver_ver_to_cfgtable(h->cfgtable); | 
 | 4192 | 	if (rc) | 
 | 4193 | 		return rc; | 
| Stephen M. Cameron | 4809d09 | 2010-07-19 13:45:21 -0500 | [diff] [blame] | 4194 | 	/* Find performant mode table. */ | 
| Stephen M. Cameron | 8e93bf6 | 2010-07-19 13:46:12 -0500 | [diff] [blame] | 4195 | 	trans_offset = readl(&h->cfgtable->TransMethodOffset); | 
| Stephen M. Cameron | 4809d09 | 2010-07-19 13:45:21 -0500 | [diff] [blame] | 4196 | 	h->transtable = remap_pci_mem(pci_resource_start(h->pdev, | 
 | 4197 | 				cfg_base_addr_index)+cfg_offset+trans_offset, | 
 | 4198 | 				sizeof(*h->transtable)); | 
 | 4199 | 	if (!h->transtable) | 
 | 4200 | 		return -ENOMEM; | 
 | 4201 | 	return 0; | 
 | 4202 | } | 
 | 4203 |  | 
| Stephen M. Cameron | adfbc1f | 2010-07-19 13:46:28 -0500 | [diff] [blame] | 4204 | static void __devinit cciss_get_max_perf_mode_cmds(struct ctlr_info *h) | 
 | 4205 | { | 
 | 4206 | 	h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands)); | 
| Stephen M. Cameron | 186fb9c | 2010-10-22 14:21:17 -0500 | [diff] [blame] | 4207 |  | 
 | 4208 | 	/* Limit commands in memory limited kdump scenario. */ | 
 | 4209 | 	if (reset_devices && h->max_commands > 32) | 
 | 4210 | 		h->max_commands = 32; | 
 | 4211 |  | 
| Stephen M. Cameron | adfbc1f | 2010-07-19 13:46:28 -0500 | [diff] [blame] | 4212 | 	if (h->max_commands < 16) { | 
 | 4213 | 		dev_warn(&h->pdev->dev, "Controller reports " | 
 | 4214 | 			"max supported commands of %d, an obvious lie. " | 
 | 4215 | 			"Using 16.  Ensure that firmware is up to date.\n", | 
 | 4216 | 			h->max_commands); | 
 | 4217 | 		h->max_commands = 16; | 
 | 4218 | 	} | 
 | 4219 | } | 
 | 4220 |  | 
| Stephen M. Cameron | afadbf4 | 2010-07-19 13:45:31 -0500 | [diff] [blame] | 4221 | /* Interrogate the hardware for some limits: | 
 | 4222 |  * max commands, max SG elements without chaining, and with chaining, | 
 | 4223 |  * SG chain block size, etc. | 
 | 4224 |  */ | 
 | 4225 | static void __devinit cciss_find_board_params(ctlr_info_t *h) | 
 | 4226 | { | 
| Stephen M. Cameron | adfbc1f | 2010-07-19 13:46:28 -0500 | [diff] [blame] | 4227 | 	cciss_get_max_perf_mode_cmds(h); | 
| Stephen M. Cameron | 8a4ec67 | 2011-05-03 14:54:12 -0500 | [diff] [blame] | 4228 | 	h->nr_cmds = h->max_commands - 4 - cciss_tape_cmds; | 
| Stephen M. Cameron | afadbf4 | 2010-07-19 13:45:31 -0500 | [diff] [blame] | 4229 | 	h->maxsgentries = readl(&(h->cfgtable->MaxSGElements)); | 
 | 4230 | 	/* | 
 | 4231 | 	 * Limit in-command s/g elements to 32 save dma'able memory. | 
 | 4232 | 	 * Howvever spec says if 0, use 31 | 
 | 4233 | 	 */ | 
 | 4234 | 	h->max_cmd_sgentries = 31; | 
 | 4235 | 	if (h->maxsgentries > 512) { | 
 | 4236 | 		h->max_cmd_sgentries = 32; | 
 | 4237 | 		h->chainsize = h->maxsgentries - h->max_cmd_sgentries + 1; | 
 | 4238 | 		h->maxsgentries--; /* save one for chain pointer */ | 
 | 4239 | 	} else { | 
 | 4240 | 		h->maxsgentries = 31; /* default to traditional values */ | 
 | 4241 | 		h->chainsize = 0; | 
 | 4242 | 	} | 
 | 4243 | } | 
 | 4244 |  | 
| Stephen M. Cameron | 501b92c | 2010-07-19 13:45:36 -0500 | [diff] [blame] | 4245 | static inline bool CISS_signature_present(ctlr_info_t *h) | 
 | 4246 | { | 
 | 4247 | 	if ((readb(&h->cfgtable->Signature[0]) != 'C') || | 
 | 4248 | 	    (readb(&h->cfgtable->Signature[1]) != 'I') || | 
 | 4249 | 	    (readb(&h->cfgtable->Signature[2]) != 'S') || | 
 | 4250 | 	    (readb(&h->cfgtable->Signature[3]) != 'S')) { | 
 | 4251 | 		dev_warn(&h->pdev->dev, "not a valid CISS config table\n"); | 
 | 4252 | 		return false; | 
 | 4253 | 	} | 
 | 4254 | 	return true; | 
 | 4255 | } | 
 | 4256 |  | 
| Stephen M. Cameron | 322e304 | 2010-07-19 13:45:41 -0500 | [diff] [blame] | 4257 | /* Need to enable prefetch in the SCSI core for 6400 in x86 */ | 
 | 4258 | static inline void cciss_enable_scsi_prefetch(ctlr_info_t *h) | 
 | 4259 | { | 
 | 4260 | #ifdef CONFIG_X86 | 
 | 4261 | 	u32 prefetch; | 
 | 4262 |  | 
 | 4263 | 	prefetch = readl(&(h->cfgtable->SCSI_Prefetch)); | 
 | 4264 | 	prefetch |= 0x100; | 
 | 4265 | 	writel(prefetch, &(h->cfgtable->SCSI_Prefetch)); | 
 | 4266 | #endif | 
 | 4267 | } | 
 | 4268 |  | 
| Stephen M. Cameron | bfd63ee | 2010-07-19 13:45:46 -0500 | [diff] [blame] | 4269 | /* Disable DMA prefetch for the P600.  Otherwise an ASIC bug may result | 
 | 4270 |  * in a prefetch beyond physical memory. | 
 | 4271 |  */ | 
 | 4272 | static inline void cciss_p600_dma_prefetch_quirk(ctlr_info_t *h) | 
 | 4273 | { | 
 | 4274 | 	u32 dma_prefetch; | 
 | 4275 | 	__u32 dma_refetch; | 
 | 4276 |  | 
 | 4277 | 	if (h->board_id != 0x3225103C) | 
 | 4278 | 		return; | 
 | 4279 | 	dma_prefetch = readl(h->vaddr + I2O_DMA1_CFG); | 
 | 4280 | 	dma_prefetch |= 0x8000; | 
 | 4281 | 	writel(dma_prefetch, h->vaddr + I2O_DMA1_CFG); | 
 | 4282 | 	pci_read_config_dword(h->pdev, PCI_COMMAND_PARITY, &dma_refetch); | 
 | 4283 | 	dma_refetch |= 0x1; | 
 | 4284 | 	pci_write_config_dword(h->pdev, PCI_COMMAND_PARITY, dma_refetch); | 
 | 4285 | } | 
 | 4286 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4287 | static int __devinit cciss_pci_init(ctlr_info_t *h) | 
| Stephen M. Cameron | 6539fa9 | 2010-07-19 13:44:55 -0500 | [diff] [blame] | 4288 | { | 
| Stephen M. Cameron | 4809d09 | 2010-07-19 13:45:21 -0500 | [diff] [blame] | 4289 | 	int prod_index, err; | 
| Stephen M. Cameron | 6539fa9 | 2010-07-19 13:44:55 -0500 | [diff] [blame] | 4290 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4291 | 	prod_index = cciss_lookup_board_id(h->pdev, &h->board_id); | 
| Stephen M. Cameron | 6539fa9 | 2010-07-19 13:44:55 -0500 | [diff] [blame] | 4292 | 	if (prod_index < 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4293 | 		return -ENODEV; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4294 | 	h->product_name = products[prod_index].product_name; | 
 | 4295 | 	h->access = *(products[prod_index].access); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4296 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4297 | 	if (cciss_board_disabled(h)) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4298 | 		dev_warn(&h->pdev->dev, "controller appears to be disabled\n"); | 
| Bjorn Helgaas | c33ac89 | 2006-06-25 05:49:01 -0700 | [diff] [blame] | 4299 | 		return -ENODEV; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4300 | 	} | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4301 | 	err = pci_enable_device(h->pdev); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4302 | 	if (err) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4303 | 		dev_warn(&h->pdev->dev, "Unable to Enable PCI device\n"); | 
| Bjorn Helgaas | c33ac89 | 2006-06-25 05:49:01 -0700 | [diff] [blame] | 4304 | 		return err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4305 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4306 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4307 | 	err = pci_request_regions(h->pdev, "cciss"); | 
| Bjorn Helgaas | 4e57030 | 2006-06-25 05:49:02 -0700 | [diff] [blame] | 4308 | 	if (err) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4309 | 		dev_warn(&h->pdev->dev, | 
 | 4310 | 			"Cannot obtain PCI resources, aborting\n"); | 
| Mike Miller | 872225c | 2006-12-13 00:34:22 -0800 | [diff] [blame] | 4311 | 		return err; | 
| Bjorn Helgaas | 4e57030 | 2006-06-25 05:49:02 -0700 | [diff] [blame] | 4312 | 	} | 
 | 4313 |  | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4314 | 	dev_dbg(&h->pdev->dev, "irq = %x\n", h->pdev->irq); | 
 | 4315 | 	dev_dbg(&h->pdev->dev, "board_id = %x\n", h->board_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4316 |  | 
| Mike Miller | fb86a35 | 2006-01-08 01:03:50 -0800 | [diff] [blame] | 4317 | /* If the kernel supports MSI/MSI-X we will try to enable that functionality, | 
 | 4318 |  * else we use the IO-APIC interrupt assigned to us by system ROM. | 
 | 4319 |  */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4320 | 	cciss_interrupt_mode(h); | 
 | 4321 | 	err = cciss_pci_find_memory_BAR(h->pdev, &h->paddr); | 
| Stephen M. Cameron | d474830 | 2010-07-19 13:45:10 -0500 | [diff] [blame] | 4322 | 	if (err) | 
| Mike Miller | e143858 | 2009-04-02 12:50:56 -0700 | [diff] [blame] | 4323 | 		goto err_out_free_res; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4324 | 	h->vaddr = remap_pci_mem(h->paddr, 0x250); | 
 | 4325 | 	if (!h->vaddr) { | 
| Stephen M. Cameron | da55032 | 2010-07-19 13:45:26 -0500 | [diff] [blame] | 4326 | 		err = -ENOMEM; | 
 | 4327 | 		goto err_out_free_res; | 
| Mike Miller | e143858 | 2009-04-02 12:50:56 -0700 | [diff] [blame] | 4328 | 	} | 
| Stephen M. Cameron | afa842f | 2010-10-22 14:21:07 -0500 | [diff] [blame] | 4329 | 	err = cciss_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY); | 
| Stephen M. Cameron | e99ba13 | 2010-07-19 13:45:15 -0500 | [diff] [blame] | 4330 | 	if (err) | 
| Bjorn Helgaas | 4e57030 | 2006-06-25 05:49:02 -0700 | [diff] [blame] | 4331 | 		goto err_out_free_res; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4332 | 	err = cciss_find_cfgtables(h); | 
| Stephen M. Cameron | 4809d09 | 2010-07-19 13:45:21 -0500 | [diff] [blame] | 4333 | 	if (err) | 
| Bjorn Helgaas | 4e57030 | 2006-06-25 05:49:02 -0700 | [diff] [blame] | 4334 | 		goto err_out_free_res; | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4335 | 	print_cfg_table(h); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4336 | 	cciss_find_board_params(h); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4337 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4338 | 	if (!CISS_signature_present(h)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4339 | 		err = -ENODEV; | 
 | 4340 | 		goto err_out_free_res; | 
 | 4341 | 	} | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4342 | 	cciss_enable_scsi_prefetch(h); | 
 | 4343 | 	cciss_p600_dma_prefetch_quirk(h); | 
 | 4344 | 	cciss_put_controller_into_performant_mode(h); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4345 | 	return 0; | 
 | 4346 |  | 
| Linus Torvalds | 5faad62 | 2006-12-13 09:15:34 -0800 | [diff] [blame] | 4347 | err_out_free_res: | 
| Mike Miller | 872225c | 2006-12-13 00:34:22 -0800 | [diff] [blame] | 4348 | 	/* | 
 | 4349 | 	 * Deliberately omit pci_disable_device(): it does something nasty to | 
 | 4350 | 	 * Smart Array controllers that pci_enable_device does not undo | 
 | 4351 | 	 */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4352 | 	if (h->transtable) | 
 | 4353 | 		iounmap(h->transtable); | 
 | 4354 | 	if (h->cfgtable) | 
 | 4355 | 		iounmap(h->cfgtable); | 
 | 4356 | 	if (h->vaddr) | 
 | 4357 | 		iounmap(h->vaddr); | 
 | 4358 | 	pci_release_regions(h->pdev); | 
| Bjorn Helgaas | c33ac89 | 2006-06-25 05:49:01 -0700 | [diff] [blame] | 4359 | 	return err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4360 | } | 
 | 4361 |  | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 4362 | /* Function to find the first free pointer into our hba[] array | 
 | 4363 |  * Returns -1 if no free entries are left. | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4364 |  */ | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4365 | static int alloc_cciss_hba(struct pci_dev *pdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4366 | { | 
| Mike Miller | 799202c | 2006-12-06 20:35:12 -0800 | [diff] [blame] | 4367 | 	int i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4368 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4369 | 	for (i = 0; i < MAX_CTLR; i++) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4370 | 		if (!hba[i]) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4371 | 			ctlr_info_t *h; | 
| Jesper Juhl | f2912a1 | 2007-07-31 00:39:39 -0700 | [diff] [blame] | 4372 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4373 | 			h = kzalloc(sizeof(ctlr_info_t), GFP_KERNEL); | 
 | 4374 | 			if (!h) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4375 | 				goto Enomem; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4376 | 			hba[i] = h; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4377 | 			return i; | 
 | 4378 | 		} | 
 | 4379 | 	} | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4380 | 	dev_warn(&pdev->dev, "This driver supports a maximum" | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4381 | 	       " of %d controllers.\n", MAX_CTLR); | 
| Mike Miller | 799202c | 2006-12-06 20:35:12 -0800 | [diff] [blame] | 4382 | 	return -1; | 
 | 4383 | Enomem: | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4384 | 	dev_warn(&pdev->dev, "out of memory.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4385 | 	return -1; | 
 | 4386 | } | 
 | 4387 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4388 | static void free_hba(ctlr_info_t *h) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4389 | { | 
| Stephen M. Cameron | 2c93559 | 2009-09-17 13:47:50 -0500 | [diff] [blame] | 4390 | 	int i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4391 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4392 | 	hba[h->ctlr] = NULL; | 
| Stephen M. Cameron | 2c93559 | 2009-09-17 13:47:50 -0500 | [diff] [blame] | 4393 | 	for (i = 0; i < h->highest_lun + 1; i++) | 
 | 4394 | 		if (h->gendisk[i] != NULL) | 
 | 4395 | 			put_disk(h->gendisk[i]); | 
 | 4396 | 	kfree(h); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4397 | } | 
 | 4398 |  | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4399 | /* Send a message CDB to the firmware. */ | 
 | 4400 | static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, unsigned char type) | 
 | 4401 | { | 
 | 4402 | 	typedef struct { | 
 | 4403 | 		CommandListHeader_struct CommandHeader; | 
 | 4404 | 		RequestBlock_struct Request; | 
 | 4405 | 		ErrDescriptor_struct ErrorDescriptor; | 
 | 4406 | 	} Command; | 
 | 4407 | 	static const size_t cmd_sz = sizeof(Command) + sizeof(ErrorInfo_struct); | 
 | 4408 | 	Command *cmd; | 
 | 4409 | 	dma_addr_t paddr64; | 
 | 4410 | 	uint32_t paddr32, tag; | 
 | 4411 | 	void __iomem *vaddr; | 
 | 4412 | 	int i, err; | 
 | 4413 |  | 
 | 4414 | 	vaddr = ioremap_nocache(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); | 
 | 4415 | 	if (vaddr == NULL) | 
 | 4416 | 		return -ENOMEM; | 
 | 4417 |  | 
 | 4418 | 	/* The Inbound Post Queue only accepts 32-bit physical addresses for the | 
 | 4419 | 	   CCISS commands, so they must be allocated from the lower 4GiB of | 
 | 4420 | 	   memory. */ | 
| Yang Hongyang | e930438 | 2009-04-13 14:40:14 -0700 | [diff] [blame] | 4421 | 	err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4422 | 	if (err) { | 
 | 4423 | 		iounmap(vaddr); | 
 | 4424 | 		return -ENOMEM; | 
 | 4425 | 	} | 
 | 4426 |  | 
 | 4427 | 	cmd = pci_alloc_consistent(pdev, cmd_sz, &paddr64); | 
 | 4428 | 	if (cmd == NULL) { | 
 | 4429 | 		iounmap(vaddr); | 
 | 4430 | 		return -ENOMEM; | 
 | 4431 | 	} | 
 | 4432 |  | 
 | 4433 | 	/* This must fit, because of the 32-bit consistent DMA mask.  Also, | 
 | 4434 | 	   although there's no guarantee, we assume that the address is at | 
 | 4435 | 	   least 4-byte aligned (most likely, it's page-aligned). */ | 
 | 4436 | 	paddr32 = paddr64; | 
 | 4437 |  | 
 | 4438 | 	cmd->CommandHeader.ReplyQueue = 0; | 
 | 4439 | 	cmd->CommandHeader.SGList = 0; | 
 | 4440 | 	cmd->CommandHeader.SGTotal = 0; | 
 | 4441 | 	cmd->CommandHeader.Tag.lower = paddr32; | 
 | 4442 | 	cmd->CommandHeader.Tag.upper = 0; | 
 | 4443 | 	memset(&cmd->CommandHeader.LUN.LunAddrBytes, 0, 8); | 
 | 4444 |  | 
 | 4445 | 	cmd->Request.CDBLen = 16; | 
 | 4446 | 	cmd->Request.Type.Type = TYPE_MSG; | 
 | 4447 | 	cmd->Request.Type.Attribute = ATTR_HEADOFQUEUE; | 
 | 4448 | 	cmd->Request.Type.Direction = XFER_NONE; | 
 | 4449 | 	cmd->Request.Timeout = 0; /* Don't time out */ | 
 | 4450 | 	cmd->Request.CDB[0] = opcode; | 
 | 4451 | 	cmd->Request.CDB[1] = type; | 
 | 4452 | 	memset(&cmd->Request.CDB[2], 0, 14); /* the rest of the CDB is reserved */ | 
 | 4453 |  | 
 | 4454 | 	cmd->ErrorDescriptor.Addr.lower = paddr32 + sizeof(Command); | 
 | 4455 | 	cmd->ErrorDescriptor.Addr.upper = 0; | 
 | 4456 | 	cmd->ErrorDescriptor.Len = sizeof(ErrorInfo_struct); | 
 | 4457 |  | 
 | 4458 | 	writel(paddr32, vaddr + SA5_REQUEST_PORT_OFFSET); | 
 | 4459 |  | 
 | 4460 | 	for (i = 0; i < 10; i++) { | 
 | 4461 | 		tag = readl(vaddr + SA5_REPLY_PORT_OFFSET); | 
 | 4462 | 		if ((tag & ~3) == paddr32) | 
 | 4463 | 			break; | 
| Stephen M. Cameron | 3e28601 | 2011-05-03 14:53:41 -0500 | [diff] [blame] | 4464 | 		msleep(CCISS_POST_RESET_NOOP_TIMEOUT_MSECS); | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4465 | 	} | 
 | 4466 |  | 
 | 4467 | 	iounmap(vaddr); | 
 | 4468 |  | 
 | 4469 | 	/* we leak the DMA buffer here ... no choice since the controller could | 
 | 4470 | 	   still complete the command. */ | 
 | 4471 | 	if (i == 10) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4472 | 		dev_err(&pdev->dev, | 
 | 4473 | 			"controller message %02x:%02x timed out\n", | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4474 | 			opcode, type); | 
 | 4475 | 		return -ETIMEDOUT; | 
 | 4476 | 	} | 
 | 4477 |  | 
 | 4478 | 	pci_free_consistent(pdev, cmd_sz, cmd, paddr64); | 
 | 4479 |  | 
 | 4480 | 	if (tag & 2) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4481 | 		dev_err(&pdev->dev, "controller message %02x:%02x failed\n", | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4482 | 			opcode, type); | 
 | 4483 | 		return -EIO; | 
 | 4484 | 	} | 
 | 4485 |  | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4486 | 	dev_info(&pdev->dev, "controller message %02x:%02x succeeded\n", | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4487 | 		opcode, type); | 
 | 4488 | 	return 0; | 
 | 4489 | } | 
 | 4490 |  | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4491 | #define cciss_noop(p) cciss_message(p, 3, 0) | 
 | 4492 |  | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4493 | static int cciss_controller_hard_reset(struct pci_dev *pdev, | 
| Stephen M. Cameron | bf2e2e6 | 2011-05-03 14:53:46 -0500 | [diff] [blame] | 4494 | 	void * __iomem vaddr, u32 use_doorbell) | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4495 | { | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4496 | 	u16 pmcsr; | 
 | 4497 | 	int pos; | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4498 |  | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4499 | 	if (use_doorbell) { | 
 | 4500 | 		/* For everything after the P600, the PCI power state method | 
 | 4501 | 		 * of resetting the controller doesn't work, so we have this | 
 | 4502 | 		 * other way using the doorbell register. | 
 | 4503 | 		 */ | 
 | 4504 | 		dev_info(&pdev->dev, "using doorbell to reset controller\n"); | 
| Stephen M. Cameron | bf2e2e6 | 2011-05-03 14:53:46 -0500 | [diff] [blame] | 4505 | 		writel(use_doorbell, vaddr + SA5_DOORBELL); | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4506 | 	} else { /* Try to do it the PCI power state way */ | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4507 |  | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4508 | 		/* Quoting from the Open CISS Specification: "The Power | 
 | 4509 | 		 * Management Control/Status Register (CSR) controls the power | 
 | 4510 | 		 * state of the device.  The normal operating state is D0, | 
 | 4511 | 		 * CSR=00h.  The software off state is D3, CSR=03h.  To reset | 
 | 4512 | 		 * the controller, place the interface device in D3 then to D0, | 
 | 4513 | 		 * this causes a secondary PCI reset which will reset the | 
 | 4514 | 		 * controller." */ | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4515 |  | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4516 | 		pos = pci_find_capability(pdev, PCI_CAP_ID_PM); | 
 | 4517 | 		if (pos == 0) { | 
 | 4518 | 			dev_err(&pdev->dev, | 
 | 4519 | 				"cciss_controller_hard_reset: " | 
 | 4520 | 				"PCI PM not supported\n"); | 
 | 4521 | 			return -ENODEV; | 
 | 4522 | 		} | 
 | 4523 | 		dev_info(&pdev->dev, "using PCI PM to reset controller\n"); | 
 | 4524 | 		/* enter the D3hot power management state */ | 
 | 4525 | 		pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr); | 
 | 4526 | 		pmcsr &= ~PCI_PM_CTRL_STATE_MASK; | 
 | 4527 | 		pmcsr |= PCI_D3hot; | 
 | 4528 | 		pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4529 |  | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4530 | 		msleep(500); | 
 | 4531 |  | 
 | 4532 | 		/* enter the D0 power management state */ | 
 | 4533 | 		pmcsr &= ~PCI_PM_CTRL_STATE_MASK; | 
 | 4534 | 		pmcsr |= PCI_D0; | 
 | 4535 | 		pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr); | 
| Mike Miller | fa7e2a6 | 2011-10-20 22:19:17 +0200 | [diff] [blame] | 4536 |  | 
 | 4537 | 		/* | 
 | 4538 | 		 * The P600 requires a small delay when changing states. | 
 | 4539 | 		 * Otherwise we may think the board did not reset and we bail. | 
 | 4540 | 		 * This for kdump only and is particular to the P600. | 
 | 4541 | 		 */ | 
 | 4542 | 		msleep(500); | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4543 | 	} | 
 | 4544 | 	return 0; | 
 | 4545 | } | 
 | 4546 |  | 
| Stephen M. Cameron | 62710ae | 2011-05-03 14:53:00 -0500 | [diff] [blame] | 4547 | static __devinit void init_driver_version(char *driver_version, int len) | 
 | 4548 | { | 
 | 4549 | 	memset(driver_version, 0, len); | 
 | 4550 | 	strncpy(driver_version, "cciss " DRIVER_NAME, len - 1); | 
 | 4551 | } | 
 | 4552 |  | 
 | 4553 | static __devinit int write_driver_ver_to_cfgtable( | 
 | 4554 | 	CfgTable_struct __iomem *cfgtable) | 
 | 4555 | { | 
 | 4556 | 	char *driver_version; | 
 | 4557 | 	int i, size = sizeof(cfgtable->driver_version); | 
 | 4558 |  | 
 | 4559 | 	driver_version = kmalloc(size, GFP_KERNEL); | 
 | 4560 | 	if (!driver_version) | 
 | 4561 | 		return -ENOMEM; | 
 | 4562 |  | 
 | 4563 | 	init_driver_version(driver_version, size); | 
 | 4564 | 	for (i = 0; i < size; i++) | 
 | 4565 | 		writeb(driver_version[i], &cfgtable->driver_version[i]); | 
 | 4566 | 	kfree(driver_version); | 
 | 4567 | 	return 0; | 
 | 4568 | } | 
 | 4569 |  | 
 | 4570 | static __devinit void read_driver_ver_from_cfgtable( | 
 | 4571 | 	CfgTable_struct __iomem *cfgtable, unsigned char *driver_ver) | 
 | 4572 | { | 
 | 4573 | 	int i; | 
 | 4574 |  | 
 | 4575 | 	for (i = 0; i < sizeof(cfgtable->driver_version); i++) | 
 | 4576 | 		driver_ver[i] = readb(&cfgtable->driver_version[i]); | 
 | 4577 | } | 
 | 4578 |  | 
 | 4579 | static __devinit int controller_reset_failed( | 
 | 4580 | 	CfgTable_struct __iomem *cfgtable) | 
 | 4581 | { | 
 | 4582 |  | 
 | 4583 | 	char *driver_ver, *old_driver_ver; | 
 | 4584 | 	int rc, size = sizeof(cfgtable->driver_version); | 
 | 4585 |  | 
 | 4586 | 	old_driver_ver = kmalloc(2 * size, GFP_KERNEL); | 
 | 4587 | 	if (!old_driver_ver) | 
 | 4588 | 		return -ENOMEM; | 
 | 4589 | 	driver_ver = old_driver_ver + size; | 
 | 4590 |  | 
 | 4591 | 	/* After a reset, the 32 bytes of "driver version" in the cfgtable | 
 | 4592 | 	 * should have been changed, otherwise we know the reset failed. | 
 | 4593 | 	 */ | 
 | 4594 | 	init_driver_version(old_driver_ver, size); | 
 | 4595 | 	read_driver_ver_from_cfgtable(cfgtable, driver_ver); | 
 | 4596 | 	rc = !memcmp(driver_ver, old_driver_ver, size); | 
 | 4597 | 	kfree(old_driver_ver); | 
 | 4598 | 	return rc; | 
 | 4599 | } | 
 | 4600 |  | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4601 | /* This does a hard reset of the controller using PCI power management | 
 | 4602 |  * states or using the doorbell register. */ | 
 | 4603 | static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev) | 
 | 4604 | { | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4605 | 	u64 cfg_offset; | 
 | 4606 | 	u32 cfg_base_addr; | 
 | 4607 | 	u64 cfg_base_addr_index; | 
 | 4608 | 	void __iomem *vaddr; | 
 | 4609 | 	unsigned long paddr; | 
| Stephen M. Cameron | 62710ae | 2011-05-03 14:53:00 -0500 | [diff] [blame] | 4610 | 	u32 misc_fw_support; | 
| Stephen M. Cameron | f442e64 | 2010-10-22 14:21:12 -0500 | [diff] [blame] | 4611 | 	int rc; | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4612 | 	CfgTable_struct __iomem *cfgtable; | 
| Stephen M. Cameron | bf2e2e6 | 2011-05-03 14:53:46 -0500 | [diff] [blame] | 4613 | 	u32 use_doorbell; | 
| Stephen M. Cameron | 058a0f9 | 2010-07-19 13:46:33 -0500 | [diff] [blame] | 4614 | 	u32 board_id; | 
| Stephen M. Cameron | f442e64 | 2010-10-22 14:21:12 -0500 | [diff] [blame] | 4615 | 	u16 command_register; | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4616 |  | 
 | 4617 | 	/* For controllers as old a the p600, this is very nearly | 
 | 4618 | 	 * the same thing as | 
 | 4619 | 	 * | 
 | 4620 | 	 * pci_save_state(pci_dev); | 
 | 4621 | 	 * pci_set_power_state(pci_dev, PCI_D3hot); | 
 | 4622 | 	 * pci_set_power_state(pci_dev, PCI_D0); | 
 | 4623 | 	 * pci_restore_state(pci_dev); | 
 | 4624 | 	 * | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4625 | 	 * For controllers newer than the P600, the pci power state | 
 | 4626 | 	 * method of resetting doesn't work so we have another way | 
 | 4627 | 	 * using the doorbell register. | 
 | 4628 | 	 */ | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4629 |  | 
| Stephen M. Cameron | 058a0f9 | 2010-07-19 13:46:33 -0500 | [diff] [blame] | 4630 | 	/* Exclude 640x boards.  These are two pci devices in one slot | 
 | 4631 | 	 * which share a battery backed cache module.  One controls the | 
 | 4632 | 	 * cache, the other accesses the cache through the one that controls | 
 | 4633 | 	 * it.  If we reset the one controlling the cache, the other will | 
 | 4634 | 	 * likely not be happy.  Just forbid resetting this conjoined mess. | 
 | 4635 | 	 */ | 
 | 4636 | 	cciss_lookup_board_id(pdev, &board_id); | 
| Stephen M. Cameron | ec52d5f | 2011-05-03 14:54:02 -0500 | [diff] [blame] | 4637 | 	if (!ctlr_is_resettable(board_id)) { | 
| Stephen M. Cameron | 058a0f9 | 2010-07-19 13:46:33 -0500 | [diff] [blame] | 4638 | 		dev_warn(&pdev->dev, "Cannot reset Smart Array 640x " | 
 | 4639 | 				"due to shared cache module."); | 
 | 4640 | 		return -ENODEV; | 
 | 4641 | 	} | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4642 |  | 
| Stephen M. Cameron | ec52d5f | 2011-05-03 14:54:02 -0500 | [diff] [blame] | 4643 | 	/* if controller is soft- but not hard resettable... */ | 
 | 4644 | 	if (!ctlr_is_hard_resettable(board_id)) | 
 | 4645 | 		return -ENOTSUPP; /* try soft reset later. */ | 
 | 4646 |  | 
| Stephen M. Cameron | f442e64 | 2010-10-22 14:21:12 -0500 | [diff] [blame] | 4647 | 	/* Save the PCI command register */ | 
 | 4648 | 	pci_read_config_word(pdev, 4, &command_register); | 
 | 4649 | 	/* Turn the board off.  This is so that later pci_restore_state() | 
 | 4650 | 	 * won't turn the board on before the rest of config space is ready. | 
 | 4651 | 	 */ | 
 | 4652 | 	pci_disable_device(pdev); | 
 | 4653 | 	pci_save_state(pdev); | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4654 |  | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4655 | 	/* find the first memory BAR, so we can find the cfg table */ | 
 | 4656 | 	rc = cciss_pci_find_memory_BAR(pdev, &paddr); | 
 | 4657 | 	if (rc) | 
 | 4658 | 		return rc; | 
 | 4659 | 	vaddr = remap_pci_mem(paddr, 0x250); | 
 | 4660 | 	if (!vaddr) | 
 | 4661 | 		return -ENOMEM; | 
 | 4662 |  | 
 | 4663 | 	/* find cfgtable in order to check if reset via doorbell is supported */ | 
 | 4664 | 	rc = cciss_find_cfg_addrs(pdev, vaddr, &cfg_base_addr, | 
 | 4665 | 					&cfg_base_addr_index, &cfg_offset); | 
 | 4666 | 	if (rc) | 
 | 4667 | 		goto unmap_vaddr; | 
 | 4668 | 	cfgtable = remap_pci_mem(pci_resource_start(pdev, | 
 | 4669 | 		       cfg_base_addr_index) + cfg_offset, sizeof(*cfgtable)); | 
 | 4670 | 	if (!cfgtable) { | 
 | 4671 | 		rc = -ENOMEM; | 
 | 4672 | 		goto unmap_vaddr; | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4673 | 	} | 
| Stephen M. Cameron | 62710ae | 2011-05-03 14:53:00 -0500 | [diff] [blame] | 4674 | 	rc = write_driver_ver_to_cfgtable(cfgtable); | 
 | 4675 | 	if (rc) | 
 | 4676 | 		goto unmap_vaddr; | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4677 |  | 
| Stephen M. Cameron | bf2e2e6 | 2011-05-03 14:53:46 -0500 | [diff] [blame] | 4678 | 	/* If reset via doorbell register is supported, use that. | 
 | 4679 | 	 * There are two such methods.  Favor the newest method. | 
| Stephen M. Cameron | 75230ff | 2010-08-23 11:02:17 +0200 | [diff] [blame] | 4680 | 	 */ | 
| Stephen M. Cameron | bf2e2e6 | 2011-05-03 14:53:46 -0500 | [diff] [blame] | 4681 | 	misc_fw_support = readl(&cfgtable->misc_fw_support); | 
 | 4682 | 	use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET2; | 
 | 4683 | 	if (use_doorbell) { | 
 | 4684 | 		use_doorbell = DOORBELL_CTLR_RESET2; | 
 | 4685 | 	} else { | 
 | 4686 | 		use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET; | 
| Stephen M. Cameron | 063d2cf | 2011-05-03 14:54:07 -0500 | [diff] [blame] | 4687 | 		if (use_doorbell) { | 
 | 4688 | 			dev_warn(&pdev->dev, "Controller claims that " | 
 | 4689 | 				"'Bit 2 doorbell reset' is " | 
 | 4690 | 				"supported, but not 'bit 5 doorbell reset'.  " | 
 | 4691 | 				"Firmware update is recommended.\n"); | 
 | 4692 | 			rc = -ENOTSUPP; /* use the soft reset */ | 
 | 4693 | 			goto unmap_cfgtable; | 
 | 4694 | 		} | 
| Stephen M. Cameron | bf2e2e6 | 2011-05-03 14:53:46 -0500 | [diff] [blame] | 4695 | 	} | 
| Stephen M. Cameron | 75230ff | 2010-08-23 11:02:17 +0200 | [diff] [blame] | 4696 |  | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4697 | 	rc = cciss_controller_hard_reset(pdev, vaddr, use_doorbell); | 
 | 4698 | 	if (rc) | 
 | 4699 | 		goto unmap_cfgtable; | 
| Stephen M. Cameron | f442e64 | 2010-10-22 14:21:12 -0500 | [diff] [blame] | 4700 | 	pci_restore_state(pdev); | 
 | 4701 | 	rc = pci_enable_device(pdev); | 
 | 4702 | 	if (rc) { | 
 | 4703 | 		dev_warn(&pdev->dev, "failed to enable device.\n"); | 
 | 4704 | 		goto unmap_cfgtable; | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4705 | 	} | 
| Stephen M. Cameron | f442e64 | 2010-10-22 14:21:12 -0500 | [diff] [blame] | 4706 | 	pci_write_config_word(pdev, 4, command_register); | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4707 |  | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4708 | 	/* Some devices (notably the HP Smart Array 5i Controller) | 
 | 4709 | 	   need a little pause here */ | 
 | 4710 | 	msleep(CCISS_POST_RESET_PAUSE_MSECS); | 
 | 4711 |  | 
| Stephen M. Cameron | afa842f | 2010-10-22 14:21:07 -0500 | [diff] [blame] | 4712 | 	/* Wait for board to become not ready, then ready. */ | 
| Stephen M. Cameron | 59ec86b | 2011-05-03 14:53:36 -0500 | [diff] [blame] | 4713 | 	dev_info(&pdev->dev, "Waiting for board to reset.\n"); | 
| Stephen M. Cameron | afa842f | 2010-10-22 14:21:07 -0500 | [diff] [blame] | 4714 | 	rc = cciss_wait_for_board_state(pdev, vaddr, BOARD_NOT_READY); | 
| Stephen M. Cameron | 5afe278 | 2011-05-03 14:53:52 -0500 | [diff] [blame] | 4715 | 	if (rc) { | 
 | 4716 | 		dev_warn(&pdev->dev, "Failed waiting for board to hard reset." | 
 | 4717 | 				"  Will try soft reset.\n"); | 
 | 4718 | 		rc = -ENOTSUPP; /* Not expected, but try soft reset later */ | 
 | 4719 | 		goto unmap_cfgtable; | 
 | 4720 | 	} | 
| Stephen M. Cameron | afa842f | 2010-10-22 14:21:07 -0500 | [diff] [blame] | 4721 | 	rc = cciss_wait_for_board_state(pdev, vaddr, BOARD_READY); | 
 | 4722 | 	if (rc) { | 
 | 4723 | 		dev_warn(&pdev->dev, | 
| Stephen M. Cameron | 5afe278 | 2011-05-03 14:53:52 -0500 | [diff] [blame] | 4724 | 			"failed waiting for board to become ready " | 
 | 4725 | 			"after hard reset\n"); | 
| Stephen M. Cameron | afa842f | 2010-10-22 14:21:07 -0500 | [diff] [blame] | 4726 | 		goto unmap_cfgtable; | 
 | 4727 | 	} | 
| Stephen M. Cameron | afa842f | 2010-10-22 14:21:07 -0500 | [diff] [blame] | 4728 |  | 
| Stephen M. Cameron | 62710ae | 2011-05-03 14:53:00 -0500 | [diff] [blame] | 4729 | 	rc = controller_reset_failed(vaddr); | 
 | 4730 | 	if (rc < 0) | 
 | 4731 | 		goto unmap_cfgtable; | 
 | 4732 | 	if (rc) { | 
| Stephen M. Cameron | 5afe278 | 2011-05-03 14:53:52 -0500 | [diff] [blame] | 4733 | 		dev_warn(&pdev->dev, "Unable to successfully hard reset " | 
 | 4734 | 			"controller. Will try soft reset.\n"); | 
 | 4735 | 		rc = -ENOTSUPP; /* Not expected, but try soft reset later */ | 
| Stephen M. Cameron | 62710ae | 2011-05-03 14:53:00 -0500 | [diff] [blame] | 4736 | 	} else { | 
| Stephen M. Cameron | 5afe278 | 2011-05-03 14:53:52 -0500 | [diff] [blame] | 4737 | 		dev_info(&pdev->dev, "Board ready after hard reset.\n"); | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4738 | 	} | 
 | 4739 |  | 
 | 4740 | unmap_cfgtable: | 
 | 4741 | 	iounmap(cfgtable); | 
 | 4742 |  | 
 | 4743 | unmap_vaddr: | 
 | 4744 | 	iounmap(vaddr); | 
 | 4745 | 	return rc; | 
| Chip Coldwell | 82eb03c | 2009-02-16 13:11:56 +0100 | [diff] [blame] | 4746 | } | 
 | 4747 |  | 
| Stephen M. Cameron | 83123cb | 2010-07-19 13:46:17 -0500 | [diff] [blame] | 4748 | static __devinit int cciss_init_reset_devices(struct pci_dev *pdev) | 
 | 4749 | { | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4750 | 	int rc, i; | 
| Stephen M. Cameron | 83123cb | 2010-07-19 13:46:17 -0500 | [diff] [blame] | 4751 |  | 
 | 4752 | 	if (!reset_devices) | 
 | 4753 | 		return 0; | 
 | 4754 |  | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4755 | 	/* Reset the controller with a PCI power-cycle or via doorbell */ | 
 | 4756 | 	rc = cciss_kdump_hard_reset_controller(pdev); | 
| Stephen M. Cameron | 83123cb | 2010-07-19 13:46:17 -0500 | [diff] [blame] | 4757 |  | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4758 | 	/* -ENOTSUPP here means we cannot reset the controller | 
 | 4759 | 	 * but it's already (and still) up and running in | 
| Stephen M. Cameron | 058a0f9 | 2010-07-19 13:46:33 -0500 | [diff] [blame] | 4760 | 	 * "performant mode".  Or, it might be 640x, which can't reset | 
 | 4761 | 	 * due to concerns about shared bbwc between 6402/6404 pair. | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4762 | 	 */ | 
 | 4763 | 	if (rc == -ENOTSUPP) | 
| Stephen M. Cameron | 5afe278 | 2011-05-03 14:53:52 -0500 | [diff] [blame] | 4764 | 		return rc; /* just try to do the kdump anyhow. */ | 
| Stephen M. Cameron | a6528d0 | 2010-07-19 13:46:22 -0500 | [diff] [blame] | 4765 | 	if (rc) | 
 | 4766 | 		return -ENODEV; | 
| Stephen M. Cameron | 83123cb | 2010-07-19 13:46:17 -0500 | [diff] [blame] | 4767 |  | 
 | 4768 | 	/* Now try to get the controller to respond to a no-op */ | 
| Stephen M. Cameron | 59ec86b | 2011-05-03 14:53:36 -0500 | [diff] [blame] | 4769 | 	dev_warn(&pdev->dev, "Waiting for controller to respond to no-op\n"); | 
| Stephen M. Cameron | 83123cb | 2010-07-19 13:46:17 -0500 | [diff] [blame] | 4770 | 	for (i = 0; i < CCISS_POST_RESET_NOOP_RETRIES; i++) { | 
 | 4771 | 		if (cciss_noop(pdev) == 0) | 
 | 4772 | 			break; | 
 | 4773 | 		else | 
 | 4774 | 			dev_warn(&pdev->dev, "no-op failed%s\n", | 
 | 4775 | 				(i < CCISS_POST_RESET_NOOP_RETRIES - 1 ? | 
 | 4776 | 					"; re-trying" : "")); | 
 | 4777 | 		msleep(CCISS_POST_RESET_NOOP_INTERVAL_MSECS); | 
 | 4778 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4779 | 	return 0; | 
 | 4780 | } | 
 | 4781 |  | 
| Stephen M. Cameron | 54dae34 | 2011-05-03 14:53:05 -0500 | [diff] [blame] | 4782 | static __devinit int cciss_allocate_cmd_pool(ctlr_info_t *h) | 
 | 4783 | { | 
 | 4784 | 	h->cmd_pool_bits = kmalloc( | 
 | 4785 | 		DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG) * | 
 | 4786 | 		sizeof(unsigned long), GFP_KERNEL); | 
 | 4787 | 	h->cmd_pool = pci_alloc_consistent(h->pdev, | 
 | 4788 | 		h->nr_cmds * sizeof(CommandList_struct), | 
 | 4789 | 		&(h->cmd_pool_dhandle)); | 
 | 4790 | 	h->errinfo_pool = pci_alloc_consistent(h->pdev, | 
 | 4791 | 		h->nr_cmds * sizeof(ErrorInfo_struct), | 
 | 4792 | 		&(h->errinfo_pool_dhandle)); | 
 | 4793 | 	if ((h->cmd_pool_bits == NULL) | 
 | 4794 | 		|| (h->cmd_pool == NULL) | 
 | 4795 | 		|| (h->errinfo_pool == NULL)) { | 
 | 4796 | 		dev_err(&h->pdev->dev, "out of memory"); | 
 | 4797 | 		return -ENOMEM; | 
 | 4798 | 	} | 
 | 4799 | 	return 0; | 
 | 4800 | } | 
 | 4801 |  | 
| Stephen M. Cameron | abf7966 | 2011-05-03 14:53:10 -0500 | [diff] [blame] | 4802 | static __devinit int cciss_allocate_scatterlists(ctlr_info_t *h) | 
 | 4803 | { | 
 | 4804 | 	int i; | 
 | 4805 |  | 
 | 4806 | 	/* zero it, so that on free we need not know how many were alloc'ed */ | 
 | 4807 | 	h->scatter_list = kzalloc(h->max_commands * | 
 | 4808 | 				sizeof(struct scatterlist *), GFP_KERNEL); | 
 | 4809 | 	if (!h->scatter_list) | 
 | 4810 | 		return -ENOMEM; | 
 | 4811 |  | 
 | 4812 | 	for (i = 0; i < h->nr_cmds; i++) { | 
 | 4813 | 		h->scatter_list[i] = kmalloc(sizeof(struct scatterlist) * | 
 | 4814 | 						h->maxsgentries, GFP_KERNEL); | 
 | 4815 | 		if (h->scatter_list[i] == NULL) { | 
 | 4816 | 			dev_err(&h->pdev->dev, "could not allocate " | 
 | 4817 | 				"s/g lists\n"); | 
 | 4818 | 			return -ENOMEM; | 
 | 4819 | 		} | 
 | 4820 | 	} | 
 | 4821 | 	return 0; | 
 | 4822 | } | 
 | 4823 |  | 
 | 4824 | static void cciss_free_scatterlists(ctlr_info_t *h) | 
 | 4825 | { | 
 | 4826 | 	int i; | 
 | 4827 |  | 
 | 4828 | 	if (h->scatter_list) { | 
 | 4829 | 		for (i = 0; i < h->nr_cmds; i++) | 
 | 4830 | 			kfree(h->scatter_list[i]); | 
 | 4831 | 		kfree(h->scatter_list); | 
 | 4832 | 	} | 
 | 4833 | } | 
 | 4834 |  | 
| Stephen M. Cameron | 54dae34 | 2011-05-03 14:53:05 -0500 | [diff] [blame] | 4835 | static void cciss_free_cmd_pool(ctlr_info_t *h) | 
 | 4836 | { | 
 | 4837 | 	kfree(h->cmd_pool_bits); | 
 | 4838 | 	if (h->cmd_pool) | 
 | 4839 | 		pci_free_consistent(h->pdev, | 
 | 4840 | 			h->nr_cmds * sizeof(CommandList_struct), | 
 | 4841 | 			h->cmd_pool, h->cmd_pool_dhandle); | 
 | 4842 | 	if (h->errinfo_pool) | 
 | 4843 | 		pci_free_consistent(h->pdev, | 
 | 4844 | 			h->nr_cmds * sizeof(ErrorInfo_struct), | 
 | 4845 | 			h->errinfo_pool, h->errinfo_pool_dhandle); | 
 | 4846 | } | 
 | 4847 |  | 
| Stephen M. Cameron | 2b48085 | 2011-05-03 14:53:16 -0500 | [diff] [blame] | 4848 | static int cciss_request_irq(ctlr_info_t *h, | 
 | 4849 | 	irqreturn_t (*msixhandler)(int, void *), | 
 | 4850 | 	irqreturn_t (*intxhandler)(int, void *)) | 
 | 4851 | { | 
 | 4852 | 	if (h->msix_vector || h->msi_vector) { | 
 | 4853 | 		if (!request_irq(h->intr[PERF_MODE_INT], msixhandler, | 
 | 4854 | 				IRQF_DISABLED, h->devname, h)) | 
 | 4855 | 			return 0; | 
 | 4856 | 		dev_err(&h->pdev->dev, "Unable to get msi irq %d" | 
 | 4857 | 			" for %s\n", h->intr[PERF_MODE_INT], | 
 | 4858 | 			h->devname); | 
 | 4859 | 		return -1; | 
 | 4860 | 	} | 
 | 4861 |  | 
 | 4862 | 	if (!request_irq(h->intr[PERF_MODE_INT], intxhandler, | 
 | 4863 | 			IRQF_DISABLED, h->devname, h)) | 
 | 4864 | 		return 0; | 
 | 4865 | 	dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n", | 
 | 4866 | 		h->intr[PERF_MODE_INT], h->devname); | 
 | 4867 | 	return -1; | 
 | 4868 | } | 
 | 4869 |  | 
| Stephen M. Cameron | 5afe278 | 2011-05-03 14:53:52 -0500 | [diff] [blame] | 4870 | static int __devinit cciss_kdump_soft_reset(ctlr_info_t *h) | 
 | 4871 | { | 
 | 4872 | 	if (cciss_send_reset(h, CTLR_LUNID, CCISS_RESET_TYPE_CONTROLLER)) { | 
 | 4873 | 		dev_warn(&h->pdev->dev, "Resetting array controller failed.\n"); | 
 | 4874 | 		return -EIO; | 
 | 4875 | 	} | 
 | 4876 |  | 
 | 4877 | 	dev_info(&h->pdev->dev, "Waiting for board to soft reset.\n"); | 
 | 4878 | 	if (cciss_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY)) { | 
 | 4879 | 		dev_warn(&h->pdev->dev, "Soft reset had no effect.\n"); | 
 | 4880 | 		return -1; | 
 | 4881 | 	} | 
 | 4882 |  | 
 | 4883 | 	dev_info(&h->pdev->dev, "Board reset, awaiting READY status.\n"); | 
 | 4884 | 	if (cciss_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY)) { | 
 | 4885 | 		dev_warn(&h->pdev->dev, "Board failed to become ready " | 
 | 4886 | 			"after soft reset.\n"); | 
 | 4887 | 		return -1; | 
 | 4888 | 	} | 
 | 4889 |  | 
 | 4890 | 	return 0; | 
 | 4891 | } | 
 | 4892 |  | 
 | 4893 | static void cciss_undo_allocations_after_kdump_soft_reset(ctlr_info_t *h) | 
 | 4894 | { | 
 | 4895 | 	int ctlr = h->ctlr; | 
 | 4896 |  | 
 | 4897 | 	free_irq(h->intr[PERF_MODE_INT], h); | 
 | 4898 | #ifdef CONFIG_PCI_MSI | 
 | 4899 | 	if (h->msix_vector) | 
 | 4900 | 		pci_disable_msix(h->pdev); | 
 | 4901 | 	else if (h->msi_vector) | 
 | 4902 | 		pci_disable_msi(h->pdev); | 
 | 4903 | #endif /* CONFIG_PCI_MSI */ | 
 | 4904 | 	cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds); | 
 | 4905 | 	cciss_free_scatterlists(h); | 
 | 4906 | 	cciss_free_cmd_pool(h); | 
 | 4907 | 	kfree(h->blockFetchTable); | 
 | 4908 | 	if (h->reply_pool) | 
 | 4909 | 		pci_free_consistent(h->pdev, h->max_commands * sizeof(__u64), | 
 | 4910 | 				h->reply_pool, h->reply_pool_dhandle); | 
 | 4911 | 	if (h->transtable) | 
 | 4912 | 		iounmap(h->transtable); | 
 | 4913 | 	if (h->cfgtable) | 
 | 4914 | 		iounmap(h->cfgtable); | 
 | 4915 | 	if (h->vaddr) | 
 | 4916 | 		iounmap(h->vaddr); | 
 | 4917 | 	unregister_blkdev(h->major, h->devname); | 
 | 4918 | 	cciss_destroy_hba_sysfs_entry(h); | 
 | 4919 | 	pci_release_regions(h->pdev); | 
 | 4920 | 	kfree(h); | 
 | 4921 | 	hba[ctlr] = NULL; | 
 | 4922 | } | 
 | 4923 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4924 | /* | 
 | 4925 |  *  This is it.  Find all the controllers and register them.  I really hate | 
 | 4926 |  *  stealing all these major device numbers. | 
 | 4927 |  *  returns the number of block devices registered. | 
 | 4928 |  */ | 
 | 4929 | static int __devinit cciss_init_one(struct pci_dev *pdev, | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4930 | 				    const struct pci_device_id *ent) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4931 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4932 | 	int i; | 
| Mike Miller | 799202c | 2006-12-06 20:35:12 -0800 | [diff] [blame] | 4933 | 	int j = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4934 | 	int rc; | 
| Stephen M. Cameron | 5afe278 | 2011-05-03 14:53:52 -0500 | [diff] [blame] | 4935 | 	int try_soft_reset = 0; | 
| Mike Miller | 22bece0 | 2008-11-06 12:53:25 -0800 | [diff] [blame] | 4936 | 	int dac, return_code; | 
| Eric Dumazet | 212a502 | 2009-08-24 10:01:53 +0200 | [diff] [blame] | 4937 | 	InquiryData_struct *inq_buff; | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4938 | 	ctlr_info_t *h; | 
| Stephen M. Cameron | 5afe278 | 2011-05-03 14:53:52 -0500 | [diff] [blame] | 4939 | 	unsigned long flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4940 |  | 
| Stephen M. Cameron | 83123cb | 2010-07-19 13:46:17 -0500 | [diff] [blame] | 4941 | 	rc = cciss_init_reset_devices(pdev); | 
| Stephen M. Cameron | 5afe278 | 2011-05-03 14:53:52 -0500 | [diff] [blame] | 4942 | 	if (rc) { | 
 | 4943 | 		if (rc != -ENOTSUPP) | 
 | 4944 | 			return rc; | 
 | 4945 | 		/* If the reset fails in a particular way (it has no way to do | 
 | 4946 | 		 * a proper hard reset, so returns -ENOTSUPP) we can try to do | 
 | 4947 | 		 * a soft reset once we get the controller configured up to the | 
 | 4948 | 		 * point that it can accept a command. | 
 | 4949 | 		 */ | 
 | 4950 | 		try_soft_reset = 1; | 
 | 4951 | 		rc = 0; | 
 | 4952 | 	} | 
 | 4953 |  | 
 | 4954 | reinit_after_soft_reset: | 
 | 4955 |  | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4956 | 	i = alloc_cciss_hba(pdev); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 4957 | 	if (i < 0) | 
| Bjorn Helgaas | e2019b5 | 2006-06-25 05:49:05 -0700 | [diff] [blame] | 4958 | 		return -1; | 
| Mike Miller | 1f8ef38 | 2005-09-13 01:25:21 -0700 | [diff] [blame] | 4959 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4960 | 	h = hba[i]; | 
 | 4961 | 	h->pdev = pdev; | 
 | 4962 | 	h->busy_initializing = 1; | 
| Jens Axboe | e6e1ee9 | 2011-01-10 21:50:33 +0100 | [diff] [blame] | 4963 | 	INIT_LIST_HEAD(&h->cmpQ); | 
 | 4964 | 	INIT_LIST_HEAD(&h->reqQ); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4965 | 	mutex_init(&h->busy_shutting_down); | 
| Mike Miller | 1f8ef38 | 2005-09-13 01:25:21 -0700 | [diff] [blame] | 4966 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4967 | 	if (cciss_pci_init(h) != 0) | 
| Stephen M. Cameron | 2cfa948 | 2009-10-13 09:18:22 +0200 | [diff] [blame] | 4968 | 		goto clean_no_release_regions; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4969 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4970 | 	sprintf(h->devname, "cciss%d", i); | 
 | 4971 | 	h->ctlr = i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4972 |  | 
| Stephen M. Cameron | 8a4ec67 | 2011-05-03 14:54:12 -0500 | [diff] [blame] | 4973 | 	if (cciss_tape_cmds < 2) | 
 | 4974 | 		cciss_tape_cmds = 2; | 
 | 4975 | 	if (cciss_tape_cmds > 16) | 
 | 4976 | 		cciss_tape_cmds = 16; | 
 | 4977 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4978 | 	init_completion(&h->scan_wait); | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 4979 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4980 | 	if (cciss_create_hba_sysfs_entry(h)) | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 4981 | 		goto clean0; | 
 | 4982 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4983 | 	/* configure PCI DMA stuff */ | 
| Yang Hongyang | 6a35528 | 2009-04-06 19:01:13 -0700 | [diff] [blame] | 4984 | 	if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) | 
| Bjorn Helgaas | 40aabb5 | 2006-06-25 05:49:03 -0700 | [diff] [blame] | 4985 | 		dac = 1; | 
| Yang Hongyang | 284901a | 2009-04-06 19:01:15 -0700 | [diff] [blame] | 4986 | 	else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) | 
| Bjorn Helgaas | 40aabb5 | 2006-06-25 05:49:03 -0700 | [diff] [blame] | 4987 | 		dac = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4988 | 	else { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 4989 | 		dev_err(&h->pdev->dev, "no suitable DMA available\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4990 | 		goto clean1; | 
 | 4991 | 	} | 
 | 4992 |  | 
 | 4993 | 	/* | 
 | 4994 | 	 * register with the major number, or get a dynamic major number | 
 | 4995 | 	 * by passing 0 as argument.  This is done for greater than | 
 | 4996 | 	 * 8 controller support. | 
 | 4997 | 	 */ | 
 | 4998 | 	if (i < MAX_CTLR_ORIG) | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 4999 | 		h->major = COMPAQ_CISS_MAJOR + i; | 
 | 5000 | 	rc = register_blkdev(h->major, h->devname); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5001 | 	if (rc == -EBUSY || rc == -EINVAL) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 5002 | 		dev_err(&h->pdev->dev, | 
 | 5003 | 		       "Unable to get major number %d for %s " | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5004 | 		       "on hba %d\n", h->major, h->devname, i); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5005 | 		goto clean1; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5006 | 	} else { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5007 | 		if (i >= MAX_CTLR_ORIG) | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5008 | 			h->major = rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5009 | 	} | 
 | 5010 |  | 
 | 5011 | 	/* make sure the board interrupts are off */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5012 | 	h->access.set_intr_mask(h, CCISS_INTR_OFF); | 
| Stephen M. Cameron | 2b48085 | 2011-05-03 14:53:16 -0500 | [diff] [blame] | 5013 | 	rc = cciss_request_irq(h, do_cciss_msix_intr, do_cciss_intx); | 
 | 5014 | 	if (rc) | 
 | 5015 | 		goto clean2; | 
| Bjorn Helgaas | 40aabb5 | 2006-06-25 05:49:03 -0700 | [diff] [blame] | 5016 |  | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 5017 | 	dev_info(&h->pdev->dev, "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n", | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5018 | 	       h->devname, pdev->device, pci_name(pdev), | 
 | 5019 | 	       h->intr[PERF_MODE_INT], dac ? "" : " not"); | 
| Bjorn Helgaas | 40aabb5 | 2006-06-25 05:49:03 -0700 | [diff] [blame] | 5020 |  | 
| Stephen M. Cameron | 54dae34 | 2011-05-03 14:53:05 -0500 | [diff] [blame] | 5021 | 	if (cciss_allocate_cmd_pool(h)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5022 | 		goto clean4; | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 5023 |  | 
| Stephen M. Cameron | abf7966 | 2011-05-03 14:53:10 -0500 | [diff] [blame] | 5024 | 	if (cciss_allocate_scatterlists(h)) | 
| Dan Carpenter | 4ee69851 | 2010-08-23 12:28:15 +0200 | [diff] [blame] | 5025 | 		goto clean4; | 
 | 5026 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5027 | 	h->cmd_sg_list = cciss_allocate_sg_chain_blocks(h, | 
 | 5028 | 		h->chainsize, h->nr_cmds); | 
 | 5029 | 	if (!h->cmd_sg_list && h->chainsize > 0) | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 5030 | 		goto clean4; | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 5031 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5032 | 	spin_lock_init(&h->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5033 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5034 | 	/* Initialize the pdev driver private data. | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5035 | 	   have it point to h.  */ | 
 | 5036 | 	pci_set_drvdata(pdev, h); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5037 | 	/* command and error info recs zeroed out before | 
 | 5038 | 	   they are used */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5039 | 	memset(h->cmd_pool_bits, 0, | 
 | 5040 | 	       DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG) | 
| Julia Lawall | 061837b | 2008-09-22 14:57:16 -0700 | [diff] [blame] | 5041 | 			* sizeof(unsigned long)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5042 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5043 | 	h->num_luns = 0; | 
 | 5044 | 	h->highest_lun = -1; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 5045 | 	for (j = 0; j < CISS_MAX_LUN; j++) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5046 | 		h->drv[j] = NULL; | 
 | 5047 | 		h->gendisk[j] = NULL; | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 5048 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5049 |  | 
| Stephen M. Cameron | 5afe278 | 2011-05-03 14:53:52 -0500 | [diff] [blame] | 5050 | 	/* At this point, the controller is ready to take commands. | 
 | 5051 | 	 * Now, if reset_devices and the hard reset didn't work, try | 
 | 5052 | 	 * the soft reset and see if that works. | 
 | 5053 | 	 */ | 
 | 5054 | 	if (try_soft_reset) { | 
 | 5055 |  | 
 | 5056 | 		/* This is kind of gross.  We may or may not get a completion | 
 | 5057 | 		 * from the soft reset command, and if we do, then the value | 
 | 5058 | 		 * from the fifo may or may not be valid.  So, we wait 10 secs | 
 | 5059 | 		 * after the reset throwing away any completions we get during | 
 | 5060 | 		 * that time.  Unregister the interrupt handler and register | 
 | 5061 | 		 * fake ones to scoop up any residual completions. | 
 | 5062 | 		 */ | 
 | 5063 | 		spin_lock_irqsave(&h->lock, flags); | 
 | 5064 | 		h->access.set_intr_mask(h, CCISS_INTR_OFF); | 
 | 5065 | 		spin_unlock_irqrestore(&h->lock, flags); | 
 | 5066 | 		free_irq(h->intr[PERF_MODE_INT], h); | 
 | 5067 | 		rc = cciss_request_irq(h, cciss_msix_discard_completions, | 
 | 5068 | 					cciss_intx_discard_completions); | 
 | 5069 | 		if (rc) { | 
 | 5070 | 			dev_warn(&h->pdev->dev, "Failed to request_irq after " | 
 | 5071 | 				"soft reset.\n"); | 
 | 5072 | 			goto clean4; | 
 | 5073 | 		} | 
 | 5074 |  | 
 | 5075 | 		rc = cciss_kdump_soft_reset(h); | 
 | 5076 | 		if (rc) { | 
 | 5077 | 			dev_warn(&h->pdev->dev, "Soft reset failed.\n"); | 
 | 5078 | 			goto clean4; | 
 | 5079 | 		} | 
 | 5080 |  | 
 | 5081 | 		dev_info(&h->pdev->dev, "Board READY.\n"); | 
 | 5082 | 		dev_info(&h->pdev->dev, | 
 | 5083 | 			"Waiting for stale completions to drain.\n"); | 
 | 5084 | 		h->access.set_intr_mask(h, CCISS_INTR_ON); | 
 | 5085 | 		msleep(10000); | 
 | 5086 | 		h->access.set_intr_mask(h, CCISS_INTR_OFF); | 
 | 5087 |  | 
 | 5088 | 		rc = controller_reset_failed(h->cfgtable); | 
 | 5089 | 		if (rc) | 
 | 5090 | 			dev_info(&h->pdev->dev, | 
 | 5091 | 				"Soft reset appears to have failed.\n"); | 
 | 5092 |  | 
 | 5093 | 		/* since the controller's reset, we have to go back and re-init | 
 | 5094 | 		 * everything.  Easiest to just forget what we've done and do it | 
 | 5095 | 		 * all over again. | 
 | 5096 | 		 */ | 
 | 5097 | 		cciss_undo_allocations_after_kdump_soft_reset(h); | 
 | 5098 | 		try_soft_reset = 0; | 
 | 5099 | 		if (rc) | 
 | 5100 | 			/* don't go to clean4, we already unallocated */ | 
 | 5101 | 			return -ENODEV; | 
 | 5102 |  | 
 | 5103 | 		goto reinit_after_soft_reset; | 
 | 5104 | 	} | 
 | 5105 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5106 | 	cciss_scsi_setup(h); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5107 |  | 
 | 5108 | 	/* Turn the interrupts on so we can service requests */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5109 | 	h->access.set_intr_mask(h, CCISS_INTR_ON); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5110 |  | 
| Mike Miller | 22bece0 | 2008-11-06 12:53:25 -0800 | [diff] [blame] | 5111 | 	/* Get the firmware version */ | 
 | 5112 | 	inq_buff = kzalloc(sizeof(InquiryData_struct), GFP_KERNEL); | 
 | 5113 | 	if (inq_buff == NULL) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 5114 | 		dev_err(&h->pdev->dev, "out of memory\n"); | 
| Mike Miller | 22bece0 | 2008-11-06 12:53:25 -0800 | [diff] [blame] | 5115 | 		goto clean4; | 
 | 5116 | 	} | 
 | 5117 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5118 | 	return_code = sendcmd_withirq(h, CISS_INQUIRY, inq_buff, | 
| scameron@beardog.cca.cpqcorp.net | b57695f | 2009-06-08 16:02:17 -0500 | [diff] [blame] | 5119 | 		sizeof(InquiryData_struct), 0, CTLR_LUNID, TYPE_CMD); | 
| Mike Miller | 22bece0 | 2008-11-06 12:53:25 -0800 | [diff] [blame] | 5120 | 	if (return_code == IO_OK) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5121 | 		h->firm_ver[0] = inq_buff->data_byte[32]; | 
 | 5122 | 		h->firm_ver[1] = inq_buff->data_byte[33]; | 
 | 5123 | 		h->firm_ver[2] = inq_buff->data_byte[34]; | 
 | 5124 | 		h->firm_ver[3] = inq_buff->data_byte[35]; | 
| Mike Miller | 22bece0 | 2008-11-06 12:53:25 -0800 | [diff] [blame] | 5125 | 	} else {	 /* send command failed */ | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 5126 | 		dev_warn(&h->pdev->dev, "unable to determine firmware" | 
| Mike Miller | 22bece0 | 2008-11-06 12:53:25 -0800 | [diff] [blame] | 5127 | 			" version of controller\n"); | 
 | 5128 | 	} | 
| Eric Dumazet | 212a502 | 2009-08-24 10:01:53 +0200 | [diff] [blame] | 5129 | 	kfree(inq_buff); | 
| Mike Miller | 22bece0 | 2008-11-06 12:53:25 -0800 | [diff] [blame] | 5130 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5131 | 	cciss_procinit(h); | 
| Mike Miller | 92c4231a | 2006-12-06 20:35:06 -0800 | [diff] [blame] | 5132 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5133 | 	h->cciss_max_sectors = 8192; | 
| Mike Miller | 92c4231a | 2006-12-06 20:35:06 -0800 | [diff] [blame] | 5134 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5135 | 	rebuild_lun_table(h, 1, 0); | 
 | 5136 | 	h->busy_initializing = 0; | 
| Bjorn Helgaas | e2019b5 | 2006-06-25 05:49:05 -0700 | [diff] [blame] | 5137 | 	return 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5138 |  | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 5139 | clean4: | 
| Stephen M. Cameron | 54dae34 | 2011-05-03 14:53:05 -0500 | [diff] [blame] | 5140 | 	cciss_free_cmd_pool(h); | 
| Stephen M. Cameron | abf7966 | 2011-05-03 14:53:10 -0500 | [diff] [blame] | 5141 | 	cciss_free_scatterlists(h); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5142 | 	cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5143 | 	free_irq(h->intr[PERF_MODE_INT], h); | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 5144 | clean2: | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5145 | 	unregister_blkdev(h->major, h->devname); | 
| Mike Miller | 6ae5ce8 | 2008-08-04 11:54:52 +0200 | [diff] [blame] | 5146 | clean1: | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5147 | 	cciss_destroy_hba_sysfs_entry(h); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 5148 | clean0: | 
| Stephen M. Cameron | 2cfa948 | 2009-10-13 09:18:22 +0200 | [diff] [blame] | 5149 | 	pci_release_regions(pdev); | 
 | 5150 | clean_no_release_regions: | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5151 | 	h->busy_initializing = 0; | 
| Stephen M. Cameron | 9cef0d2 | 2009-09-17 13:48:31 -0500 | [diff] [blame] | 5152 |  | 
| Mike Miller | 872225c | 2006-12-13 00:34:22 -0800 | [diff] [blame] | 5153 | 	/* | 
 | 5154 | 	 * Deliberately omit pci_disable_device(): it does something nasty to | 
 | 5155 | 	 * Smart Array controllers that pci_enable_device does not undo | 
 | 5156 | 	 */ | 
| Mike Miller | 799202c | 2006-12-06 20:35:12 -0800 | [diff] [blame] | 5157 | 	pci_set_drvdata(pdev, NULL); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5158 | 	free_hba(h); | 
| Bjorn Helgaas | e2019b5 | 2006-06-25 05:49:05 -0700 | [diff] [blame] | 5159 | 	return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5160 | } | 
 | 5161 |  | 
| Gerald Britton | e9ca75b | 2007-05-14 13:53:01 -0400 | [diff] [blame] | 5162 | static void cciss_shutdown(struct pci_dev *pdev) | 
 | 5163 | { | 
| Stephen M. Cameron | 29009a0 | 2009-11-12 12:49:35 -0600 | [diff] [blame] | 5164 | 	ctlr_info_t *h; | 
 | 5165 | 	char *flush_buf; | 
| Gerald Britton | e9ca75b | 2007-05-14 13:53:01 -0400 | [diff] [blame] | 5166 | 	int return_code; | 
 | 5167 |  | 
| Stephen M. Cameron | 29009a0 | 2009-11-12 12:49:35 -0600 | [diff] [blame] | 5168 | 	h = pci_get_drvdata(pdev); | 
 | 5169 | 	flush_buf = kzalloc(4, GFP_KERNEL); | 
 | 5170 | 	if (!flush_buf) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 5171 | 		dev_warn(&h->pdev->dev, "cache not flushed, out of memory.\n"); | 
| Gerald Britton | e9ca75b | 2007-05-14 13:53:01 -0400 | [diff] [blame] | 5172 | 		return; | 
| Gerald Britton | e9ca75b | 2007-05-14 13:53:01 -0400 | [diff] [blame] | 5173 | 	} | 
| Stephen M. Cameron | 29009a0 | 2009-11-12 12:49:35 -0600 | [diff] [blame] | 5174 | 	/* write all data in the battery backed cache to disk */ | 
 | 5175 | 	memset(flush_buf, 0, 4); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5176 | 	return_code = sendcmd_withirq(h, CCISS_CACHE_FLUSH, flush_buf, | 
| Stephen M. Cameron | 29009a0 | 2009-11-12 12:49:35 -0600 | [diff] [blame] | 5177 | 		4, 0, CTLR_LUNID, TYPE_CMD); | 
 | 5178 | 	kfree(flush_buf); | 
 | 5179 | 	if (return_code != IO_OK) | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 5180 | 		dev_warn(&h->pdev->dev, "Error flushing cache\n"); | 
| Stephen M. Cameron | 29009a0 | 2009-11-12 12:49:35 -0600 | [diff] [blame] | 5181 | 	h->access.set_intr_mask(h, CCISS_INTR_OFF); | 
| Mike Miller | 5e21615 | 2010-06-02 12:58:06 -0700 | [diff] [blame] | 5182 | 	free_irq(h->intr[PERF_MODE_INT], h); | 
| Gerald Britton | e9ca75b | 2007-05-14 13:53:01 -0400 | [diff] [blame] | 5183 | } | 
 | 5184 |  | 
 | 5185 | static void __devexit cciss_remove_one(struct pci_dev *pdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5186 | { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5187 | 	ctlr_info_t *h; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5188 | 	int i, j; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5189 |  | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5190 | 	if (pci_get_drvdata(pdev) == NULL) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 5191 | 		dev_err(&pdev->dev, "Unable to remove device\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5192 | 		return; | 
 | 5193 | 	} | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 5194 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5195 | 	h = pci_get_drvdata(pdev); | 
 | 5196 | 	i = h->ctlr; | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5197 | 	if (hba[i] == NULL) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 5198 | 		dev_err(&pdev->dev, "device appears to already be removed\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5199 | 		return; | 
 | 5200 | 	} | 
| Bjorn Helgaas | b655077 | 2007-04-11 23:28:43 -0700 | [diff] [blame] | 5201 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5202 | 	mutex_lock(&h->busy_shutting_down); | 
| Mike Miller | 0a9279c | 2009-04-02 12:50:55 -0700 | [diff] [blame] | 5203 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5204 | 	remove_from_scan_list(h); | 
 | 5205 | 	remove_proc_entry(h->devname, proc_cciss); | 
 | 5206 | 	unregister_blkdev(h->major, h->devname); | 
| Bjorn Helgaas | b655077 | 2007-04-11 23:28:43 -0700 | [diff] [blame] | 5207 |  | 
 | 5208 | 	/* remove it from the disk list */ | 
 | 5209 | 	for (j = 0; j < CISS_MAX_LUN; j++) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5210 | 		struct gendisk *disk = h->gendisk[j]; | 
| Bjorn Helgaas | b655077 | 2007-04-11 23:28:43 -0700 | [diff] [blame] | 5211 | 		if (disk) { | 
| Jens Axboe | 165125e | 2007-07-24 09:28:11 +0200 | [diff] [blame] | 5212 | 			struct request_queue *q = disk->queue; | 
| Bjorn Helgaas | b655077 | 2007-04-11 23:28:43 -0700 | [diff] [blame] | 5213 |  | 
| Stephen M. Cameron | 097d026 | 2009-09-17 13:47:19 -0500 | [diff] [blame] | 5214 | 			if (disk->flags & GENHD_FL_UP) { | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5215 | 				cciss_destroy_ld_sysfs_entry(h, j, 1); | 
| Bjorn Helgaas | b655077 | 2007-04-11 23:28:43 -0700 | [diff] [blame] | 5216 | 				del_gendisk(disk); | 
| Stephen M. Cameron | 097d026 | 2009-09-17 13:47:19 -0500 | [diff] [blame] | 5217 | 			} | 
| Bjorn Helgaas | b655077 | 2007-04-11 23:28:43 -0700 | [diff] [blame] | 5218 | 			if (q) | 
 | 5219 | 				blk_cleanup_queue(q); | 
 | 5220 | 		} | 
 | 5221 | 	} | 
 | 5222 |  | 
| Mike Miller | ba198ef | 2008-08-04 11:54:55 +0200 | [diff] [blame] | 5223 | #ifdef CONFIG_CISS_SCSI_TAPE | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5224 | 	cciss_unregister_scsi(h);	/* unhook from SCSI subsystem */ | 
| Mike Miller | ba198ef | 2008-08-04 11:54:55 +0200 | [diff] [blame] | 5225 | #endif | 
| Bjorn Helgaas | b655077 | 2007-04-11 23:28:43 -0700 | [diff] [blame] | 5226 |  | 
| Gerald Britton | e9ca75b | 2007-05-14 13:53:01 -0400 | [diff] [blame] | 5227 | 	cciss_shutdown(pdev); | 
| Mike Miller | fb86a35 | 2006-01-08 01:03:50 -0800 | [diff] [blame] | 5228 |  | 
 | 5229 | #ifdef CONFIG_PCI_MSI | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5230 | 	if (h->msix_vector) | 
 | 5231 | 		pci_disable_msix(h->pdev); | 
 | 5232 | 	else if (h->msi_vector) | 
 | 5233 | 		pci_disable_msi(h->pdev); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5234 | #endif				/* CONFIG_PCI_MSI */ | 
| Mike Miller | fb86a35 | 2006-01-08 01:03:50 -0800 | [diff] [blame] | 5235 |  | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5236 | 	iounmap(h->transtable); | 
 | 5237 | 	iounmap(h->cfgtable); | 
 | 5238 | 	iounmap(h->vaddr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5239 |  | 
| Stephen M. Cameron | 54dae34 | 2011-05-03 14:53:05 -0500 | [diff] [blame] | 5240 | 	cciss_free_cmd_pool(h); | 
| Don Brace | 5c07a31 | 2009-11-12 12:50:01 -0600 | [diff] [blame] | 5241 | 	/* Free up sg elements */ | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5242 | 	for (j = 0; j < h->nr_cmds; j++) | 
 | 5243 | 		kfree(h->scatter_list[j]); | 
 | 5244 | 	kfree(h->scatter_list); | 
 | 5245 | 	cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds); | 
| Stephen M. Cameron | e363e01 | 2011-05-03 14:53:21 -0500 | [diff] [blame] | 5246 | 	kfree(h->blockFetchTable); | 
 | 5247 | 	if (h->reply_pool) | 
 | 5248 | 		pci_free_consistent(h->pdev, h->max_commands * sizeof(__u64), | 
 | 5249 | 				h->reply_pool, h->reply_pool_dhandle); | 
| Mike Miller | 872225c | 2006-12-13 00:34:22 -0800 | [diff] [blame] | 5250 | 	/* | 
 | 5251 | 	 * Deliberately omit pci_disable_device(): it does something nasty to | 
 | 5252 | 	 * Smart Array controllers that pci_enable_device does not undo | 
 | 5253 | 	 */ | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5254 | 	pci_release_regions(pdev); | 
| Bjorn Helgaas | 4e57030 | 2006-06-25 05:49:02 -0700 | [diff] [blame] | 5255 | 	pci_set_drvdata(pdev, NULL); | 
| Stephen M. Cameron | f70dba8 | 2010-07-19 13:46:38 -0500 | [diff] [blame] | 5256 | 	cciss_destroy_hba_sysfs_entry(h); | 
 | 5257 | 	mutex_unlock(&h->busy_shutting_down); | 
 | 5258 | 	free_hba(h); | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5259 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5260 |  | 
 | 5261 | static struct pci_driver cciss_pci_driver = { | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5262 | 	.name = "cciss", | 
 | 5263 | 	.probe = cciss_init_one, | 
 | 5264 | 	.remove = __devexit_p(cciss_remove_one), | 
 | 5265 | 	.id_table = cciss_pci_device_id,	/* id_table */ | 
| Gerald Britton | e9ca75b | 2007-05-14 13:53:01 -0400 | [diff] [blame] | 5266 | 	.shutdown = cciss_shutdown, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5267 | }; | 
 | 5268 |  | 
 | 5269 | /* | 
 | 5270 |  *  This is it.  Register the PCI driver information for the cards we control | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5271 |  *  the OS will call our registered routines when it finds one of our cards. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5272 |  */ | 
 | 5273 | static int __init cciss_init(void) | 
 | 5274 | { | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 5275 | 	int err; | 
 | 5276 |  | 
| Jens Axboe | 10cbda9 | 2009-02-27 20:14:20 +0100 | [diff] [blame] | 5277 | 	/* | 
 | 5278 | 	 * The hardware requires that commands are aligned on a 64-bit | 
 | 5279 | 	 * boundary. Given that we use pci_alloc_consistent() to allocate an | 
 | 5280 | 	 * array of them, the size must be a multiple of 8 bytes. | 
 | 5281 | 	 */ | 
| Stephen M. Cameron | 1b7d0d2 | 2010-02-26 16:01:17 -0600 | [diff] [blame] | 5282 | 	BUILD_BUG_ON(sizeof(CommandList_struct) % COMMANDLIST_ALIGNMENT); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5283 | 	printk(KERN_INFO DRIVER_NAME "\n"); | 
 | 5284 |  | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 5285 | 	err = bus_register(&cciss_bus_type); | 
 | 5286 | 	if (err) | 
 | 5287 | 		return err; | 
 | 5288 |  | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 5289 | 	/* Start the scan thread */ | 
 | 5290 | 	cciss_scan_thread = kthread_run(scan_thread, NULL, "cciss_scan"); | 
 | 5291 | 	if (IS_ERR(cciss_scan_thread)) { | 
 | 5292 | 		err = PTR_ERR(cciss_scan_thread); | 
 | 5293 | 		goto err_bus_unregister; | 
 | 5294 | 	} | 
 | 5295 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5296 | 	/* Register for our PCI devices */ | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 5297 | 	err = pci_register_driver(&cciss_pci_driver); | 
 | 5298 | 	if (err) | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 5299 | 		goto err_thread_stop; | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 5300 |  | 
| Stephen M. Cameron | 617e134 | 2009-09-17 13:47:14 -0500 | [diff] [blame] | 5301 | 	return err; | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 5302 |  | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 5303 | err_thread_stop: | 
 | 5304 | 	kthread_stop(cciss_scan_thread); | 
 | 5305 | err_bus_unregister: | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 5306 | 	bus_unregister(&cciss_bus_type); | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 5307 |  | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 5308 | 	return err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5309 | } | 
 | 5310 |  | 
 | 5311 | static void __exit cciss_cleanup(void) | 
 | 5312 | { | 
 | 5313 | 	int i; | 
 | 5314 |  | 
 | 5315 | 	pci_unregister_driver(&cciss_pci_driver); | 
 | 5316 | 	/* double check that all controller entrys have been removed */ | 
| Bjorn Helgaas | 7c83283 | 2006-06-25 05:49:06 -0700 | [diff] [blame] | 5317 | 	for (i = 0; i < MAX_CTLR; i++) { | 
 | 5318 | 		if (hba[i] != NULL) { | 
| Stephen M. Cameron | b2a4a43 | 2010-07-19 13:46:48 -0500 | [diff] [blame] | 5319 | 			dev_warn(&hba[i]->pdev->dev, | 
 | 5320 | 				"had to remove controller\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5321 | 			cciss_remove_one(hba[i]->pdev); | 
 | 5322 | 		} | 
 | 5323 | 	} | 
| Andrew Patterson | b368c9d | 2009-09-17 13:46:58 -0500 | [diff] [blame] | 5324 | 	kthread_stop(cciss_scan_thread); | 
| Jens Axboe | 90fdb0b | 2010-11-08 14:29:13 +0100 | [diff] [blame] | 5325 | 	if (proc_cciss) | 
 | 5326 | 		remove_proc_entry("driver/cciss", NULL); | 
| Andrew Patterson | 7fe0632 | 2009-06-02 14:48:39 +0200 | [diff] [blame] | 5327 | 	bus_unregister(&cciss_bus_type); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5328 | } | 
 | 5329 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5330 | module_init(cciss_init); | 
 | 5331 | module_exit(cciss_cleanup); |