1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> |
4 | * Based on.......: linux/drivers/s390/block/mdisk.c |
5 | * ...............: by Hartmunt Penner <hpenner@de.ibm.com> |
6 | * Bugreports.to..: <Linux390@de.ibm.com> |
7 | * Copyright IBM Corp. 1999, 2000 |
8 | * |
9 | */ |
10 | |
11 | #define KMSG_COMPONENT "dasd" |
12 | |
13 | #include <linux/kernel_stat.h> |
14 | #include <linux/stddef.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/hdreg.h> |
18 | #include <linux/bio.h> |
19 | #include <linux/module.h> |
20 | #include <linux/init.h> |
21 | #include <linux/jiffies.h> |
22 | #include <asm/asm-extable.h> |
23 | #include <asm/dasd.h> |
24 | #include <asm/debug.h> |
25 | #include <asm/diag.h> |
26 | #include <asm/ebcdic.h> |
27 | #include <linux/io.h> |
28 | #include <asm/irq.h> |
29 | #include <asm/vtoc.h> |
30 | |
31 | #include "dasd_int.h" |
32 | #include "dasd_diag.h" |
33 | |
34 | #define "dasd(diag):" |
35 | |
36 | MODULE_LICENSE("GPL" ); |
37 | |
38 | /* The maximum number of blocks per request (max_blocks) is dependent on the |
39 | * amount of storage that is available in the static I/O buffer for each |
40 | * device. Currently each device gets 2 pages. We want to fit two requests |
41 | * into the available memory so that we can immediately start the next if one |
42 | * finishes. */ |
43 | #define DIAG_MAX_BLOCKS (((2 * PAGE_SIZE - sizeof(struct dasd_ccw_req) - \ |
44 | sizeof(struct dasd_diag_req)) / \ |
45 | sizeof(struct dasd_diag_bio)) / 2) |
46 | #define DIAG_MAX_RETRIES 32 |
47 | #define DIAG_TIMEOUT 50 |
48 | |
49 | static struct dasd_discipline dasd_diag_discipline; |
50 | |
51 | struct dasd_diag_private { |
52 | struct dasd_diag_characteristics rdc_data; |
53 | struct dasd_diag_rw_io iob; |
54 | struct dasd_diag_init_io iib; |
55 | blocknum_t pt_block; |
56 | struct ccw_dev_id dev_id; |
57 | }; |
58 | |
59 | struct dasd_diag_req { |
60 | unsigned int block_count; |
61 | struct dasd_diag_bio bio[]; |
62 | }; |
63 | |
64 | static const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 */ |
65 | |
66 | /* Perform DIAG250 call with block I/O parameter list iob (input and output) |
67 | * and function code cmd. |
68 | * In case of an exception return 3. Otherwise return result of bitwise OR of |
69 | * resulting condition code and DIAG return code. */ |
70 | static inline int __dia250(void *iob, int cmd) |
71 | { |
72 | union register_pair rx = { .even = (unsigned long)iob, }; |
73 | typedef union { |
74 | struct dasd_diag_init_io init_io; |
75 | struct dasd_diag_rw_io rw_io; |
76 | } addr_type; |
77 | int cc; |
78 | |
79 | cc = 3; |
80 | asm volatile( |
81 | " diag %[rx],%[cmd],0x250\n" |
82 | "0: ipm %[cc]\n" |
83 | " srl %[cc],28\n" |
84 | "1:\n" |
85 | EX_TABLE(0b,1b) |
86 | : [cc] "+&d" (cc), [rx] "+&d" (rx.pair), "+m" (*(addr_type *)iob) |
87 | : [cmd] "d" (cmd) |
88 | : "cc" ); |
89 | return cc | rx.odd; |
90 | } |
91 | |
92 | static inline int dia250(void *iob, int cmd) |
93 | { |
94 | diag_stat_inc(DIAG_STAT_X250); |
95 | return __dia250(iob, cmd); |
96 | } |
97 | |
98 | /* Initialize block I/O to DIAG device using the specified blocksize and |
99 | * block offset. On success, return zero and set end_block to contain the |
100 | * number of blocks on the device minus the specified offset. Return non-zero |
101 | * otherwise. */ |
102 | static inline int |
103 | mdsk_init_io(struct dasd_device *device, unsigned int blocksize, |
104 | blocknum_t offset, blocknum_t *end_block) |
105 | { |
106 | struct dasd_diag_private *private = device->private; |
107 | struct dasd_diag_init_io *iib = &private->iib; |
108 | int rc; |
109 | |
110 | memset(iib, 0, sizeof (struct dasd_diag_init_io)); |
111 | |
112 | iib->dev_nr = private->dev_id.devno; |
113 | iib->block_size = blocksize; |
114 | iib->offset = offset; |
115 | iib->flaga = DASD_DIAG_FLAGA_DEFAULT; |
116 | |
117 | rc = dia250(iob: iib, INIT_BIO); |
118 | |
119 | if ((rc & 3) == 0 && end_block) |
120 | *end_block = iib->end_block; |
121 | |
122 | return rc; |
123 | } |
124 | |
125 | /* Remove block I/O environment for device. Return zero on success, non-zero |
126 | * otherwise. */ |
127 | static inline int |
128 | mdsk_term_io(struct dasd_device * device) |
129 | { |
130 | struct dasd_diag_private *private = device->private; |
131 | struct dasd_diag_init_io *iib = &private->iib; |
132 | int rc; |
133 | |
134 | memset(iib, 0, sizeof (struct dasd_diag_init_io)); |
135 | iib->dev_nr = private->dev_id.devno; |
136 | rc = dia250(iob: iib, TERM_BIO); |
137 | return rc; |
138 | } |
139 | |
140 | /* Error recovery for failed DIAG requests - try to reestablish the DIAG |
141 | * environment. */ |
142 | static void |
143 | dasd_diag_erp(struct dasd_device *device) |
144 | { |
145 | int rc; |
146 | |
147 | mdsk_term_io(device); |
148 | rc = mdsk_init_io(device, blocksize: device->block->bp_block, offset: 0, NULL); |
149 | if (rc == 4) { |
150 | if (!(test_and_set_bit(DASD_FLAG_DEVICE_RO, addr: &device->flags))) |
151 | pr_warn("%s: The access mode of a DIAG device changed to read-only\n" , |
152 | dev_name(&device->cdev->dev)); |
153 | rc = 0; |
154 | } |
155 | if (rc) |
156 | pr_warn("%s: DIAG ERP failed with rc=%d\n" , |
157 | dev_name(&device->cdev->dev), rc); |
158 | } |
159 | |
160 | /* Start a given request at the device. Return zero on success, non-zero |
161 | * otherwise. */ |
162 | static int |
163 | dasd_start_diag(struct dasd_ccw_req * cqr) |
164 | { |
165 | struct dasd_device *device; |
166 | struct dasd_diag_private *private; |
167 | struct dasd_diag_req *dreq; |
168 | int rc; |
169 | |
170 | device = cqr->startdev; |
171 | if (cqr->retries < 0) { |
172 | DBF_DEV_EVENT(DBF_ERR, device, "DIAG start_IO: request %p " |
173 | "- no retry left)" , cqr); |
174 | cqr->status = DASD_CQR_ERROR; |
175 | return -EIO; |
176 | } |
177 | private = device->private; |
178 | dreq = cqr->data; |
179 | |
180 | private->iob.dev_nr = private->dev_id.devno; |
181 | private->iob.key = 0; |
182 | private->iob.flags = DASD_DIAG_RWFLAG_ASYNC; |
183 | private->iob.block_count = dreq->block_count; |
184 | private->iob.interrupt_params = (addr_t) cqr; |
185 | private->iob.bio_list = dreq->bio; |
186 | private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT; |
187 | |
188 | cqr->startclk = get_tod_clock(); |
189 | cqr->starttime = jiffies; |
190 | cqr->retries--; |
191 | |
192 | rc = dia250(iob: &private->iob, RW_BIO); |
193 | switch (rc) { |
194 | case 0: /* Synchronous I/O finished successfully */ |
195 | cqr->stopclk = get_tod_clock(); |
196 | cqr->status = DASD_CQR_SUCCESS; |
197 | /* Indicate to calling function that only a dasd_schedule_bh() |
198 | and no timer is needed */ |
199 | rc = -EACCES; |
200 | break; |
201 | case 8: /* Asynchronous I/O was started */ |
202 | cqr->status = DASD_CQR_IN_IO; |
203 | rc = 0; |
204 | break; |
205 | default: /* Error condition */ |
206 | cqr->status = DASD_CQR_QUEUED; |
207 | DBF_DEV_EVENT(DBF_WARNING, device, "dia250 returned rc=%d" , rc); |
208 | dasd_diag_erp(device); |
209 | rc = -EIO; |
210 | break; |
211 | } |
212 | cqr->intrc = rc; |
213 | return rc; |
214 | } |
215 | |
216 | /* Terminate given request at the device. */ |
217 | static int |
218 | dasd_diag_term_IO(struct dasd_ccw_req * cqr) |
219 | { |
220 | struct dasd_device *device; |
221 | |
222 | device = cqr->startdev; |
223 | mdsk_term_io(device); |
224 | mdsk_init_io(device, blocksize: device->block->bp_block, offset: 0, NULL); |
225 | cqr->status = DASD_CQR_CLEAR_PENDING; |
226 | cqr->stopclk = get_tod_clock(); |
227 | dasd_schedule_device_bh(device); |
228 | return 0; |
229 | } |
230 | |
231 | /* Handle external interruption. */ |
232 | static void dasd_ext_handler(struct ext_code ext_code, |
233 | unsigned int param32, unsigned long param64) |
234 | { |
235 | struct dasd_ccw_req *cqr, *next; |
236 | struct dasd_device *device; |
237 | unsigned long expires; |
238 | unsigned long flags; |
239 | addr_t ip; |
240 | int rc; |
241 | |
242 | switch (ext_code.subcode >> 8) { |
243 | case DASD_DIAG_CODE_31BIT: |
244 | ip = (addr_t) param32; |
245 | break; |
246 | case DASD_DIAG_CODE_64BIT: |
247 | ip = (addr_t) param64; |
248 | break; |
249 | default: |
250 | return; |
251 | } |
252 | inc_irq_stat(IRQEXT_DSD); |
253 | if (!ip) { /* no intparm: unsolicited interrupt */ |
254 | DBF_EVENT(DBF_NOTICE, "%s" , "caught unsolicited " |
255 | "interrupt" ); |
256 | return; |
257 | } |
258 | cqr = (struct dasd_ccw_req *) ip; |
259 | device = (struct dasd_device *) cqr->startdev; |
260 | if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { |
261 | DBF_DEV_EVENT(DBF_WARNING, device, |
262 | " magic number of dasd_ccw_req 0x%08X doesn't" |
263 | " match discipline 0x%08X" , |
264 | cqr->magic, *(int *) (&device->discipline->name)); |
265 | return; |
266 | } |
267 | |
268 | /* get irq lock to modify request queue */ |
269 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); |
270 | |
271 | /* Check for a pending clear operation */ |
272 | if (cqr->status == DASD_CQR_CLEAR_PENDING) { |
273 | cqr->status = DASD_CQR_CLEARED; |
274 | dasd_device_clear_timer(device); |
275 | dasd_schedule_device_bh(device); |
276 | spin_unlock_irqrestore(lock: get_ccwdev_lock(device->cdev), flags); |
277 | return; |
278 | } |
279 | |
280 | cqr->stopclk = get_tod_clock(); |
281 | |
282 | expires = 0; |
283 | if ((ext_code.subcode & 0xff) == 0) { |
284 | cqr->status = DASD_CQR_SUCCESS; |
285 | /* Start first request on queue if possible -> fast_io. */ |
286 | if (!list_empty(head: &device->ccw_queue)) { |
287 | next = list_entry(device->ccw_queue.next, |
288 | struct dasd_ccw_req, devlist); |
289 | if (next->status == DASD_CQR_QUEUED) { |
290 | rc = dasd_start_diag(cqr: next); |
291 | if (rc == 0) |
292 | expires = next->expires; |
293 | } |
294 | } |
295 | } else { |
296 | cqr->status = DASD_CQR_QUEUED; |
297 | DBF_DEV_EVENT(DBF_DEBUG, device, "interrupt status for " |
298 | "request %p was %d (%d retries left)" , cqr, |
299 | ext_code.subcode & 0xff, cqr->retries); |
300 | dasd_diag_erp(device); |
301 | } |
302 | |
303 | if (expires != 0) |
304 | dasd_device_set_timer(device, expires); |
305 | else |
306 | dasd_device_clear_timer(device); |
307 | dasd_schedule_device_bh(device); |
308 | |
309 | spin_unlock_irqrestore(lock: get_ccwdev_lock(device->cdev), flags); |
310 | } |
311 | |
312 | /* Check whether device can be controlled by DIAG discipline. Return zero on |
313 | * success, non-zero otherwise. */ |
314 | static int |
315 | dasd_diag_check_device(struct dasd_device *device) |
316 | { |
317 | struct dasd_diag_private *private = device->private; |
318 | struct dasd_diag_characteristics *rdc_data; |
319 | struct vtoc_cms_label *label; |
320 | struct dasd_block *block; |
321 | struct dasd_diag_bio *bio; |
322 | unsigned int sb, bsize; |
323 | blocknum_t end_block; |
324 | int rc; |
325 | |
326 | if (private == NULL) { |
327 | private = kzalloc(size: sizeof(*private), GFP_KERNEL); |
328 | if (private == NULL) { |
329 | DBF_DEV_EVENT(DBF_WARNING, device, "%s" , |
330 | "Allocating memory for private DASD data " |
331 | "failed\n" ); |
332 | return -ENOMEM; |
333 | } |
334 | ccw_device_get_id(device->cdev, &private->dev_id); |
335 | device->private = private; |
336 | } |
337 | block = dasd_alloc_block(); |
338 | if (IS_ERR(ptr: block)) { |
339 | DBF_DEV_EVENT(DBF_WARNING, device, "%s" , |
340 | "could not allocate dasd block structure" ); |
341 | device->private = NULL; |
342 | kfree(objp: private); |
343 | return PTR_ERR(ptr: block); |
344 | } |
345 | device->block = block; |
346 | block->base = device; |
347 | |
348 | /* Read Device Characteristics */ |
349 | rdc_data = &private->rdc_data; |
350 | rdc_data->dev_nr = private->dev_id.devno; |
351 | rdc_data->rdc_len = sizeof (struct dasd_diag_characteristics); |
352 | |
353 | rc = diag210((struct diag210 *) rdc_data); |
354 | if (rc) { |
355 | DBF_DEV_EVENT(DBF_WARNING, device, "failed to retrieve device " |
356 | "information (rc=%d)" , rc); |
357 | rc = -EOPNOTSUPP; |
358 | goto out; |
359 | } |
360 | |
361 | device->default_expires = DIAG_TIMEOUT; |
362 | device->default_retries = DIAG_MAX_RETRIES; |
363 | |
364 | /* Figure out position of label block */ |
365 | switch (private->rdc_data.vdev_class) { |
366 | case DEV_CLASS_FBA: |
367 | private->pt_block = 1; |
368 | break; |
369 | case DEV_CLASS_ECKD: |
370 | private->pt_block = 2; |
371 | break; |
372 | default: |
373 | pr_warn("%s: Device type %d is not supported in DIAG mode\n" , |
374 | dev_name(&device->cdev->dev), |
375 | private->rdc_data.vdev_class); |
376 | rc = -EOPNOTSUPP; |
377 | goto out; |
378 | } |
379 | |
380 | DBF_DEV_EVENT(DBF_INFO, device, |
381 | "%04X: %04X on real %04X/%02X" , |
382 | rdc_data->dev_nr, |
383 | rdc_data->vdev_type, |
384 | rdc_data->rdev_type, rdc_data->rdev_model); |
385 | |
386 | /* terminate all outstanding operations */ |
387 | mdsk_term_io(device); |
388 | |
389 | /* figure out blocksize of device */ |
390 | label = (struct vtoc_cms_label *) get_zeroed_page(GFP_KERNEL); |
391 | if (label == NULL) { |
392 | DBF_DEV_EVENT(DBF_WARNING, device, "%s" , |
393 | "No memory to allocate initialization request" ); |
394 | rc = -ENOMEM; |
395 | goto out; |
396 | } |
397 | bio = kzalloc(size: sizeof(*bio), GFP_KERNEL); |
398 | if (bio == NULL) { |
399 | DBF_DEV_EVENT(DBF_WARNING, device, "%s" , |
400 | "No memory to allocate initialization bio" ); |
401 | rc = -ENOMEM; |
402 | goto out_label; |
403 | } |
404 | rc = 0; |
405 | end_block = 0; |
406 | /* try all sizes - needed for ECKD devices */ |
407 | for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) { |
408 | mdsk_init_io(device, blocksize: bsize, offset: 0, end_block: &end_block); |
409 | memset(bio, 0, sizeof(*bio)); |
410 | bio->type = MDSK_READ_REQ; |
411 | bio->block_number = private->pt_block + 1; |
412 | bio->buffer = label; |
413 | memset(&private->iob, 0, sizeof (struct dasd_diag_rw_io)); |
414 | private->iob.dev_nr = rdc_data->dev_nr; |
415 | private->iob.key = 0; |
416 | private->iob.flags = 0; /* do synchronous io */ |
417 | private->iob.block_count = 1; |
418 | private->iob.interrupt_params = 0; |
419 | private->iob.bio_list = bio; |
420 | private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT; |
421 | rc = dia250(iob: &private->iob, RW_BIO); |
422 | if (rc == 3) { |
423 | pr_warn("%s: A 64-bit DIAG call failed\n" , |
424 | dev_name(&device->cdev->dev)); |
425 | rc = -EOPNOTSUPP; |
426 | goto out_bio; |
427 | } |
428 | mdsk_term_io(device); |
429 | if (rc == 0) |
430 | break; |
431 | } |
432 | if (bsize > PAGE_SIZE) { |
433 | pr_warn("%s: Accessing the DASD failed because of an incorrect format (rc=%d)\n" , |
434 | dev_name(&device->cdev->dev), rc); |
435 | rc = -EIO; |
436 | goto out_bio; |
437 | } |
438 | /* check for label block */ |
439 | if (memcmp(label->label_id, DASD_DIAG_CMS1, |
440 | sizeof(DASD_DIAG_CMS1)) == 0) { |
441 | /* get formatted blocksize from label block */ |
442 | bsize = (unsigned int) label->block_size; |
443 | block->blocks = (unsigned long) label->block_count; |
444 | } else |
445 | block->blocks = end_block; |
446 | block->bp_block = bsize; |
447 | block->s2b_shift = 0; /* bits to shift 512 to get a block */ |
448 | for (sb = 512; sb < bsize; sb = sb << 1) |
449 | block->s2b_shift++; |
450 | rc = mdsk_init_io(device, blocksize: block->bp_block, offset: 0, NULL); |
451 | if (rc && (rc != 4)) { |
452 | pr_warn("%s: DIAG initialization failed with rc=%d\n" , |
453 | dev_name(&device->cdev->dev), rc); |
454 | rc = -EIO; |
455 | } else { |
456 | if (rc == 4) |
457 | set_bit(DASD_FLAG_DEVICE_RO, addr: &device->flags); |
458 | pr_info("%s: New DASD with %ld byte/block, total size %ld " |
459 | "KB%s\n" , dev_name(&device->cdev->dev), |
460 | (unsigned long) block->bp_block, |
461 | (unsigned long) (block->blocks << |
462 | block->s2b_shift) >> 1, |
463 | (rc == 4) ? ", read-only device" : "" ); |
464 | rc = 0; |
465 | } |
466 | out_bio: |
467 | kfree(objp: bio); |
468 | out_label: |
469 | free_page((long) label); |
470 | out: |
471 | if (rc) { |
472 | device->block = NULL; |
473 | dasd_free_block(block); |
474 | device->private = NULL; |
475 | kfree(objp: private); |
476 | } |
477 | return rc; |
478 | } |
479 | |
480 | /* Fill in virtual disk geometry for device. Return zero on success, non-zero |
481 | * otherwise. */ |
482 | static int |
483 | dasd_diag_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) |
484 | { |
485 | if (dasd_check_blocksize(bsize: block->bp_block) != 0) |
486 | return -EINVAL; |
487 | geo->cylinders = (block->blocks << block->s2b_shift) >> 10; |
488 | geo->heads = 16; |
489 | geo->sectors = 128 >> block->s2b_shift; |
490 | return 0; |
491 | } |
492 | |
493 | static dasd_erp_fn_t |
494 | dasd_diag_erp_action(struct dasd_ccw_req * cqr) |
495 | { |
496 | return dasd_default_erp_action; |
497 | } |
498 | |
499 | static dasd_erp_fn_t |
500 | dasd_diag_erp_postaction(struct dasd_ccw_req * cqr) |
501 | { |
502 | return dasd_default_erp_postaction; |
503 | } |
504 | |
505 | /* Create DASD request from block device request. Return pointer to new |
506 | * request on success, ERR_PTR otherwise. */ |
507 | static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, |
508 | struct dasd_block *block, |
509 | struct request *req) |
510 | { |
511 | struct dasd_ccw_req *cqr; |
512 | struct dasd_diag_req *dreq; |
513 | struct dasd_diag_bio *dbio; |
514 | struct req_iterator iter; |
515 | struct bio_vec bv; |
516 | char *dst; |
517 | unsigned int count; |
518 | sector_t recid, first_rec, last_rec; |
519 | unsigned int blksize, off; |
520 | unsigned char rw_cmd; |
521 | |
522 | if (rq_data_dir(req) == READ) |
523 | rw_cmd = MDSK_READ_REQ; |
524 | else if (rq_data_dir(req) == WRITE) |
525 | rw_cmd = MDSK_WRITE_REQ; |
526 | else |
527 | return ERR_PTR(error: -EINVAL); |
528 | blksize = block->bp_block; |
529 | /* Calculate record id of first and last block. */ |
530 | first_rec = blk_rq_pos(rq: req) >> block->s2b_shift; |
531 | last_rec = |
532 | (blk_rq_pos(rq: req) + blk_rq_sectors(rq: req) - 1) >> block->s2b_shift; |
533 | /* Check struct bio and count the number of blocks for the request. */ |
534 | count = 0; |
535 | rq_for_each_segment(bv, req, iter) { |
536 | if (bv.bv_len & (blksize - 1)) |
537 | /* Fba can only do full blocks. */ |
538 | return ERR_PTR(error: -EINVAL); |
539 | count += bv.bv_len >> (block->s2b_shift + 9); |
540 | } |
541 | /* Paranoia. */ |
542 | if (count != last_rec - first_rec + 1) |
543 | return ERR_PTR(error: -EINVAL); |
544 | /* Build the request */ |
545 | cqr = dasd_smalloc_request(DASD_DIAG_MAGIC, 0, struct_size(dreq, bio, count), |
546 | memdev, blk_mq_rq_to_pdu(rq: req)); |
547 | if (IS_ERR(ptr: cqr)) |
548 | return cqr; |
549 | |
550 | dreq = (struct dasd_diag_req *) cqr->data; |
551 | dreq->block_count = count; |
552 | dbio = dreq->bio; |
553 | recid = first_rec; |
554 | rq_for_each_segment(bv, req, iter) { |
555 | dst = bvec_virt(bvec: &bv); |
556 | for (off = 0; off < bv.bv_len; off += blksize) { |
557 | memset(dbio, 0, sizeof (struct dasd_diag_bio)); |
558 | dbio->type = rw_cmd; |
559 | dbio->block_number = recid + 1; |
560 | dbio->buffer = dst; |
561 | dbio++; |
562 | dst += blksize; |
563 | recid++; |
564 | } |
565 | } |
566 | cqr->retries = memdev->default_retries; |
567 | cqr->buildclk = get_tod_clock(); |
568 | if (blk_noretry_request(req) || |
569 | block->base->features & DASD_FEATURE_FAILFAST) |
570 | set_bit(DASD_CQR_FLAGS_FAILFAST, addr: &cqr->flags); |
571 | cqr->startdev = memdev; |
572 | cqr->memdev = memdev; |
573 | cqr->block = block; |
574 | cqr->expires = memdev->default_expires * HZ; |
575 | cqr->status = DASD_CQR_FILLED; |
576 | return cqr; |
577 | } |
578 | |
579 | /* Release DASD request. Return non-zero if request was successful, zero |
580 | * otherwise. */ |
581 | static int |
582 | dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) |
583 | { |
584 | int status; |
585 | |
586 | status = cqr->status == DASD_CQR_DONE; |
587 | dasd_sfree_request(cqr, cqr->memdev); |
588 | return status; |
589 | } |
590 | |
591 | static void dasd_diag_handle_terminated_request(struct dasd_ccw_req *cqr) |
592 | { |
593 | if (cqr->retries < 0) |
594 | cqr->status = DASD_CQR_FAILED; |
595 | else |
596 | cqr->status = DASD_CQR_FILLED; |
597 | }; |
598 | |
599 | /* Fill in IOCTL data for device. */ |
600 | static int |
601 | dasd_diag_fill_info(struct dasd_device * device, |
602 | struct dasd_information2_t * info) |
603 | { |
604 | struct dasd_diag_private *private = device->private; |
605 | |
606 | info->label_block = (unsigned int) private->pt_block; |
607 | info->FBA_layout = 1; |
608 | info->format = DASD_FORMAT_LDL; |
609 | info->characteristics_size = sizeof(private->rdc_data); |
610 | memcpy(info->characteristics, &private->rdc_data, |
611 | sizeof(private->rdc_data)); |
612 | info->confdata_size = 0; |
613 | return 0; |
614 | } |
615 | |
616 | static void |
617 | dasd_diag_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, |
618 | struct irb *stat) |
619 | { |
620 | DBF_DEV_EVENT(DBF_WARNING, device, "%s" , |
621 | "dump sense not available for DIAG data" ); |
622 | } |
623 | |
624 | /* |
625 | * Initialize block layer request queue. |
626 | */ |
627 | static void dasd_diag_setup_blk_queue(struct dasd_block *block) |
628 | { |
629 | unsigned int logical_block_size = block->bp_block; |
630 | struct request_queue *q = block->gdp->queue; |
631 | int max; |
632 | |
633 | max = DIAG_MAX_BLOCKS << block->s2b_shift; |
634 | blk_queue_flag_set(QUEUE_FLAG_NONROT, q); |
635 | q->limits.max_dev_sectors = max; |
636 | blk_queue_logical_block_size(q, logical_block_size); |
637 | blk_queue_max_hw_sectors(q, max); |
638 | blk_queue_max_segments(q, USHRT_MAX); |
639 | /* With page sized segments each segment can be translated into one idaw/tidaw */ |
640 | blk_queue_max_segment_size(q, PAGE_SIZE); |
641 | blk_queue_segment_boundary(q, PAGE_SIZE - 1); |
642 | blk_queue_dma_alignment(q, PAGE_SIZE - 1); |
643 | } |
644 | |
645 | static int dasd_diag_pe_handler(struct dasd_device *device, |
646 | __u8 tbvpm, __u8 fcsecpm) |
647 | { |
648 | return dasd_generic_verify_path(device, tbvpm); |
649 | } |
650 | |
651 | static struct dasd_discipline dasd_diag_discipline = { |
652 | .owner = THIS_MODULE, |
653 | .name = "DIAG" , |
654 | .ebcname = "DIAG" , |
655 | .check_device = dasd_diag_check_device, |
656 | .pe_handler = dasd_diag_pe_handler, |
657 | .fill_geometry = dasd_diag_fill_geometry, |
658 | .setup_blk_queue = dasd_diag_setup_blk_queue, |
659 | .start_IO = dasd_start_diag, |
660 | .term_IO = dasd_diag_term_IO, |
661 | .handle_terminated_request = dasd_diag_handle_terminated_request, |
662 | .erp_action = dasd_diag_erp_action, |
663 | .erp_postaction = dasd_diag_erp_postaction, |
664 | .build_cp = dasd_diag_build_cp, |
665 | .free_cp = dasd_diag_free_cp, |
666 | .dump_sense = dasd_diag_dump_sense, |
667 | .fill_info = dasd_diag_fill_info, |
668 | }; |
669 | |
670 | static int __init |
671 | dasd_diag_init(void) |
672 | { |
673 | if (!MACHINE_IS_VM) { |
674 | pr_info("Discipline %s cannot be used without z/VM\n" , |
675 | dasd_diag_discipline.name); |
676 | return -ENODEV; |
677 | } |
678 | ASCEBC(dasd_diag_discipline.ebcname, 4); |
679 | |
680 | irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL); |
681 | register_external_irq(EXT_IRQ_CP_SERVICE, dasd_ext_handler); |
682 | dasd_diag_discipline_pointer = &dasd_diag_discipline; |
683 | return 0; |
684 | } |
685 | |
686 | static void __exit |
687 | dasd_diag_cleanup(void) |
688 | { |
689 | unregister_external_irq(EXT_IRQ_CP_SERVICE, dasd_ext_handler); |
690 | irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL); |
691 | dasd_diag_discipline_pointer = NULL; |
692 | } |
693 | |
694 | module_init(dasd_diag_init); |
695 | module_exit(dasd_diag_cleanup); |
696 | |