]>
Commit | Line | Data |
---|---|---|
dc950861 KC |
1 | /* virthba.c |
2 | * | |
f6d0c1e6 | 3 | * Copyright (C) 2010 - 2013 UNISYS CORPORATION |
dc950861 KC |
4 | * All rights reserved. |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or (at | |
9 | * your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
14 | * NON INFRINGEMENT. See the GNU General Public License for more | |
15 | * details. | |
16 | */ | |
17 | ||
18 | #define EXPORT_SYMTAB | |
19 | ||
20 | /* if you want to turn on some debugging of write device data or read | |
21 | * device data, define these two undefs. You will probably want to | |
22 | * customize the code which is here since it was written assuming | |
23 | * reading and writing a specific data file df.64M.txt which is a | |
24 | * 64Megabyte file created by Art Nilson using a scritp I wrote called | |
25 | * cr_test_data.pl. The data file consists of 256 byte lines of text | |
26 | * which start with an 8 digit sequence number, a colon, and then | |
27 | * letters after that */ | |
28 | ||
dc950861 KC |
29 | #include <linux/kernel.h> |
30 | #ifdef CONFIG_MODVERSIONS | |
31 | #include <config/modversions.h> | |
32 | #endif | |
33 | ||
dc950861 KC |
34 | #include "diagnostics/appos_subsystems.h" |
35 | #include "uisutils.h" | |
36 | #include "uisqueue.h" | |
37 | #include "uisthread.h" | |
38 | ||
39 | #include <linux/module.h> | |
40 | #include <linux/init.h> | |
41 | #include <linux/pci.h> | |
42 | #include <linux/spinlock.h> | |
43 | #include <linux/device.h> | |
44 | #include <linux/slab.h> | |
45 | #include <scsi/scsi.h> | |
46 | #include <scsi/scsi_host.h> | |
47 | #include <scsi/scsi_cmnd.h> | |
48 | #include <scsi/scsi_device.h> | |
49 | #include <asm/param.h> | |
d9c5607e | 50 | #include <linux/debugfs.h> |
dc950861 KC |
51 | #include <linux/types.h> |
52 | ||
53 | #include "virthba.h" | |
54 | #include "virtpci.h" | |
55 | #include "visorchipset.h" | |
56 | #include "version.h" | |
57 | #include "guestlinuxdebug.h" | |
58 | /* this is shorter than using __FILE__ (full path name) in | |
59 | * debug/info/error messages | |
60 | */ | |
61 | #define CURRENT_FILE_PC VIRT_HBA_PC_virthba_c | |
62 | #define __MYFILE__ "virthba.c" | |
63 | ||
64 | /* NOTE: L1_CACHE_BYTES >=128 */ | |
65 | #define DEVICE_ATTRIBUTE struct device_attribute | |
66 | ||
d9c5607e EA |
67 | /* MAX_BUF = 6 lines x 10 MAXVHBA x 80 characters |
68 | * = 4800 bytes ~ 2^13 = 8192 bytes | |
69 | */ | |
70 | #define MAX_BUF 8192 | |
71 | ||
dc950861 KC |
72 | /*****************************************************/ |
73 | /* Forward declarations */ | |
74 | /*****************************************************/ | |
75 | static int virthba_probe(struct virtpci_dev *dev, | |
76 | const struct pci_device_id *id); | |
77 | static void virthba_remove(struct virtpci_dev *dev); | |
78 | static int virthba_abort_handler(struct scsi_cmnd *scsicmd); | |
79 | static int virthba_bus_reset_handler(struct scsi_cmnd *scsicmd); | |
80 | static int virthba_device_reset_handler(struct scsi_cmnd *scsicmd); | |
81 | static int virthba_host_reset_handler(struct scsi_cmnd *scsicmd); | |
82 | static const char *virthba_get_info(struct Scsi_Host *shp); | |
83 | static int virthba_ioctl(struct scsi_device *dev, int cmd, void __user *arg); | |
84 | static int virthba_queue_command_lck(struct scsi_cmnd *scsicmd, | |
fb649950 KD |
85 | void (*virthba_cmnd_done) |
86 | (struct scsi_cmnd *)); | |
a8d7f21d | 87 | |
87995171 KC |
88 | static const struct x86_cpu_id unisys_spar_ids[] = { |
89 | { X86_VENDOR_INTEL, 6, 62, X86_FEATURE_ANY }, | |
90 | {} | |
91 | }; | |
92 | ||
93 | /* Autoload */ | |
94 | MODULE_DEVICE_TABLE(x86cpu, unisys_spar_ids); | |
95 | ||
dc950861 | 96 | #ifdef DEF_SCSI_QCMD |
385914c3 | 97 | static DEF_SCSI_QCMD(virthba_queue_command) |
dc950861 KC |
98 | #else |
99 | #define virthba_queue_command virthba_queue_command_lck | |
100 | #endif | |
a8d7f21d | 101 | |
dc950861 KC |
102 | static int virthba_slave_alloc(struct scsi_device *scsidev); |
103 | static int virthba_slave_configure(struct scsi_device *scsidev); | |
104 | static void virthba_slave_destroy(struct scsi_device *scsidev); | |
105 | static int process_incoming_rsps(void *); | |
106 | static int virthba_serverup(struct virtpci_dev *virtpcidev); | |
107 | static int virthba_serverdown(struct virtpci_dev *virtpcidev, u32 state); | |
b6e0a708 | 108 | static void do_disk_add_remove(struct work_struct *work); |
dc950861 | 109 | static void virthba_serverdown_complete(struct work_struct *work); |
d9c5607e | 110 | static ssize_t info_debugfs_read(struct file *file, char __user *buf, |
836600fa | 111 | size_t len, loff_t *offset); |
79014eb1 | 112 | static ssize_t enable_ints_write(struct file *file, |
836600fa KD |
113 | const char __user *buffer, size_t count, |
114 | loff_t *ppos); | |
dc950861 | 115 | |
dc950861 KC |
116 | /*****************************************************/ |
117 | /* Globals */ | |
118 | /*****************************************************/ | |
119 | ||
a8d7f21d | 120 | static int rsltq_wait_usecs = 4000; /* Default 4ms */ |
366cf71a | 121 | static unsigned int max_buff_len; |
dc950861 KC |
122 | |
123 | /* Module options */ | |
a8d7f21d | 124 | static char *virthba_options = "NONE"; |
dc950861 KC |
125 | |
126 | static const struct pci_device_id virthba_id_table[] = { | |
127 | {PCI_DEVICE(PCI_VENDOR_ID_UNISYS, PCI_DEVICE_ID_VIRTHBA)}, | |
128 | {0}, | |
129 | }; | |
130 | ||
131 | /* export virthba_id_table */ | |
132 | MODULE_DEVICE_TABLE(pci, virthba_id_table); | |
133 | ||
134 | static struct workqueue_struct *virthba_serverdown_workqueue; | |
135 | ||
136 | static struct virtpci_driver virthba_driver = { | |
137 | .name = "uisvirthba", | |
138 | .version = VERSION, | |
139 | .vertag = NULL, | |
dc950861 KC |
140 | .id_table = virthba_id_table, |
141 | .probe = virthba_probe, | |
142 | .remove = virthba_remove, | |
143 | .resume = virthba_serverup, | |
144 | .suspend = virthba_serverdown | |
145 | }; | |
146 | ||
147 | /* The Send and Recive Buffers of the IO Queue may both be full */ | |
148 | #define MAX_PENDING_REQUESTS (MIN_NUMSIGNALS*2) | |
149 | #define INTERRUPT_VECTOR_MASK 0x3F | |
150 | ||
151 | struct scsipending { | |
152 | char cmdtype; /* Type of pointer that is being stored */ | |
153 | void *sent; /* The Data being tracked */ | |
154 | /* struct scsi_cmnd *type for virthba_queue_command */ | |
155 | /* struct uiscmdrsp *type for management commands */ | |
156 | }; | |
157 | ||
158 | #define VIRTHBA_ERROR_COUNT 30 | |
159 | #define IOS_ERROR_THRESHOLD 1000 | |
160 | struct virtdisk_info { | |
b3c55b13 BR |
161 | u32 valid; |
162 | u32 channel, id, lun; /* Disk Path */ | |
dc950861 KC |
163 | atomic_t ios_threshold; |
164 | atomic_t error_count; | |
165 | struct virtdisk_info *next; | |
166 | }; | |
1cc598c7 | 167 | |
dc950861 KC |
168 | /* Each Scsi_Host has a host_data area that contains this struct. */ |
169 | struct virthba_info { | |
170 | struct Scsi_Host *scsihost; | |
171 | struct virtpci_dev *virtpcidev; | |
172 | struct list_head dev_info_list; | |
173 | struct chaninfo chinfo; | |
4eddbf13 | 174 | struct irq_info intr; /* use recvInterrupt info to receive |
dc950861 KC |
175 | interrupts when IOs complete */ |
176 | int interrupt_vector; | |
177 | struct scsipending pending[MAX_PENDING_REQUESTS]; /* Tracks the requests | |
178 | that have been */ | |
179 | /* forwarded to the IOVM and haven't returned yet */ | |
180 | unsigned int nextinsert; /* Start search for next pending | |
181 | free slot here */ | |
182 | spinlock_t privlock; | |
183 | bool serverdown; | |
184 | bool serverchangingstate; | |
185 | unsigned long long acquire_failed_cnt; | |
186 | unsigned long long interrupts_rcvd; | |
187 | unsigned long long interrupts_notme; | |
188 | unsigned long long interrupts_disabled; | |
189 | struct work_struct serverdown_completion; | |
5fc0229a | 190 | u64 __iomem *flags_addr; |
dc950861 KC |
191 | atomic_t interrupt_rcvd; |
192 | wait_queue_head_t rsp_queue; | |
193 | struct virtdisk_info head; | |
194 | }; | |
195 | ||
83afa349 | 196 | /* Work Data for dar_work_queue */ |
dc950861 | 197 | struct diskaddremove { |
c242233e | 198 | u8 add; /* 0-remove, 1-add */ |
dc950861 | 199 | struct Scsi_Host *shost; /* Scsi Host for this virthba instance */ |
b3c55b13 | 200 | u32 channel, id, lun; /* Disk Path */ |
dc950861 KC |
201 | struct diskaddremove *next; |
202 | }; | |
203 | ||
204 | #define virtpci_dev_to_virthba_virthba_get_info(d) \ | |
205 | container_of(d, struct virthba_info, virtpcidev) | |
206 | ||
207 | static DEVICE_ATTRIBUTE *virthba_shost_attrs[]; | |
208 | static struct scsi_host_template virthba_driver_template = { | |
209 | .name = "Unisys Virtual HBA", | |
dc950861 KC |
210 | .info = virthba_get_info, |
211 | .ioctl = virthba_ioctl, | |
212 | .queuecommand = virthba_queue_command, | |
213 | .eh_abort_handler = virthba_abort_handler, | |
214 | .eh_device_reset_handler = virthba_device_reset_handler, | |
215 | .eh_bus_reset_handler = virthba_bus_reset_handler, | |
216 | .eh_host_reset_handler = virthba_host_reset_handler, | |
217 | .shost_attrs = virthba_shost_attrs, | |
218 | ||
219 | #define VIRTHBA_MAX_CMNDS 128 | |
220 | .can_queue = VIRTHBA_MAX_CMNDS, | |
221 | .sg_tablesize = 64, /* largest number of address/length pairs */ | |
222 | .this_id = -1, | |
223 | .slave_alloc = virthba_slave_alloc, | |
224 | .slave_configure = virthba_slave_configure, | |
225 | .slave_destroy = virthba_slave_destroy, | |
226 | .use_clustering = ENABLE_CLUSTERING, | |
227 | }; | |
228 | ||
229 | struct virthba_devices_open { | |
230 | struct virthba_info *virthbainfo; | |
231 | }; | |
232 | ||
d9c5607e EA |
233 | static const struct file_operations debugfs_info_fops = { |
234 | .read = info_debugfs_read, | |
235 | }; | |
236 | ||
79014eb1 EA |
237 | static const struct file_operations debugfs_enable_ints_fops = { |
238 | .write = enable_ints_write, | |
239 | }; | |
240 | ||
d9c5607e EA |
241 | /*****************************************************/ |
242 | /* Structs */ | |
243 | /*****************************************************/ | |
244 | ||
dc950861 KC |
245 | #define VIRTHBASOPENMAX 1 |
246 | /* array of open devices maintained by open() and close(); */ | |
366cf71a | 247 | static struct virthba_devices_open virthbas_open[VIRTHBASOPENMAX]; |
d9c5607e | 248 | static struct dentry *virthba_debugfs_dir; |
dc950861 KC |
249 | |
250 | /*****************************************************/ | |
251 | /* Local Functions */ | |
252 | /*****************************************************/ | |
253 | static int | |
254 | add_scsipending_entry(struct virthba_info *vhbainfo, char cmdtype, void *new) | |
255 | { | |
256 | unsigned long flags; | |
257 | int insert_location; | |
258 | ||
259 | spin_lock_irqsave(&vhbainfo->privlock, flags); | |
260 | insert_location = vhbainfo->nextinsert; | |
5af3b098 | 261 | while (vhbainfo->pending[insert_location].sent) { |
dc950861 | 262 | insert_location = (insert_location + 1) % MAX_PENDING_REQUESTS; |
396beed6 | 263 | if (insert_location == (int)vhbainfo->nextinsert) { |
dc950861 KC |
264 | spin_unlock_irqrestore(&vhbainfo->privlock, flags); |
265 | return -1; | |
266 | } | |
267 | } | |
268 | ||
269 | vhbainfo->pending[insert_location].cmdtype = cmdtype; | |
270 | vhbainfo->pending[insert_location].sent = new; | |
271 | vhbainfo->nextinsert = (insert_location + 1) % MAX_PENDING_REQUESTS; | |
272 | spin_unlock_irqrestore(&vhbainfo->privlock, flags); | |
273 | ||
274 | return insert_location; | |
275 | } | |
276 | ||
277 | static unsigned int | |
278 | add_scsipending_entry_with_wait(struct virthba_info *vhbainfo, char cmdtype, | |
279 | void *new) | |
280 | { | |
281 | int insert_location = add_scsipending_entry(vhbainfo, cmdtype, new); | |
282 | ||
283 | while (insert_location == -1) { | |
dc950861 KC |
284 | set_current_state(TASK_INTERRUPTIBLE); |
285 | schedule_timeout(msecs_to_jiffies(10)); | |
286 | insert_location = add_scsipending_entry(vhbainfo, cmdtype, new); | |
287 | } | |
288 | ||
396beed6 | 289 | return (unsigned int)insert_location; |
dc950861 KC |
290 | } |
291 | ||
292 | static void * | |
293 | del_scsipending_entry(struct virthba_info *vhbainfo, uintptr_t del) | |
294 | { | |
295 | unsigned long flags; | |
296 | void *sent = NULL; | |
297 | ||
0aca7844 | 298 | if (del < MAX_PENDING_REQUESTS) { |
dc950861 | 299 | spin_lock_irqsave(&vhbainfo->privlock, flags); |
dc950861 KC |
300 | sent = vhbainfo->pending[del].sent; |
301 | ||
302 | vhbainfo->pending[del].cmdtype = 0; | |
303 | vhbainfo->pending[del].sent = NULL; | |
304 | spin_unlock_irqrestore(&vhbainfo->privlock, flags); | |
305 | } | |
306 | ||
307 | return sent; | |
308 | } | |
309 | ||
83afa349 KD |
310 | /* dar_work_queue (Disk Add/Remove) */ |
311 | static struct work_struct dar_work_queue; | |
312 | static struct diskaddremove *dar_work_queue_head; | |
313 | static spinlock_t dar_work_queue_lock; | |
314 | static unsigned short dar_work_queue_sched; | |
dc950861 | 315 | #define QUEUE_DISKADDREMOVE(dar) { \ |
83afa349 KD |
316 | spin_lock_irqsave(&dar_work_queue_lock, flags); \ |
317 | if (!dar_work_queue_head) { \ | |
318 | dar_work_queue_head = dar; \ | |
dc950861 KC |
319 | dar->next = NULL; \ |
320 | } \ | |
321 | else { \ | |
83afa349 KD |
322 | dar->next = dar_work_queue_head; \ |
323 | dar_work_queue_head = dar; \ | |
dc950861 | 324 | } \ |
83afa349 KD |
325 | if (!dar_work_queue_sched) { \ |
326 | schedule_work(&dar_work_queue); \ | |
327 | dar_work_queue_sched = 1; \ | |
dc950861 | 328 | } \ |
83afa349 | 329 | spin_unlock_irqrestore(&dar_work_queue_lock, flags); \ |
dc950861 KC |
330 | } |
331 | ||
332 | static inline void | |
b6e0a708 | 333 | send_disk_add_remove(struct diskaddremove *dar) |
dc950861 KC |
334 | { |
335 | struct scsi_device *sdev; | |
336 | int error; | |
337 | ||
338 | sdev = scsi_device_lookup(dar->shost, dar->channel, dar->id, dar->lun); | |
339 | if (sdev) { | |
340 | if (!(dar->add)) | |
341 | scsi_remove_device(sdev); | |
342 | } else if (dar->add) { | |
343 | error = | |
344 | scsi_add_device(dar->shost, dar->channel, dar->id, | |
345 | dar->lun); | |
0aca7844 | 346 | } |
dc950861 KC |
347 | kfree(dar); |
348 | } | |
349 | ||
350 | /*****************************************************/ | |
83afa349 | 351 | /* dar_work_queue Handler Thread */ |
dc950861 KC |
352 | /*****************************************************/ |
353 | static void | |
b6e0a708 | 354 | do_disk_add_remove(struct work_struct *work) |
dc950861 KC |
355 | { |
356 | struct diskaddremove *dar; | |
357 | struct diskaddremove *tmphead; | |
358 | int i = 0; | |
359 | unsigned long flags; | |
360 | ||
83afa349 KD |
361 | spin_lock_irqsave(&dar_work_queue_lock, flags); |
362 | tmphead = dar_work_queue_head; | |
363 | dar_work_queue_head = NULL; | |
364 | dar_work_queue_sched = 0; | |
365 | spin_unlock_irqrestore(&dar_work_queue_lock, flags); | |
dc950861 KC |
366 | while (tmphead) { |
367 | dar = tmphead; | |
368 | tmphead = dar->next; | |
b6e0a708 | 369 | send_disk_add_remove(dar); |
dc950861 KC |
370 | i++; |
371 | } | |
372 | } | |
373 | ||
374 | /*****************************************************/ | |
83afa349 | 375 | /* Routine to add entry to dar_work_queue */ |
dc950861 KC |
376 | /*****************************************************/ |
377 | static void | |
378 | process_disk_notify(struct Scsi_Host *shost, struct uiscmdrsp *cmdrsp) | |
379 | { | |
380 | struct diskaddremove *dar; | |
381 | unsigned long flags; | |
382 | ||
c7553dad | 383 | dar = kzalloc(sizeof(*dar), GFP_ATOMIC); |
dc950861 | 384 | if (dar) { |
dc950861 KC |
385 | dar->add = cmdrsp->disknotify.add; |
386 | dar->shost = shost; | |
387 | dar->channel = cmdrsp->disknotify.channel; | |
388 | dar->id = cmdrsp->disknotify.id; | |
389 | dar->lun = cmdrsp->disknotify.lun; | |
390 | QUEUE_DISKADDREMOVE(dar); | |
dc950861 KC |
391 | } |
392 | } | |
393 | ||
394 | /*****************************************************/ | |
395 | /* Probe Remove Functions */ | |
396 | /*****************************************************/ | |
a8d7f21d | 397 | static irqreturn_t |
347d0560 | 398 | virthba_isr(int irq, void *dev_id) |
dc950861 | 399 | { |
396beed6 | 400 | struct virthba_info *virthbainfo = (struct virthba_info *)dev_id; |
347d0560 | 401 | struct channel_header __iomem *channel_header; |
e0fed862 | 402 | struct signal_queue_header __iomem *pqhdr; |
5fc0229a | 403 | u64 mask; |
dc950861 KC |
404 | unsigned long long rc1; |
405 | ||
5af3b098 | 406 | if (!virthbainfo) |
dc950861 KC |
407 | return IRQ_NONE; |
408 | virthbainfo->interrupts_rcvd++; | |
347d0560 BR |
409 | channel_header = virthbainfo->chinfo.queueinfo->chan; |
410 | if (((readq(&channel_header->features) | |
45104bf9 | 411 | & ULTRA_IO_IOVM_IS_OK_WITH_DRIVER_DISABLING_INTS) != 0) && |
347d0560 | 412 | ((readq(&channel_header->features) & |
a8d7f21d | 413 | ULTRA_IO_DRIVER_DISABLES_INTS) != |
dc950861 KC |
414 | 0)) { |
415 | virthbainfo->interrupts_disabled++; | |
416 | mask = ~ULTRA_CHANNEL_ENABLE_INTS; | |
17f5e491 | 417 | rc1 = uisqueue_interlocked_and(virthbainfo->flags_addr, mask); |
dc950861 | 418 | } |
347d0560 | 419 | if (spar_signalqueue_empty(channel_header, IOCHAN_FROM_IOPART)) { |
dc950861 KC |
420 | virthbainfo->interrupts_notme++; |
421 | return IRQ_NONE; | |
422 | } | |
e0fed862 | 423 | pqhdr = (struct signal_queue_header __iomem *) |
347d0560 BR |
424 | ((char __iomem *)channel_header + |
425 | readq(&channel_header->ch_space_offset)) + IOCHAN_FROM_IOPART; | |
153cf710 BR |
426 | writeq(readq(&pqhdr->num_irq_received) + 1, |
427 | &pqhdr->num_irq_received); | |
dc950861 KC |
428 | atomic_set(&virthbainfo->interrupt_rcvd, 1); |
429 | wake_up_interruptible(&virthbainfo->rsp_queue); | |
430 | return IRQ_HANDLED; | |
431 | } | |
432 | ||
433 | static int | |
434 | virthba_probe(struct virtpci_dev *virtpcidev, const struct pci_device_id *id) | |
435 | { | |
436 | int error; | |
437 | struct Scsi_Host *scsihost; | |
438 | struct virthba_info *virthbainfo; | |
439 | int rsp; | |
440 | int i; | |
347d0560 | 441 | irq_handler_t handler = virthba_isr; |
17893770 | 442 | struct channel_header __iomem *channel_header; |
e0fed862 | 443 | struct signal_queue_header __iomem *pqhdr; |
5fc0229a | 444 | u64 mask; |
dc950861 | 445 | |
dc950861 KC |
446 | POSTCODE_LINUX_2(VHBA_PROBE_ENTRY_PC, POSTCODE_SEVERITY_INFO); |
447 | /* call scsi_host_alloc to register a scsi host adapter | |
448 | * instance - this virthba that has just been created is an | |
449 | * instance of a scsi host adapter. This scsi_host_alloc | |
450 | * function allocates a new Scsi_Host struct & performs basic | |
659688a5 | 451 | * initialization. The host is not published to the scsi |
dc950861 KC |
452 | * midlayer until scsi_add_host is called. |
453 | */ | |
dc950861 KC |
454 | |
455 | /* arg 2 passed in length of extra space we want allocated | |
456 | * with scsi_host struct for our own use scsi_host_alloc | |
457 | * assign host_no | |
458 | */ | |
459 | scsihost = scsi_host_alloc(&virthba_driver_template, | |
460 | sizeof(struct virthba_info)); | |
5af3b098 | 461 | if (!scsihost) |
dc950861 KC |
462 | return -ENODEV; |
463 | ||
dc950861 KC |
464 | scsihost->this_id = UIS_MAGIC_VHBA; |
465 | /* linux treats max-channel differently than max-id & max-lun. | |
466 | * In the latter cases, those two values result in 0 to max-1 | |
467 | * (inclusive) being scanned. But in the case of channels, the | |
468 | * scan is 0 to max (inclusive); so we will subtract one from | |
469 | * the max-channel value. | |
470 | */ | |
396beed6 KD |
471 | scsihost->max_channel = (unsigned)virtpcidev->scsi.max.max_channel; |
472 | scsihost->max_id = (unsigned)virtpcidev->scsi.max.max_id; | |
473 | scsihost->max_lun = (unsigned)virtpcidev->scsi.max.max_lun; | |
474 | scsihost->cmd_per_lun = (unsigned)virtpcidev->scsi.max.cmd_per_lun; | |
dc950861 | 475 | scsihost->max_sectors = |
396beed6 | 476 | (unsigned short)(virtpcidev->scsi.max.max_io_size >> 9); |
dc950861 | 477 | scsihost->sg_tablesize = |
396beed6 | 478 | (unsigned short)(virtpcidev->scsi.max.max_io_size / PAGE_SIZE); |
dc950861 KC |
479 | if (scsihost->sg_tablesize > MAX_PHYS_INFO) |
480 | scsihost->sg_tablesize = MAX_PHYS_INFO; | |
dc950861 | 481 | |
dc950861 KC |
482 | /* this creates "host%d" in sysfs. If 2nd argument is NULL, |
483 | * then this generic /sys/devices/platform/host? device is | |
484 | * created and /sys/scsi_host/host? -> | |
485 | * /sys/devices/platform/host? If 2nd argument is not NULL, | |
486 | * then this generic /sys/devices/<path>/host? is created and | |
487 | * host? points to that device instead. | |
488 | */ | |
489 | error = scsi_add_host(scsihost, &virtpcidev->generic_dev); | |
490 | if (error) { | |
dc950861 KC |
491 | POSTCODE_LINUX_2(VHBA_PROBE_FAILURE_PC, POSTCODE_SEVERITY_ERR); |
492 | /* decr refcount on scsihost which was incremented by | |
493 | * scsi_add_host so the scsi_host gets deleted | |
494 | */ | |
495 | scsi_host_put(scsihost); | |
496 | return -ENODEV; | |
497 | } | |
498 | ||
396beed6 | 499 | virthbainfo = (struct virthba_info *)scsihost->hostdata; |
dc950861 KC |
500 | memset(virthbainfo, 0, sizeof(struct virthba_info)); |
501 | for (i = 0; i < VIRTHBASOPENMAX; i++) { | |
5af3b098 | 502 | if (!virthbas_open[i].virthbainfo) { |
366cf71a | 503 | virthbas_open[i].virthbainfo = virthbainfo; |
dc950861 KC |
504 | break; |
505 | } | |
506 | } | |
507 | virthbainfo->interrupt_vector = -1; | |
508 | virthbainfo->chinfo.queueinfo = &virtpcidev->queueinfo; | |
509 | virthbainfo->virtpcidev = virtpcidev; | |
510 | spin_lock_init(&virthbainfo->chinfo.insertlock); | |
511 | ||
dc950861 KC |
512 | init_waitqueue_head(&virthbainfo->rsp_queue); |
513 | spin_lock_init(&virthbainfo->privlock); | |
514 | memset(&virthbainfo->pending, 0, sizeof(virthbainfo->pending)); | |
515 | virthbainfo->serverdown = false; | |
516 | virthbainfo->serverchangingstate = false; | |
517 | ||
518 | virthbainfo->intr = virtpcidev->intr; | |
519 | /* save of host within virthba_info */ | |
520 | virthbainfo->scsihost = scsihost; | |
521 | ||
522 | /* save of host within virtpci_dev */ | |
523 | virtpcidev->scsi.scsihost = scsihost; | |
524 | ||
525 | /* Setup workqueue for serverdown messages */ | |
526 | INIT_WORK(&virthbainfo->serverdown_completion, | |
527 | virthba_serverdown_complete); | |
528 | ||
a8a31f61 | 529 | writeq(readq(&virthbainfo->chinfo.queueinfo->chan->features) | |
a8d7f21d | 530 | ULTRA_IO_CHANNEL_IS_POLLING, |
a8a31f61 | 531 | &virthbainfo->chinfo.queueinfo->chan->features); |
dc950861 | 532 | /* start thread that will receive scsicmnd responses */ |
dc950861 | 533 | |
17893770 | 534 | channel_header = virthbainfo->chinfo.queueinfo->chan; |
e0fed862 | 535 | pqhdr = (struct signal_queue_header __iomem *) |
17893770 BR |
536 | ((char __iomem *)channel_header + |
537 | readq(&channel_header->ch_space_offset)) + IOCHAN_FROM_IOPART; | |
153cf710 | 538 | virthbainfo->flags_addr = &pqhdr->features; |
dc950861 KC |
539 | |
540 | if (!uisthread_start(&virthbainfo->chinfo.threadinfo, | |
541 | process_incoming_rsps, | |
542 | virthbainfo, "vhba_incoming")) { | |
dc950861 KC |
543 | /* decr refcount on scsihost which was incremented by |
544 | * scsi_add_host so the scsi_host gets deleted | |
545 | */ | |
546 | POSTCODE_LINUX_2(VHBA_PROBE_FAILURE_PC, POSTCODE_SEVERITY_ERR); | |
547 | scsi_host_put(scsihost); | |
548 | return -ENODEV; | |
549 | } | |
dc950861 | 550 | virthbainfo->interrupt_vector = |
af3286bd | 551 | virthbainfo->intr.recv_irq_handle & INTERRUPT_VECTOR_MASK; |
dc950861 KC |
552 | rsp = request_irq(virthbainfo->interrupt_vector, handler, IRQF_SHARED, |
553 | scsihost->hostt->name, virthbainfo); | |
554 | if (rsp != 0) { | |
dc950861 KC |
555 | virthbainfo->interrupt_vector = -1; |
556 | POSTCODE_LINUX_2(VHBA_PROBE_FAILURE_PC, POSTCODE_SEVERITY_ERR); | |
557 | } else { | |
56215add | 558 | u64 __iomem *features_addr = |
a8a31f61 | 559 | &virthbainfo->chinfo.queueinfo->chan->features; |
dc950861 KC |
560 | mask = ~(ULTRA_IO_CHANNEL_IS_POLLING | |
561 | ULTRA_IO_DRIVER_DISABLES_INTS); | |
56215add | 562 | uisqueue_interlocked_and(features_addr, mask); |
dc950861 | 563 | mask = ULTRA_IO_DRIVER_ENABLES_INTS; |
56215add | 564 | uisqueue_interlocked_or(features_addr, mask); |
dc950861 KC |
565 | rsltq_wait_usecs = 4000000; |
566 | } | |
567 | ||
dc950861 | 568 | scsi_scan_host(scsihost); |
dc950861 | 569 | |
dc950861 KC |
570 | POSTCODE_LINUX_2(VHBA_PROBE_EXIT_PC, POSTCODE_SEVERITY_INFO); |
571 | return 0; | |
572 | } | |
573 | ||
574 | static void | |
575 | virthba_remove(struct virtpci_dev *virtpcidev) | |
576 | { | |
577 | struct virthba_info *virthbainfo; | |
578 | struct Scsi_Host *scsihost = | |
396beed6 | 579 | (struct Scsi_Host *)virtpcidev->scsi.scsihost; |
dc950861 | 580 | |
396beed6 | 581 | virthbainfo = (struct virthba_info *)scsihost->hostdata; |
dc950861 KC |
582 | if (virthbainfo->interrupt_vector != -1) |
583 | free_irq(virthbainfo->interrupt_vector, virthbainfo); | |
dc950861 | 584 | |
dc950861 KC |
585 | scsi_remove_host(scsihost); |
586 | ||
dc950861 KC |
587 | uisthread_stop(&virthbainfo->chinfo.threadinfo); |
588 | ||
dc950861 KC |
589 | /* decr refcount on scsihost which was incremented by |
590 | * scsi_add_host so the scsi_host gets deleted | |
591 | */ | |
592 | scsi_host_put(scsihost); | |
dc950861 KC |
593 | } |
594 | ||
595 | static int | |
c8cf5d07 | 596 | forward_vdiskmgmt_command(enum vdisk_mgmt_types vdiskcmdtype, |
dc950861 KC |
597 | struct Scsi_Host *scsihost, |
598 | struct uisscsi_dest *vdest) | |
599 | { | |
600 | struct uiscmdrsp *cmdrsp; | |
601 | struct virthba_info *virthbainfo = | |
396beed6 | 602 | (struct virthba_info *)scsihost->hostdata; |
dc950861 KC |
603 | int notifyresult = 0xffff; |
604 | wait_queue_head_t notifyevent; | |
605 | ||
4de57acb | 606 | if (virthbainfo->serverdown || virthbainfo->serverchangingstate) |
dc950861 | 607 | return FAILED; |
dc950861 | 608 | |
a3acc83a | 609 | cmdrsp = kzalloc(SIZEOF_CMDRSP, GFP_ATOMIC); |
5af3b098 | 610 | if (!cmdrsp) |
edff162c | 611 | return FAILED; /* reject */ |
dc950861 KC |
612 | |
613 | init_waitqueue_head(¬ifyevent); | |
614 | ||
615 | /* issue VDISK_MGMT_CMD | |
616 | * set type to command - as opposed to task mgmt | |
617 | */ | |
618 | cmdrsp->cmdtype = CMD_VDISKMGMT_TYPE; | |
619 | /* specify the event that has to be triggered when this cmd is | |
620 | * complete | |
621 | */ | |
396beed6 KD |
622 | cmdrsp->vdiskmgmt.notify = (void *)¬ifyevent; |
623 | cmdrsp->vdiskmgmt.notifyresult = (void *)¬ifyresult; | |
dc950861 KC |
624 | |
625 | /* save destination */ | |
626 | cmdrsp->vdiskmgmt.vdisktype = vdiskcmdtype; | |
627 | cmdrsp->vdiskmgmt.vdest.channel = vdest->channel; | |
628 | cmdrsp->vdiskmgmt.vdest.id = vdest->id; | |
629 | cmdrsp->vdiskmgmt.vdest.lun = vdest->lun; | |
630 | cmdrsp->vdiskmgmt.scsicmd = | |
396beed6 | 631 | (void *)(uintptr_t) |
dc950861 | 632 | add_scsipending_entry_with_wait(virthbainfo, CMD_VDISKMGMT_TYPE, |
396beed6 | 633 | (void *)cmdrsp); |
dc950861 KC |
634 | |
635 | uisqueue_put_cmdrsp_with_lock_client(virthbainfo->chinfo.queueinfo, | |
636 | cmdrsp, IOCHAN_TO_IOPART, | |
637 | &virthbainfo->chinfo.insertlock, | |
396beed6 | 638 | DONT_ISSUE_INTERRUPT, (u64)NULL, |
dc950861 | 639 | OK_TO_WAIT, "vhba"); |
dc950861 | 640 | wait_event(notifyevent, notifyresult != 0xffff); |
dc950861 KC |
641 | kfree(cmdrsp); |
642 | return SUCCESS; | |
643 | } | |
644 | ||
645 | /*****************************************************/ | |
646 | /* Scsi Host support functions */ | |
647 | /*****************************************************/ | |
648 | ||
649 | static int | |
63f06ba1 BR |
650 | forward_taskmgmt_command(enum task_mgmt_types tasktype, |
651 | struct scsi_device *scsidev) | |
dc950861 KC |
652 | { |
653 | struct uiscmdrsp *cmdrsp; | |
654 | struct virthba_info *virthbainfo = | |
396beed6 | 655 | (struct virthba_info *)scsidev->host->hostdata; |
dc950861 KC |
656 | int notifyresult = 0xffff; |
657 | wait_queue_head_t notifyevent; | |
658 | ||
4de57acb | 659 | if (virthbainfo->serverdown || virthbainfo->serverchangingstate) |
dc950861 | 660 | return FAILED; |
dc950861 | 661 | |
a3acc83a | 662 | cmdrsp = kzalloc(SIZEOF_CMDRSP, GFP_ATOMIC); |
5af3b098 BR |
663 | if (!cmdrsp) |
664 | return FAILED; /* reject */ | |
dc950861 KC |
665 | |
666 | init_waitqueue_head(¬ifyevent); | |
667 | ||
668 | /* issue TASK_MGMT_ABORT_TASK */ | |
669 | /* set type to command - as opposed to task mgmt */ | |
670 | cmdrsp->cmdtype = CMD_SCSITASKMGMT_TYPE; | |
671 | /* specify the event that has to be triggered when this */ | |
672 | /* cmd is complete */ | |
396beed6 KD |
673 | cmdrsp->scsitaskmgmt.notify = (void *)¬ifyevent; |
674 | cmdrsp->scsitaskmgmt.notifyresult = (void *)¬ifyresult; | |
dc950861 KC |
675 | |
676 | /* save destination */ | |
677 | cmdrsp->scsitaskmgmt.tasktype = tasktype; | |
678 | cmdrsp->scsitaskmgmt.vdest.channel = scsidev->channel; | |
679 | cmdrsp->scsitaskmgmt.vdest.id = scsidev->id; | |
680 | cmdrsp->scsitaskmgmt.vdest.lun = scsidev->lun; | |
681 | cmdrsp->scsitaskmgmt.scsicmd = | |
396beed6 | 682 | (void *)(uintptr_t) |
dc950861 KC |
683 | add_scsipending_entry_with_wait(virthbainfo, |
684 | CMD_SCSITASKMGMT_TYPE, | |
396beed6 | 685 | (void *)cmdrsp); |
dc950861 KC |
686 | |
687 | uisqueue_put_cmdrsp_with_lock_client(virthbainfo->chinfo.queueinfo, | |
688 | cmdrsp, IOCHAN_TO_IOPART, | |
689 | &virthbainfo->chinfo.insertlock, | |
396beed6 | 690 | DONT_ISSUE_INTERRUPT, (u64)NULL, |
dc950861 | 691 | OK_TO_WAIT, "vhba"); |
dc950861 | 692 | wait_event(notifyevent, notifyresult != 0xffff); |
dc950861 KC |
693 | kfree(cmdrsp); |
694 | return SUCCESS; | |
695 | } | |
696 | ||
697 | /* The abort handler returns SUCCESS if it has succeeded to make LLDD | |
698 | * and all related hardware forget about the scmd. | |
699 | */ | |
700 | static int | |
701 | virthba_abort_handler(struct scsi_cmnd *scsicmd) | |
702 | { | |
703 | /* issue TASK_MGMT_ABORT_TASK */ | |
704 | struct scsi_device *scsidev; | |
705 | struct virtdisk_info *vdisk; | |
706 | ||
707 | scsidev = scsicmd->device; | |
396beed6 | 708 | for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head; |
dc950861 | 709 | vdisk->next; vdisk = vdisk->next) { |
45104bf9 KD |
710 | if ((scsidev->channel == vdisk->channel) && |
711 | (scsidev->id == vdisk->id) && | |
712 | (scsidev->lun == vdisk->lun)) { | |
dc950861 KC |
713 | if (atomic_read(&vdisk->error_count) < |
714 | VIRTHBA_ERROR_COUNT) { | |
715 | atomic_inc(&vdisk->error_count); | |
716 | POSTCODE_LINUX_2(VHBA_COMMAND_HANDLER_PC, | |
717 | POSTCODE_SEVERITY_INFO); | |
718 | } else | |
719 | atomic_set(&vdisk->ios_threshold, | |
720 | IOS_ERROR_THRESHOLD); | |
721 | } | |
722 | } | |
723 | return forward_taskmgmt_command(TASK_MGMT_ABORT_TASK, scsicmd->device); | |
724 | } | |
725 | ||
726 | static int | |
727 | virthba_bus_reset_handler(struct scsi_cmnd *scsicmd) | |
728 | { | |
729 | /* issue TASK_MGMT_TARGET_RESET for each target on the bus */ | |
730 | struct scsi_device *scsidev; | |
731 | struct virtdisk_info *vdisk; | |
732 | ||
733 | scsidev = scsicmd->device; | |
396beed6 | 734 | for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head; |
dc950861 | 735 | vdisk->next; vdisk = vdisk->next) { |
45104bf9 KD |
736 | if ((scsidev->channel == vdisk->channel) && |
737 | (scsidev->id == vdisk->id) && | |
738 | (scsidev->lun == vdisk->lun)) { | |
dc950861 KC |
739 | if (atomic_read(&vdisk->error_count) < |
740 | VIRTHBA_ERROR_COUNT) { | |
741 | atomic_inc(&vdisk->error_count); | |
742 | POSTCODE_LINUX_2(VHBA_COMMAND_HANDLER_PC, | |
743 | POSTCODE_SEVERITY_INFO); | |
744 | } else | |
745 | atomic_set(&vdisk->ios_threshold, | |
746 | IOS_ERROR_THRESHOLD); | |
747 | } | |
748 | } | |
749 | return forward_taskmgmt_command(TASK_MGMT_BUS_RESET, scsicmd->device); | |
750 | } | |
751 | ||
752 | static int | |
753 | virthba_device_reset_handler(struct scsi_cmnd *scsicmd) | |
754 | { | |
755 | /* issue TASK_MGMT_LUN_RESET */ | |
756 | struct scsi_device *scsidev; | |
757 | struct virtdisk_info *vdisk; | |
758 | ||
759 | scsidev = scsicmd->device; | |
396beed6 | 760 | for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head; |
dc950861 | 761 | vdisk->next; vdisk = vdisk->next) { |
45104bf9 KD |
762 | if ((scsidev->channel == vdisk->channel) && |
763 | (scsidev->id == vdisk->id) && | |
764 | (scsidev->lun == vdisk->lun)) { | |
dc950861 KC |
765 | if (atomic_read(&vdisk->error_count) < |
766 | VIRTHBA_ERROR_COUNT) { | |
767 | atomic_inc(&vdisk->error_count); | |
768 | POSTCODE_LINUX_2(VHBA_COMMAND_HANDLER_PC, | |
769 | POSTCODE_SEVERITY_INFO); | |
770 | } else | |
771 | atomic_set(&vdisk->ios_threshold, | |
772 | IOS_ERROR_THRESHOLD); | |
773 | } | |
774 | } | |
775 | return forward_taskmgmt_command(TASK_MGMT_LUN_RESET, scsicmd->device); | |
776 | } | |
777 | ||
778 | static int | |
779 | virthba_host_reset_handler(struct scsi_cmnd *scsicmd) | |
780 | { | |
781 | /* issue TASK_MGMT_TARGET_RESET for each target on each bus for host */ | |
dc950861 KC |
782 | return SUCCESS; |
783 | } | |
784 | ||
785 | static char virthba_get_info_str[256]; | |
786 | ||
787 | static const char * | |
788 | virthba_get_info(struct Scsi_Host *shp) | |
789 | { | |
790 | /* Return version string */ | |
791 | sprintf(virthba_get_info_str, "virthba, version %s\n", VIRTHBA_VERSION); | |
792 | return virthba_get_info_str; | |
793 | } | |
794 | ||
795 | static int | |
796 | virthba_ioctl(struct scsi_device *dev, int cmd, void __user *arg) | |
797 | { | |
dc950861 KC |
798 | return -EINVAL; |
799 | } | |
800 | ||
801 | /* This returns SCSI_MLQUEUE_DEVICE_BUSY if the signal queue to IOpart | |
802 | * is full. | |
803 | */ | |
804 | static int | |
805 | virthba_queue_command_lck(struct scsi_cmnd *scsicmd, | |
806 | void (*virthba_cmnd_done)(struct scsi_cmnd *)) | |
807 | { | |
808 | struct scsi_device *scsidev = scsicmd->device; | |
809 | int insert_location; | |
810 | unsigned char op; | |
811 | unsigned char *cdb = scsicmd->cmnd; | |
812 | struct Scsi_Host *scsihost = scsidev->host; | |
813 | struct uiscmdrsp *cmdrsp; | |
814 | unsigned int i; | |
815 | struct virthba_info *virthbainfo = | |
396beed6 | 816 | (struct virthba_info *)scsihost->hostdata; |
dc950861 KC |
817 | struct scatterlist *sg = NULL; |
818 | struct scatterlist *sgl = NULL; | |
819 | int sg_failed = 0; | |
820 | ||
4de57acb | 821 | if (virthbainfo->serverdown || virthbainfo->serverchangingstate) |
dc950861 | 822 | return SCSI_MLQUEUE_DEVICE_BUSY; |
a3acc83a | 823 | cmdrsp = kzalloc(SIZEOF_CMDRSP, GFP_ATOMIC); |
5af3b098 | 824 | if (!cmdrsp) |
dc950861 | 825 | return 1; /* reject the command */ |
dc950861 KC |
826 | |
827 | /* now saving everything we need from scsi_cmd into cmdrsp | |
828 | * before we queue cmdrsp set type to command - as opposed to | |
829 | * task mgmt | |
830 | */ | |
831 | cmdrsp->cmdtype = CMD_SCSI_TYPE; | |
832 | /* save the pending insertion location. Deletion from pending | |
833 | * will return the scsicmd pointer for completion | |
834 | */ | |
835 | insert_location = | |
396beed6 | 836 | add_scsipending_entry(virthbainfo, CMD_SCSI_TYPE, (void *)scsicmd); |
dc950861 | 837 | if (insert_location != -1) { |
396beed6 | 838 | cmdrsp->scsi.scsicmd = (void *)(uintptr_t)insert_location; |
dc950861 | 839 | } else { |
dc950861 KC |
840 | kfree(cmdrsp); |
841 | return SCSI_MLQUEUE_DEVICE_BUSY; | |
842 | } | |
843 | /* save done function that we have call when cmd is complete */ | |
844 | scsicmd->scsi_done = virthba_cmnd_done; | |
845 | /* save destination */ | |
846 | cmdrsp->scsi.vdest.channel = scsidev->channel; | |
847 | cmdrsp->scsi.vdest.id = scsidev->id; | |
848 | cmdrsp->scsi.vdest.lun = scsidev->lun; | |
849 | /* save datadir */ | |
850 | cmdrsp->scsi.data_dir = scsicmd->sc_data_direction; | |
851 | memcpy(cmdrsp->scsi.cmnd, cdb, MAX_CMND_SIZE); | |
852 | ||
853 | cmdrsp->scsi.bufflen = scsi_bufflen(scsicmd); | |
854 | ||
855 | /* keep track of the max buffer length so far. */ | |
366cf71a KD |
856 | if (cmdrsp->scsi.bufflen > max_buff_len) |
857 | max_buff_len = cmdrsp->scsi.bufflen; | |
dc950861 KC |
858 | |
859 | if (scsi_sg_count(scsicmd) > MAX_PHYS_INFO) { | |
396beed6 | 860 | del_scsipending_entry(virthbainfo, (uintptr_t)insert_location); |
dc950861 KC |
861 | kfree(cmdrsp); |
862 | return 1; /* reject the command */ | |
863 | } | |
864 | ||
865 | /* This is what we USED to do when we assumed we were running */ | |
866 | /* uissd & virthba on the same Linux system. */ | |
867 | /* cmdrsp->scsi.buffer = scsicmd->request_buffer; */ | |
868 | /* The following code does NOT make that assumption. */ | |
869 | /* convert buffer to phys information */ | |
870 | if (scsi_sg_count(scsicmd) == 0) { | |
871 | if (scsi_bufflen(scsicmd) > 0) { | |
dc950861 KC |
872 | BUG_ON(scsi_sg_count(scsicmd) == 0); |
873 | } | |
dc950861 KC |
874 | } else { |
875 | /* buffer is scatterlist - copy it out */ | |
876 | sgl = scsi_sglist(scsicmd); | |
877 | ||
878 | for_each_sg(sgl, sg, scsi_sg_count(scsicmd), i) { | |
dc950861 KC |
879 | cmdrsp->scsi.gpi_list[i].address = sg_phys(sg); |
880 | cmdrsp->scsi.gpi_list[i].length = sg->length; | |
dc950861 KC |
881 | } |
882 | ||
883 | if (sg_failed) { | |
dc950861 KC |
884 | /* BUG(); ***** For now, let it fail in uissd |
885 | * if it is a problem, as it might just | |
886 | * work | |
887 | */ | |
888 | } | |
889 | ||
890 | cmdrsp->scsi.guest_phys_entries = scsi_sg_count(scsicmd); | |
891 | } | |
892 | ||
893 | op = cdb[0]; | |
894 | i = uisqueue_put_cmdrsp_with_lock_client(virthbainfo->chinfo.queueinfo, | |
895 | cmdrsp, IOCHAN_TO_IOPART, | |
896 | &virthbainfo->chinfo. | |
897 | insertlock, | |
898 | DONT_ISSUE_INTERRUPT, | |
396beed6 | 899 | (u64)NULL, DONT_WAIT, "vhba"); |
dc950861 KC |
900 | if (i == 0) { |
901 | /* queue must be full - and we said don't wait - return busy */ | |
dc950861 | 902 | kfree(cmdrsp); |
396beed6 | 903 | del_scsipending_entry(virthbainfo, (uintptr_t)insert_location); |
dc950861 KC |
904 | return SCSI_MLQUEUE_DEVICE_BUSY; |
905 | } | |
906 | ||
907 | /* we're done with cmdrsp space - data from it has been copied | |
908 | * into channel - free it now. | |
909 | */ | |
910 | kfree(cmdrsp); | |
911 | return 0; /* non-zero implies host/device is busy */ | |
912 | } | |
913 | ||
914 | static int | |
915 | virthba_slave_alloc(struct scsi_device *scsidev) | |
916 | { | |
917 | /* this called by the midlayer before scan for new devices - | |
fb90c609 | 918 | * LLD can alloc any struct & do init if needed. |
dc950861 KC |
919 | */ |
920 | struct virtdisk_info *vdisk; | |
921 | struct virtdisk_info *tmpvdisk; | |
922 | struct virthba_info *virthbainfo; | |
396beed6 | 923 | struct Scsi_Host *scsihost = (struct Scsi_Host *)scsidev->host; |
dc950861 | 924 | |
396beed6 | 925 | virthbainfo = (struct virthba_info *)scsihost->hostdata; |
0aca7844 | 926 | if (!virthbainfo) |
dc950861 | 927 | return 0; /* even though we errored, treat as success */ |
0aca7844 | 928 | |
dc950861 KC |
929 | for (vdisk = &virthbainfo->head; vdisk->next; vdisk = vdisk->next) { |
930 | if (vdisk->next->valid && | |
931 | (vdisk->next->channel == scsidev->channel) && | |
932 | (vdisk->next->id == scsidev->id) && | |
933 | (vdisk->next->lun == scsidev->lun)) | |
934 | return 0; | |
935 | } | |
c7553dad | 936 | tmpvdisk = kzalloc(sizeof(*tmpvdisk), GFP_ATOMIC); |
edff162c | 937 | if (!tmpvdisk) |
dc950861 | 938 | return 0; |
97a84f12 | 939 | |
dc950861 KC |
940 | tmpvdisk->channel = scsidev->channel; |
941 | tmpvdisk->id = scsidev->id; | |
942 | tmpvdisk->lun = scsidev->lun; | |
943 | tmpvdisk->valid = 1; | |
944 | vdisk->next = tmpvdisk; | |
945 | return 0; /* success */ | |
946 | } | |
947 | ||
948 | static int | |
949 | virthba_slave_configure(struct scsi_device *scsidev) | |
950 | { | |
951 | return 0; /* success */ | |
952 | } | |
953 | ||
954 | static void | |
955 | virthba_slave_destroy(struct scsi_device *scsidev) | |
956 | { | |
957 | /* midlevel calls this after device has been quiesced and | |
958 | * before it is to be deleted. | |
959 | */ | |
960 | struct virtdisk_info *vdisk, *delvdisk; | |
961 | struct virthba_info *virthbainfo; | |
396beed6 | 962 | struct Scsi_Host *scsihost = (struct Scsi_Host *)scsidev->host; |
dc950861 | 963 | |
396beed6 | 964 | virthbainfo = (struct virthba_info *)scsihost->hostdata; |
dc950861 KC |
965 | for (vdisk = &virthbainfo->head; vdisk->next; vdisk = vdisk->next) { |
966 | if (vdisk->next->valid && | |
967 | (vdisk->next->channel == scsidev->channel) && | |
968 | (vdisk->next->id == scsidev->id) && | |
969 | (vdisk->next->lun == scsidev->lun)) { | |
970 | delvdisk = vdisk->next; | |
971 | vdisk->next = vdisk->next->next; | |
972 | kfree(delvdisk); | |
973 | return; | |
974 | } | |
975 | } | |
dc950861 KC |
976 | } |
977 | ||
978 | /*****************************************************/ | |
979 | /* Scsi Cmnd support thread */ | |
980 | /*****************************************************/ | |
981 | ||
982 | static void | |
983 | do_scsi_linuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd) | |
984 | { | |
985 | struct virtdisk_info *vdisk; | |
986 | struct scsi_device *scsidev; | |
987 | struct sense_data *sd; | |
988 | ||
989 | scsidev = scsicmd->device; | |
990 | memcpy(scsicmd->sense_buffer, cmdrsp->scsi.sensebuf, MAX_SENSE_SIZE); | |
396beed6 | 991 | sd = (struct sense_data *)scsicmd->sense_buffer; |
dc950861 KC |
992 | |
993 | /* Do not log errors for disk-not-present inquiries */ | |
994 | if ((cmdrsp->scsi.cmnd[0] == INQUIRY) && | |
995 | (host_byte(cmdrsp->scsi.linuxstat) == DID_NO_CONNECT) && | |
996 | (cmdrsp->scsi.addlstat == ADDL_SEL_TIMEOUT)) | |
997 | return; | |
998 | ||
999 | /* Okay see what our error_count is here.... */ | |
396beed6 | 1000 | for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head; |
dc950861 | 1001 | vdisk->next; vdisk = vdisk->next) { |
45104bf9 KD |
1002 | if ((scsidev->channel != vdisk->channel) || |
1003 | (scsidev->id != vdisk->id) || | |
1004 | (scsidev->lun != vdisk->lun)) | |
dc950861 KC |
1005 | continue; |
1006 | ||
1007 | if (atomic_read(&vdisk->error_count) < VIRTHBA_ERROR_COUNT) { | |
1008 | atomic_inc(&vdisk->error_count); | |
dc950861 KC |
1009 | atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD); |
1010 | } | |
1011 | } | |
1012 | } | |
1013 | ||
1014 | static void | |
1015 | do_scsi_nolinuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd) | |
1016 | { | |
1017 | struct scsi_device *scsidev; | |
1018 | unsigned char buf[36]; | |
1019 | struct scatterlist *sg; | |
1020 | unsigned int i; | |
1021 | char *thispage; | |
1022 | char *thispage_orig; | |
1023 | int bufind = 0; | |
1024 | struct virtdisk_info *vdisk; | |
1025 | ||
1026 | scsidev = scsicmd->device; | |
45104bf9 KD |
1027 | if ((cmdrsp->scsi.cmnd[0] == INQUIRY) && |
1028 | (cmdrsp->scsi.bufflen >= MIN_INQUIRY_RESULT_LEN)) { | |
dc950861 KC |
1029 | if (cmdrsp->scsi.no_disk_result == 0) |
1030 | return; | |
1031 | ||
1032 | /* Linux scsi code is weird; it wants | |
1033 | * a device at Lun 0 to issue report | |
1034 | * luns, but we don't want a disk | |
1035 | * there so we'll present a processor | |
1036 | * there. */ | |
1037 | SET_NO_DISK_INQUIRY_RESULT(buf, cmdrsp->scsi.bufflen, | |
1038 | scsidev->lun, | |
1039 | DEV_DISK_CAPABLE_NOT_PRESENT, | |
1040 | DEV_NOT_CAPABLE); | |
1041 | ||
1042 | if (scsi_sg_count(scsicmd) == 0) { | |
1043 | if (scsi_bufflen(scsicmd) > 0) { | |
dc950861 KC |
1044 | BUG_ON(scsi_sg_count(scsicmd) == |
1045 | 0); | |
1046 | } | |
1047 | memcpy(scsi_sglist(scsicmd), buf, | |
1048 | cmdrsp->scsi.bufflen); | |
1049 | return; | |
1050 | } | |
1051 | ||
1052 | sg = scsi_sglist(scsicmd); | |
1053 | for (i = 0; i < scsi_sg_count(scsicmd); i++) { | |
dc950861 | 1054 | thispage_orig = kmap_atomic(sg_page(sg + i)); |
396beed6 | 1055 | thispage = (void *)((unsigned long)thispage_orig | |
dc950861 KC |
1056 | sg[i].offset); |
1057 | memcpy(thispage, buf + bufind, sg[i].length); | |
1058 | kunmap_atomic(thispage_orig); | |
1059 | bufind += sg[i].length; | |
1060 | } | |
1061 | } else { | |
dc950861 KC |
1062 | vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head; |
1063 | for ( ; vdisk->next; vdisk = vdisk->next) { | |
45104bf9 KD |
1064 | if ((scsidev->channel != vdisk->channel) || |
1065 | (scsidev->id != vdisk->id) || | |
1066 | (scsidev->lun != vdisk->lun)) | |
dc950861 KC |
1067 | continue; |
1068 | ||
1069 | if (atomic_read(&vdisk->ios_threshold) > 0) { | |
1070 | atomic_dec(&vdisk->ios_threshold); | |
1071 | if (atomic_read(&vdisk->ios_threshold) == 0) { | |
dc950861 KC |
1072 | atomic_set(&vdisk->error_count, 0); |
1073 | } | |
1074 | } | |
1075 | } | |
1076 | } | |
1077 | } | |
1078 | ||
1079 | static void | |
1080 | complete_scsi_command(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd) | |
1081 | { | |
dc950861 KC |
1082 | /* take what we need out of cmdrsp and complete the scsicmd */ |
1083 | scsicmd->result = cmdrsp->scsi.linuxstat; | |
1084 | if (cmdrsp->scsi.linuxstat) | |
1085 | do_scsi_linuxstat(cmdrsp, scsicmd); | |
1086 | else | |
1087 | do_scsi_nolinuxstat(cmdrsp, scsicmd); | |
1088 | ||
4de57acb | 1089 | if (scsicmd->scsi_done) |
dc950861 | 1090 | scsicmd->scsi_done(scsicmd); |
dc950861 KC |
1091 | } |
1092 | ||
1093 | static inline void | |
1094 | complete_vdiskmgmt_command(struct uiscmdrsp *cmdrsp) | |
1095 | { | |
1096 | /* copy the result of the taskmgmt and */ | |
1097 | /* wake up the error handler that is waiting for this */ | |
396beed6 KD |
1098 | *(int *)cmdrsp->vdiskmgmt.notifyresult = cmdrsp->vdiskmgmt.result; |
1099 | wake_up_all((wait_queue_head_t *)cmdrsp->vdiskmgmt.notify); | |
dc950861 KC |
1100 | } |
1101 | ||
1102 | static inline void | |
1103 | complete_taskmgmt_command(struct uiscmdrsp *cmdrsp) | |
1104 | { | |
1105 | /* copy the result of the taskmgmt and */ | |
1106 | /* wake up the error handler that is waiting for this */ | |
396beed6 | 1107 | *(int *)cmdrsp->scsitaskmgmt.notifyresult = |
dc950861 | 1108 | cmdrsp->scsitaskmgmt.result; |
396beed6 | 1109 | wake_up_all((wait_queue_head_t *)cmdrsp->scsitaskmgmt.notify); |
dc950861 KC |
1110 | } |
1111 | ||
1112 | static void | |
1113 | drain_queue(struct virthba_info *virthbainfo, struct chaninfo *dc, | |
836600fa | 1114 | struct uiscmdrsp *cmdrsp) |
dc950861 KC |
1115 | { |
1116 | unsigned long flags; | |
1117 | int qrslt = 0; | |
1118 | struct scsi_cmnd *scsicmd; | |
1119 | struct Scsi_Host *shost = virthbainfo->scsihost; | |
1120 | ||
1121 | while (1) { | |
1122 | spin_lock_irqsave(&virthbainfo->chinfo.insertlock, flags); | |
9f16930e | 1123 | if (!spar_channel_client_acquire_os(dc->queueinfo->chan, |
836600fa | 1124 | "vhba")) { |
dc950861 KC |
1125 | spin_unlock_irqrestore(&virthbainfo->chinfo.insertlock, |
1126 | flags); | |
1127 | virthbainfo->acquire_failed_cnt++; | |
1128 | break; | |
1129 | } | |
1130 | qrslt = uisqueue_get_cmdrsp(dc->queueinfo, cmdrsp, | |
1131 | IOCHAN_FROM_IOPART); | |
08598918 | 1132 | spar_channel_client_release_os(dc->queueinfo->chan, "vhba"); |
dc950861 KC |
1133 | spin_unlock_irqrestore(&virthbainfo->chinfo.insertlock, flags); |
1134 | if (qrslt == 0) | |
1135 | break; | |
1136 | if (cmdrsp->cmdtype == CMD_SCSI_TYPE) { | |
1137 | /* scsicmd location is returned by the | |
1138 | * deletion | |
1139 | */ | |
1140 | scsicmd = del_scsipending_entry(virthbainfo, | |
a57fc10e KD |
1141 | (uintptr_t) |
1142 | cmdrsp->scsi.scsicmd); | |
dc950861 KC |
1143 | if (!scsicmd) |
1144 | break; | |
1145 | /* complete the orig cmd */ | |
1146 | complete_scsi_command(cmdrsp, scsicmd); | |
1147 | } else if (cmdrsp->cmdtype == CMD_SCSITASKMGMT_TYPE) { | |
1148 | if (!del_scsipending_entry(virthbainfo, | |
396beed6 | 1149 | (uintptr_t)cmdrsp->scsitaskmgmt.scsicmd)) |
dc950861 KC |
1150 | break; |
1151 | complete_taskmgmt_command(cmdrsp); | |
1152 | } else if (cmdrsp->cmdtype == CMD_NOTIFYGUEST_TYPE) { | |
1153 | /* The vHba pointer has no meaning in | |
1154 | * a Client/Guest Partition. Let's be | |
1155 | * safe and set it to NULL now. Do | |
1156 | * not use it here! */ | |
be3c3669 | 1157 | cmdrsp->disknotify.v_hba = NULL; |
dc950861 KC |
1158 | process_disk_notify(shost, cmdrsp); |
1159 | } else if (cmdrsp->cmdtype == CMD_VDISKMGMT_TYPE) { | |
1160 | if (!del_scsipending_entry(virthbainfo, | |
a57fc10e KD |
1161 | (uintptr_t) |
1162 | cmdrsp->vdiskmgmt.scsicmd)) | |
dc950861 KC |
1163 | break; |
1164 | complete_vdiskmgmt_command(cmdrsp); | |
0aca7844 | 1165 | } |
dc950861 KC |
1166 | /* cmdrsp is now available for reuse */ |
1167 | } | |
1168 | } | |
1169 | ||
dc950861 KC |
1170 | /* main function for the thread that waits for scsi commands to arrive |
1171 | * in a specified queue | |
1172 | */ | |
1173 | static int | |
1174 | process_incoming_rsps(void *v) | |
1175 | { | |
1176 | struct virthba_info *virthbainfo = v; | |
1177 | struct chaninfo *dc = &virthbainfo->chinfo; | |
1178 | struct uiscmdrsp *cmdrsp = NULL; | |
1179 | const int SZ = sizeof(struct uiscmdrsp); | |
5fc0229a | 1180 | u64 mask; |
dc950861 KC |
1181 | unsigned long long rc1; |
1182 | ||
1183 | UIS_DAEMONIZE("vhba_incoming"); | |
1184 | /* alloc once and reuse */ | |
1185 | cmdrsp = kmalloc(SZ, GFP_ATOMIC); | |
5af3b098 | 1186 | if (!cmdrsp) { |
dc950861 KC |
1187 | complete_and_exit(&dc->threadinfo.has_stopped, 0); |
1188 | return 0; | |
1189 | } | |
1190 | mask = ULTRA_CHANNEL_ENABLE_INTS; | |
1191 | while (1) { | |
010c9f8e DN |
1192 | if (kthread_should_stop()) |
1193 | break; | |
dc950861 KC |
1194 | wait_event_interruptible_timeout(virthbainfo->rsp_queue, |
1195 | (atomic_read(&virthbainfo->interrupt_rcvd) == 1), | |
836600fa | 1196 | usecs_to_jiffies(rsltq_wait_usecs)); |
dc950861 KC |
1197 | atomic_set(&virthbainfo->interrupt_rcvd, 0); |
1198 | /* drain queue */ | |
1199 | drain_queue(virthbainfo, dc, cmdrsp); | |
17f5e491 | 1200 | rc1 = uisqueue_interlocked_or(virthbainfo->flags_addr, mask); |
dc950861 KC |
1201 | } |
1202 | ||
1203 | kfree(cmdrsp); | |
1204 | ||
dc950861 KC |
1205 | complete_and_exit(&dc->threadinfo.has_stopped, 0); |
1206 | } | |
1207 | ||
d9c5607e EA |
1208 | /*****************************************************/ |
1209 | /* Debugfs filesystem functions */ | |
1210 | /*****************************************************/ | |
1211 | ||
1212 | static ssize_t info_debugfs_read(struct file *file, | |
836600fa | 1213 | char __user *buf, size_t len, loff_t *offset) |
d9c5607e EA |
1214 | { |
1215 | ssize_t bytes_read = 0; | |
1216 | int str_pos = 0; | |
5fc0229a | 1217 | u64 phys_flags_addr; |
d9c5607e EA |
1218 | int i; |
1219 | struct virthba_info *virthbainfo; | |
1220 | char *vbuf; | |
1221 | ||
1222 | if (len > MAX_BUF) | |
1223 | len = MAX_BUF; | |
1224 | vbuf = kzalloc(len, GFP_KERNEL); | |
1225 | if (!vbuf) | |
1226 | return -ENOMEM; | |
1227 | ||
1228 | for (i = 0; i < VIRTHBASOPENMAX; i++) { | |
5af3b098 | 1229 | if (!virthbas_open[i].virthbainfo) |
d9c5607e EA |
1230 | continue; |
1231 | ||
366cf71a | 1232 | virthbainfo = virthbas_open[i].virthbainfo; |
d9c5607e EA |
1233 | |
1234 | str_pos += scnprintf(vbuf + str_pos, | |
366cf71a KD |
1235 | len - str_pos, "max_buff_len:%u\n", |
1236 | max_buff_len); | |
d9c5607e EA |
1237 | |
1238 | str_pos += scnprintf(vbuf + str_pos, len - str_pos, | |
1239 | "\nvirthba result queue poll wait:%d usecs.\n", | |
1240 | rsltq_wait_usecs); | |
1241 | str_pos += scnprintf(vbuf + str_pos, len - str_pos, | |
1242 | "\ninterrupts_rcvd = %llu, interrupts_disabled = %llu\n", | |
1243 | virthbainfo->interrupts_rcvd, | |
1244 | virthbainfo->interrupts_disabled); | |
1245 | str_pos += scnprintf(vbuf + str_pos, | |
1246 | len - str_pos, "\ninterrupts_notme = %llu,\n", | |
1247 | virthbainfo->interrupts_notme); | |
1248 | phys_flags_addr = virt_to_phys((__force void *) | |
1249 | virthbainfo->flags_addr); | |
1250 | str_pos += scnprintf(vbuf + str_pos, len - str_pos, | |
1251 | "flags_addr = %p, phys_flags_addr=0x%016llx, FeatureFlags=%llu\n", | |
1252 | virthbainfo->flags_addr, phys_flags_addr, | |
1253 | (__le64)readq(virthbainfo->flags_addr)); | |
1254 | str_pos += scnprintf(vbuf + str_pos, | |
1255 | len - str_pos, "acquire_failed_cnt:%llu\n", | |
1256 | virthbainfo->acquire_failed_cnt); | |
1257 | str_pos += scnprintf(vbuf + str_pos, len - str_pos, "\n"); | |
1258 | } | |
1259 | ||
1260 | bytes_read = simple_read_from_buffer(buf, len, offset, vbuf, str_pos); | |
1261 | kfree(vbuf); | |
1262 | return bytes_read; | |
1263 | } | |
1264 | ||
836600fa KD |
1265 | static ssize_t enable_ints_write(struct file *file, const char __user *buffer, |
1266 | size_t count, loff_t *ppos) | |
79014eb1 EA |
1267 | { |
1268 | char buf[4]; | |
1269 | int i, new_value; | |
1270 | struct virthba_info *virthbainfo; | |
1271 | ||
05f35980 | 1272 | u64 __iomem *features_addr; |
5fc0229a | 1273 | u64 mask; |
79014eb1 EA |
1274 | |
1275 | if (count >= ARRAY_SIZE(buf)) | |
1276 | return -EINVAL; | |
1277 | ||
1278 | buf[count] = '\0'; | |
0aca7844 | 1279 | if (copy_from_user(buf, buffer, count)) |
79014eb1 | 1280 | return -EFAULT; |
79014eb1 | 1281 | |
29d58624 | 1282 | i = kstrtoint(buf, 10, &new_value); |
79014eb1 | 1283 | |
0aca7844 | 1284 | if (i != 0) |
79014eb1 | 1285 | return -EFAULT; |
79014eb1 | 1286 | |
cfe4571f | 1287 | /* set all counts to new_value usually 0 */ |
79014eb1 | 1288 | for (i = 0; i < VIRTHBASOPENMAX; i++) { |
5af3b098 | 1289 | if (virthbas_open[i].virthbainfo) { |
366cf71a | 1290 | virthbainfo = virthbas_open[i].virthbainfo; |
05f35980 | 1291 | features_addr = |
a8a31f61 | 1292 | &virthbainfo->chinfo.queueinfo->chan->features; |
79014eb1 EA |
1293 | if (new_value == 1) { |
1294 | mask = ~(ULTRA_IO_CHANNEL_IS_POLLING | | |
1295 | ULTRA_IO_DRIVER_DISABLES_INTS); | |
05f35980 | 1296 | uisqueue_interlocked_and(features_addr, mask); |
79014eb1 | 1297 | mask = ULTRA_IO_DRIVER_ENABLES_INTS; |
05f35980 | 1298 | uisqueue_interlocked_or(features_addr, mask); |
79014eb1 EA |
1299 | rsltq_wait_usecs = 4000000; |
1300 | } else { | |
1301 | mask = ~(ULTRA_IO_DRIVER_ENABLES_INTS | | |
1302 | ULTRA_IO_DRIVER_DISABLES_INTS); | |
05f35980 | 1303 | uisqueue_interlocked_and(features_addr, mask); |
79014eb1 | 1304 | mask = ULTRA_IO_CHANNEL_IS_POLLING; |
05f35980 | 1305 | uisqueue_interlocked_or(features_addr, mask); |
79014eb1 EA |
1306 | rsltq_wait_usecs = 4000; |
1307 | } | |
1308 | } | |
1309 | } | |
1310 | return count; | |
1311 | } | |
1312 | ||
dc950861 KC |
1313 | /* As per VirtpciFunc returns 1 for success and 0 for failure */ |
1314 | static int | |
1315 | virthba_serverup(struct virtpci_dev *virtpcidev) | |
1316 | { | |
1317 | struct virthba_info *virthbainfo = | |
396beed6 | 1318 | (struct virthba_info *)((struct Scsi_Host *)virtpcidev->scsi. |
dc950861 KC |
1319 | scsihost)->hostdata; |
1320 | ||
4de57acb | 1321 | if (!virthbainfo->serverdown) |
dc950861 | 1322 | return 1; |
4de57acb | 1323 | |
0aca7844 | 1324 | if (virthbainfo->serverchangingstate) |
dc950861 | 1325 | return 0; |
dc950861 KC |
1326 | |
1327 | virthbainfo->serverchangingstate = true; | |
1328 | /* Must transition channel to ATTACHED state BEFORE we | |
1329 | * can start using the device again | |
1330 | */ | |
ff97a3fd BR |
1331 | SPAR_CHANNEL_CLIENT_TRANSITION(virthbainfo->chinfo.queueinfo->chan, |
1332 | dev_name(&virtpcidev->generic_dev), | |
1333 | CHANNELCLI_ATTACHED, NULL); | |
dc950861 KC |
1334 | |
1335 | /* Start Processing the IOVM Response Queue Again */ | |
1336 | if (!uisthread_start(&virthbainfo->chinfo.threadinfo, | |
1337 | process_incoming_rsps, | |
1338 | virthbainfo, "vhba_incoming")) { | |
dc950861 KC |
1339 | return 0; |
1340 | } | |
1341 | virthbainfo->serverdown = false; | |
1342 | virthbainfo->serverchangingstate = false; | |
1343 | ||
1344 | return 1; | |
1345 | } | |
1346 | ||
1347 | static void | |
1348 | virthba_serverdown_complete(struct work_struct *work) | |
1349 | { | |
1350 | struct virthba_info *virthbainfo; | |
1351 | struct virtpci_dev *virtpcidev; | |
1352 | int i; | |
1353 | struct scsipending *pendingdel = NULL; | |
1354 | struct scsi_cmnd *scsicmd = NULL; | |
1355 | struct uiscmdrsp *cmdrsp; | |
1356 | unsigned long flags; | |
1357 | ||
1358 | virthbainfo = container_of(work, struct virthba_info, | |
1359 | serverdown_completion); | |
1360 | ||
1361 | /* Stop Using the IOVM Response Queue (queue should be drained | |
1362 | * by the end) | |
1363 | */ | |
1364 | uisthread_stop(&virthbainfo->chinfo.threadinfo); | |
1365 | ||
1366 | /* Fail Commands that weren't completed */ | |
1367 | spin_lock_irqsave(&virthbainfo->privlock, flags); | |
1368 | for (i = 0; i < MAX_PENDING_REQUESTS; i++) { | |
1cc598c7 | 1369 | pendingdel = &virthbainfo->pending[i]; |
dc950861 KC |
1370 | switch (pendingdel->cmdtype) { |
1371 | case CMD_SCSI_TYPE: | |
396beed6 | 1372 | scsicmd = (struct scsi_cmnd *)pendingdel->sent; |
a0648724 | 1373 | scsicmd->result = DID_RESET << 16; |
dc950861 KC |
1374 | if (scsicmd->scsi_done) |
1375 | scsicmd->scsi_done(scsicmd); | |
1376 | break; | |
1377 | case CMD_SCSITASKMGMT_TYPE: | |
396beed6 | 1378 | cmdrsp = (struct uiscmdrsp *)pendingdel->sent; |
dc950861 KC |
1379 | wake_up_all((wait_queue_head_t *) |
1380 | cmdrsp->scsitaskmgmt.notify); | |
4de57acb BR |
1381 | *(int *)cmdrsp->scsitaskmgmt.notifyresult = |
1382 | TASK_MGMT_FAILED; | |
dc950861 KC |
1383 | break; |
1384 | case CMD_VDISKMGMT_TYPE: | |
396beed6 KD |
1385 | cmdrsp = (struct uiscmdrsp *)pendingdel->sent; |
1386 | *(int *)cmdrsp->vdiskmgmt.notifyresult = | |
dc950861 KC |
1387 | VDISK_MGMT_FAILED; |
1388 | wake_up_all((wait_queue_head_t *) | |
1389 | cmdrsp->vdiskmgmt.notify); | |
1390 | break; | |
1391 | default: | |
0aca7844 | 1392 | break; |
dc950861 KC |
1393 | } |
1394 | pendingdel->cmdtype = 0; | |
1395 | pendingdel->sent = NULL; | |
1396 | } | |
1397 | spin_unlock_irqrestore(&virthbainfo->privlock, flags); | |
1398 | ||
1399 | virtpcidev = virthbainfo->virtpcidev; | |
1400 | ||
dc950861 KC |
1401 | virthbainfo->serverdown = true; |
1402 | virthbainfo->serverchangingstate = false; | |
1403 | /* Return the ServerDown response to Command */ | |
55f714cd BR |
1404 | visorchipset_device_pause_response(virtpcidev->bus_no, |
1405 | virtpcidev->device_no, 0); | |
dc950861 KC |
1406 | } |
1407 | ||
1408 | /* As per VirtpciFunc returns 1 for success and 0 for failure */ | |
1409 | static int | |
1410 | virthba_serverdown(struct virtpci_dev *virtpcidev, u32 state) | |
1411 | { | |
20078e48 KD |
1412 | int stat = 1; |
1413 | ||
dc950861 | 1414 | struct virthba_info *virthbainfo = |
396beed6 | 1415 | (struct virthba_info *)((struct Scsi_Host *)virtpcidev->scsi. |
dc950861 KC |
1416 | scsihost)->hostdata; |
1417 | ||
dc950861 KC |
1418 | if (!virthbainfo->serverdown && !virthbainfo->serverchangingstate) { |
1419 | virthbainfo->serverchangingstate = true; | |
1420 | queue_work(virthba_serverdown_workqueue, | |
1421 | &virthbainfo->serverdown_completion); | |
1422 | } else if (virthbainfo->serverchangingstate) { | |
20078e48 | 1423 | stat = 0; |
20078e48 | 1424 | } |
dc950861 | 1425 | |
20078e48 | 1426 | return stat; |
dc950861 KC |
1427 | } |
1428 | ||
1429 | /*****************************************************/ | |
1430 | /* Module Init & Exit functions */ | |
1431 | /*****************************************************/ | |
1432 | ||
1433 | static int __init | |
1434 | virthba_parse_line(char *str) | |
1435 | { | |
dc950861 KC |
1436 | return 1; |
1437 | } | |
1438 | ||
1439 | static void __init | |
1440 | virthba_parse_options(char *line) | |
1441 | { | |
1442 | char *next = line; | |
1443 | ||
1444 | POSTCODE_LINUX_2(VHBA_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO); | |
5af3b098 | 1445 | if (!line || !*line) |
dc950861 | 1446 | return; |
5af3b098 | 1447 | while ((line = next)) { |
dc950861 | 1448 | next = strchr(line, ' '); |
5af3b098 | 1449 | if (next) |
dc950861 | 1450 | *next++ = 0; |
4de57acb | 1451 | virthba_parse_line(line); |
dc950861 KC |
1452 | } |
1453 | ||
1454 | POSTCODE_LINUX_2(VHBA_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO); | |
1455 | } | |
1456 | ||
1457 | static int __init | |
1458 | virthba_mod_init(void) | |
1459 | { | |
1460 | int error; | |
1461 | int i; | |
1462 | ||
fcd0157e KC |
1463 | if (!unisys_spar_platform) |
1464 | return -ENODEV; | |
1465 | ||
dc950861 KC |
1466 | POSTCODE_LINUX_2(VHBA_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO); |
1467 | virthba_parse_options(virthba_options); | |
1468 | ||
1469 | error = virtpci_register_driver(&virthba_driver); | |
1470 | if (error < 0) { | |
dc950861 KC |
1471 | POSTCODE_LINUX_3(VHBA_CREATE_FAILURE_PC, error, |
1472 | POSTCODE_SEVERITY_ERR); | |
1473 | } else { | |
d9c5607e EA |
1474 | /* create the debugfs directories and entries */ |
1475 | virthba_debugfs_dir = debugfs_create_dir("virthba", NULL); | |
1476 | debugfs_create_file("info", S_IRUSR, virthba_debugfs_dir, | |
836600fa | 1477 | NULL, &debugfs_info_fops); |
5d150191 | 1478 | debugfs_create_u32("rqwait_usecs", S_IRUSR | S_IWUSR, |
836600fa | 1479 | virthba_debugfs_dir, &rsltq_wait_usecs); |
79014eb1 | 1480 | debugfs_create_file("enable_ints", S_IWUSR, |
836600fa KD |
1481 | virthba_debugfs_dir, NULL, |
1482 | &debugfs_enable_ints_fops); | |
1483 | /* Initialize dar_work_queue */ | |
b6e0a708 | 1484 | INIT_WORK(&dar_work_queue, do_disk_add_remove); |
83afa349 | 1485 | spin_lock_init(&dar_work_queue_lock); |
dc950861 KC |
1486 | |
1487 | /* clear out array */ | |
1488 | for (i = 0; i < VIRTHBASOPENMAX; i++) | |
366cf71a | 1489 | virthbas_open[i].virthbainfo = NULL; |
dc950861 KC |
1490 | /* Initialize the serverdown workqueue */ |
1491 | virthba_serverdown_workqueue = | |
1492 | create_singlethread_workqueue("virthba_serverdown"); | |
5af3b098 | 1493 | if (!virthba_serverdown_workqueue) { |
dc950861 KC |
1494 | POSTCODE_LINUX_2(VHBA_CREATE_FAILURE_PC, |
1495 | POSTCODE_SEVERITY_ERR); | |
1496 | error = -1; | |
1497 | } | |
1498 | } | |
1499 | ||
1500 | POSTCODE_LINUX_2(VHBA_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO); | |
dc950861 KC |
1501 | return error; |
1502 | } | |
1503 | ||
1504 | static ssize_t | |
1505 | virthba_acquire_lun(struct device *cdev, struct device_attribute *attr, | |
1506 | const char *buf, size_t count) | |
1507 | { | |
1508 | struct uisscsi_dest vdest; | |
1509 | struct Scsi_Host *shost = class_to_shost(cdev); | |
1510 | int i; | |
1511 | ||
1512 | i = sscanf(buf, "%d-%d-%d", &vdest.channel, &vdest.id, &vdest.lun); | |
1513 | if (i != 3) | |
1514 | return i; | |
1515 | ||
1516 | return forward_vdiskmgmt_command(VDISK_MGMT_ACQUIRE, shost, &vdest); | |
1517 | } | |
1518 | ||
1519 | static ssize_t | |
1520 | virthba_release_lun(struct device *cdev, struct device_attribute *attr, | |
1521 | const char *buf, size_t count) | |
1522 | { | |
1523 | struct uisscsi_dest vdest; | |
1524 | struct Scsi_Host *shost = class_to_shost(cdev); | |
1525 | int i; | |
1526 | ||
1527 | i = sscanf(buf, "%d-%d-%d", &vdest.channel, &vdest.id, &vdest.lun); | |
1528 | if (i != 3) | |
1529 | return i; | |
1530 | ||
1531 | return forward_vdiskmgmt_command(VDISK_MGMT_RELEASE, shost, &vdest); | |
1532 | } | |
1533 | ||
1534 | #define CLASS_DEVICE_ATTR(_name, _mode, _show, _store) \ | |
1535 | struct device_attribute class_device_attr_##_name = \ | |
1536 | __ATTR(_name, _mode, _show, _store) | |
1537 | ||
1538 | static CLASS_DEVICE_ATTR(acquire_lun, S_IWUSR, NULL, virthba_acquire_lun); | |
1539 | static CLASS_DEVICE_ATTR(release_lun, S_IWUSR, NULL, virthba_release_lun); | |
1540 | ||
1541 | static DEVICE_ATTRIBUTE *virthba_shost_attrs[] = { | |
1542 | &class_device_attr_acquire_lun, | |
1543 | &class_device_attr_release_lun, | |
1544 | NULL | |
1545 | }; | |
1546 | ||
1547 | static void __exit | |
1548 | virthba_mod_exit(void) | |
1549 | { | |
dc950861 KC |
1550 | virtpci_unregister_driver(&virthba_driver); |
1551 | /* unregister is going to call virthba_remove */ | |
1552 | /* destroy serverdown completion workqueue */ | |
1553 | if (virthba_serverdown_workqueue) { | |
1554 | destroy_workqueue(virthba_serverdown_workqueue); | |
1555 | virthba_serverdown_workqueue = NULL; | |
1556 | } | |
1557 | ||
d9c5607e | 1558 | debugfs_remove_recursive(virthba_debugfs_dir); |
dc950861 KC |
1559 | } |
1560 | ||
1561 | /* specify function to be run at module insertion time */ | |
1562 | module_init(virthba_mod_init); | |
1563 | ||
1564 | /* specify function to be run when module is removed */ | |
1565 | module_exit(virthba_mod_exit); | |
1566 | ||
1567 | MODULE_LICENSE("GPL"); | |
1568 | MODULE_AUTHOR("Usha Srinivasan"); | |
1569 | MODULE_ALIAS("uisvirthba"); | |
1570 | /* this is extracted during depmod and kept in modules.dep */ | |
1571 | /* module parameter */ | |
1572 | module_param(virthba_options, charp, S_IRUGO); |