1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> |
4 | * Bugreports.to..: <Linux390@de.ibm.com> |
5 | * Copyright IBM Corp. 1999, 2009 |
6 | */ |
7 | |
8 | #define KMSG_COMPONENT "dasd-fba" |
9 | |
10 | #include <linux/stddef.h> |
11 | #include <linux/kernel.h> |
12 | #include <asm/debug.h> |
13 | |
14 | #include <linux/slab.h> |
15 | #include <linux/hdreg.h> /* HDIO_GETGEO */ |
16 | #include <linux/bio.h> |
17 | #include <linux/module.h> |
18 | #include <linux/init.h> |
19 | #include <linux/io.h> |
20 | |
21 | #include <asm/idals.h> |
22 | #include <asm/ebcdic.h> |
23 | #include <asm/ccwdev.h> |
24 | |
25 | #include "dasd_int.h" |
26 | #include "dasd_fba.h" |
27 | |
28 | #ifdef PRINTK_HEADER |
29 | #undef PRINTK_HEADER |
30 | #endif /* PRINTK_HEADER */ |
31 | #define "dasd(fba):" |
32 | |
33 | #define FBA_DEFAULT_RETRIES 32 |
34 | |
35 | #define DASD_FBA_CCW_WRITE 0x41 |
36 | #define DASD_FBA_CCW_READ 0x42 |
37 | #define DASD_FBA_CCW_LOCATE 0x43 |
38 | #define DASD_FBA_CCW_DEFINE_EXTENT 0x63 |
39 | |
40 | MODULE_LICENSE("GPL" ); |
41 | |
42 | static struct dasd_discipline dasd_fba_discipline; |
43 | static void *dasd_fba_zero_page; |
44 | |
45 | struct dasd_fba_private { |
46 | struct dasd_fba_characteristics rdc_data; |
47 | }; |
48 | |
49 | static struct ccw_device_id dasd_fba_ids[] = { |
50 | { CCW_DEVICE_DEVTYPE (0x6310, 0, 0x9336, 0), .driver_info = 0x1}, |
51 | { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3370, 0), .driver_info = 0x2}, |
52 | { /* end of list */ }, |
53 | }; |
54 | |
55 | MODULE_DEVICE_TABLE(ccw, dasd_fba_ids); |
56 | |
57 | static int |
58 | dasd_fba_set_online(struct ccw_device *cdev) |
59 | { |
60 | return dasd_generic_set_online(cdev, &dasd_fba_discipline); |
61 | } |
62 | |
63 | static struct ccw_driver dasd_fba_driver = { |
64 | .driver = { |
65 | .name = "dasd-fba" , |
66 | .owner = THIS_MODULE, |
67 | .dev_groups = dasd_dev_groups, |
68 | }, |
69 | .ids = dasd_fba_ids, |
70 | .probe = dasd_generic_probe, |
71 | .remove = dasd_generic_remove, |
72 | .set_offline = dasd_generic_set_offline, |
73 | .set_online = dasd_fba_set_online, |
74 | .notify = dasd_generic_notify, |
75 | .path_event = dasd_generic_path_event, |
76 | .int_class = IRQIO_DAS, |
77 | }; |
78 | |
79 | static void |
80 | define_extent(struct ccw1 * ccw, struct DE_fba_data *data, int rw, |
81 | int blksize, int beg, int nr) |
82 | { |
83 | ccw->cmd_code = DASD_FBA_CCW_DEFINE_EXTENT; |
84 | ccw->flags = 0; |
85 | ccw->count = 16; |
86 | ccw->cda = (__u32)virt_to_phys(address: data); |
87 | memset(data, 0, sizeof (struct DE_fba_data)); |
88 | if (rw == WRITE) |
89 | (data->mask).perm = 0x0; |
90 | else if (rw == READ) |
91 | (data->mask).perm = 0x1; |
92 | else |
93 | data->mask.perm = 0x2; |
94 | data->blk_size = blksize; |
95 | data->ext_loc = beg; |
96 | data->ext_end = nr - 1; |
97 | } |
98 | |
99 | static void |
100 | locate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw, |
101 | int block_nr, int block_ct) |
102 | { |
103 | ccw->cmd_code = DASD_FBA_CCW_LOCATE; |
104 | ccw->flags = 0; |
105 | ccw->count = 8; |
106 | ccw->cda = (__u32)virt_to_phys(address: data); |
107 | memset(data, 0, sizeof (struct LO_fba_data)); |
108 | if (rw == WRITE) |
109 | data->operation.cmd = 0x5; |
110 | else if (rw == READ) |
111 | data->operation.cmd = 0x6; |
112 | else |
113 | data->operation.cmd = 0x8; |
114 | data->blk_nr = block_nr; |
115 | data->blk_ct = block_ct; |
116 | } |
117 | |
118 | static int |
119 | dasd_fba_check_characteristics(struct dasd_device *device) |
120 | { |
121 | struct dasd_fba_private *private = device->private; |
122 | struct ccw_device *cdev = device->cdev; |
123 | struct dasd_block *block; |
124 | int readonly, rc; |
125 | |
126 | if (!private) { |
127 | private = kzalloc(size: sizeof(*private), GFP_KERNEL | GFP_DMA); |
128 | if (!private) { |
129 | dev_warn(&device->cdev->dev, |
130 | "Allocating memory for private DASD " |
131 | "data failed\n" ); |
132 | return -ENOMEM; |
133 | } |
134 | device->private = private; |
135 | } else { |
136 | memset(private, 0, sizeof(*private)); |
137 | } |
138 | block = dasd_alloc_block(); |
139 | if (IS_ERR(ptr: block)) { |
140 | DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s" , "could not allocate " |
141 | "dasd block structure" ); |
142 | device->private = NULL; |
143 | kfree(objp: private); |
144 | return PTR_ERR(ptr: block); |
145 | } |
146 | device->block = block; |
147 | block->base = device; |
148 | |
149 | /* Read Device Characteristics */ |
150 | rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC, |
151 | &private->rdc_data, 32); |
152 | if (rc) { |
153 | DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device " |
154 | "characteristics returned error %d" , rc); |
155 | device->block = NULL; |
156 | dasd_free_block(block); |
157 | device->private = NULL; |
158 | kfree(objp: private); |
159 | return rc; |
160 | } |
161 | |
162 | device->default_expires = DASD_EXPIRES; |
163 | device->default_retries = FBA_DEFAULT_RETRIES; |
164 | dasd_path_set_opm(device, pm: LPM_ANYPATH); |
165 | |
166 | readonly = dasd_device_is_ro(device); |
167 | if (readonly) |
168 | set_bit(DASD_FLAG_DEVICE_RO, addr: &device->flags); |
169 | |
170 | /* FBA supports discard, set the according feature bit */ |
171 | dasd_set_feature(cdev, DASD_FEATURE_DISCARD, 1); |
172 | |
173 | dev_info(&device->cdev->dev, |
174 | "New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB " |
175 | "and %d B/blk%s\n" , |
176 | cdev->id.dev_type, |
177 | cdev->id.dev_model, |
178 | cdev->id.cu_type, |
179 | cdev->id.cu_model, |
180 | ((private->rdc_data.blk_bdsa * |
181 | (private->rdc_data.blk_size >> 9)) >> 11), |
182 | private->rdc_data.blk_size, |
183 | readonly ? ", read-only device" : "" ); |
184 | return 0; |
185 | } |
186 | |
187 | static int dasd_fba_do_analysis(struct dasd_block *block) |
188 | { |
189 | struct dasd_fba_private *private = block->base->private; |
190 | int sb, rc; |
191 | |
192 | rc = dasd_check_blocksize(bsize: private->rdc_data.blk_size); |
193 | if (rc) { |
194 | DBF_DEV_EVENT(DBF_WARNING, block->base, "unknown blocksize %d" , |
195 | private->rdc_data.blk_size); |
196 | return rc; |
197 | } |
198 | block->blocks = private->rdc_data.blk_bdsa; |
199 | block->bp_block = private->rdc_data.blk_size; |
200 | block->s2b_shift = 0; /* bits to shift 512 to get a block */ |
201 | for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1) |
202 | block->s2b_shift++; |
203 | return 0; |
204 | } |
205 | |
206 | static int dasd_fba_fill_geometry(struct dasd_block *block, |
207 | struct hd_geometry *geo) |
208 | { |
209 | if (dasd_check_blocksize(bsize: block->bp_block) != 0) |
210 | return -EINVAL; |
211 | geo->cylinders = (block->blocks << block->s2b_shift) >> 10; |
212 | geo->heads = 16; |
213 | geo->sectors = 128 >> block->s2b_shift; |
214 | return 0; |
215 | } |
216 | |
217 | static dasd_erp_fn_t |
218 | dasd_fba_erp_action(struct dasd_ccw_req * cqr) |
219 | { |
220 | return dasd_default_erp_action; |
221 | } |
222 | |
223 | static dasd_erp_fn_t |
224 | dasd_fba_erp_postaction(struct dasd_ccw_req * cqr) |
225 | { |
226 | if (cqr->function == dasd_default_erp_action) |
227 | return dasd_default_erp_postaction; |
228 | |
229 | DBF_DEV_EVENT(DBF_WARNING, cqr->startdev, "unknown ERP action %p" , |
230 | cqr->function); |
231 | return NULL; |
232 | } |
233 | |
234 | static void dasd_fba_check_for_device_change(struct dasd_device *device, |
235 | struct dasd_ccw_req *cqr, |
236 | struct irb *irb) |
237 | { |
238 | char mask; |
239 | |
240 | /* first of all check for state change pending interrupt */ |
241 | mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; |
242 | if ((irb->scsw.cmd.dstat & mask) == mask) |
243 | dasd_generic_handle_state_change(device); |
244 | }; |
245 | |
246 | |
247 | /* |
248 | * Builds a CCW with no data payload |
249 | */ |
250 | static void ccw_write_no_data(struct ccw1 *ccw) |
251 | { |
252 | ccw->cmd_code = DASD_FBA_CCW_WRITE; |
253 | ccw->flags |= CCW_FLAG_SLI; |
254 | ccw->count = 0; |
255 | } |
256 | |
257 | /* |
258 | * Builds a CCW that writes only zeroes. |
259 | */ |
260 | static void ccw_write_zero(struct ccw1 *ccw, int count) |
261 | { |
262 | ccw->cmd_code = DASD_FBA_CCW_WRITE; |
263 | ccw->flags |= CCW_FLAG_SLI; |
264 | ccw->count = count; |
265 | ccw->cda = (__u32)virt_to_phys(address: dasd_fba_zero_page); |
266 | } |
267 | |
268 | /* |
269 | * Helper function to count the amount of necessary CCWs within a given range |
270 | * with 4k alignment and command chaining in mind. |
271 | */ |
272 | static int count_ccws(sector_t first_rec, sector_t last_rec, |
273 | unsigned int blocks_per_page) |
274 | { |
275 | sector_t wz_stop = 0, d_stop = 0; |
276 | int cur_pos = 0; |
277 | int count = 0; |
278 | |
279 | if (first_rec % blocks_per_page != 0) { |
280 | wz_stop = first_rec + blocks_per_page - |
281 | (first_rec % blocks_per_page) - 1; |
282 | if (wz_stop > last_rec) |
283 | wz_stop = last_rec; |
284 | cur_pos = wz_stop - first_rec + 1; |
285 | count++; |
286 | } |
287 | |
288 | if (last_rec - (first_rec + cur_pos) + 1 >= blocks_per_page) { |
289 | if ((last_rec - blocks_per_page + 1) % blocks_per_page != 0) |
290 | d_stop = last_rec - ((last_rec - blocks_per_page + 1) % |
291 | blocks_per_page); |
292 | else |
293 | d_stop = last_rec; |
294 | |
295 | cur_pos += d_stop - (first_rec + cur_pos) + 1; |
296 | count++; |
297 | } |
298 | |
299 | if (cur_pos == 0 || first_rec + cur_pos - 1 < last_rec) |
300 | count++; |
301 | |
302 | return count; |
303 | } |
304 | |
305 | /* |
306 | * This function builds a CCW request for block layer discard requests. |
307 | * Each page in the z/VM hypervisor that represents certain records of an FBA |
308 | * device will be padded with zeros. This is a special behaviour of the WRITE |
309 | * command which is triggered when no data payload is added to the CCW. |
310 | * |
311 | * Note: Due to issues in some z/VM versions, we can't fully utilise this |
312 | * special behaviour. We have to keep a 4k (or 8 block) alignment in mind to |
313 | * work around those issues and write actual zeroes to the unaligned parts in |
314 | * the request. This workaround might be removed in the future. |
315 | */ |
316 | static struct dasd_ccw_req *dasd_fba_build_cp_discard( |
317 | struct dasd_device *memdev, |
318 | struct dasd_block *block, |
319 | struct request *req) |
320 | { |
321 | struct LO_fba_data *LO_data; |
322 | struct dasd_ccw_req *cqr; |
323 | struct ccw1 *ccw; |
324 | |
325 | sector_t wz_stop = 0, d_stop = 0; |
326 | sector_t first_rec, last_rec; |
327 | |
328 | unsigned int blksize = block->bp_block; |
329 | unsigned int blocks_per_page; |
330 | int wz_count = 0; |
331 | int d_count = 0; |
332 | int cur_pos = 0; /* Current position within the extent */ |
333 | int count = 0; |
334 | int cplength; |
335 | int datasize; |
336 | int nr_ccws; |
337 | |
338 | first_rec = blk_rq_pos(rq: req) >> block->s2b_shift; |
339 | last_rec = |
340 | (blk_rq_pos(rq: req) + blk_rq_sectors(rq: req) - 1) >> block->s2b_shift; |
341 | count = last_rec - first_rec + 1; |
342 | |
343 | blocks_per_page = BLOCKS_PER_PAGE(blksize); |
344 | nr_ccws = count_ccws(first_rec, last_rec, blocks_per_page); |
345 | |
346 | /* define extent + nr_ccws * locate record + nr_ccws * single CCW */ |
347 | cplength = 1 + 2 * nr_ccws; |
348 | datasize = sizeof(struct DE_fba_data) + |
349 | nr_ccws * (sizeof(struct LO_fba_data) + sizeof(struct ccw1)); |
350 | |
351 | cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev, |
352 | blk_mq_rq_to_pdu(rq: req)); |
353 | if (IS_ERR(ptr: cqr)) |
354 | return cqr; |
355 | |
356 | ccw = cqr->cpaddr; |
357 | |
358 | define_extent(ccw: ccw++, data: cqr->data, WRITE, blksize, beg: first_rec, nr: count); |
359 | LO_data = cqr->data + sizeof(struct DE_fba_data); |
360 | |
361 | /* First part is not aligned. Calculate range to write zeroes. */ |
362 | if (first_rec % blocks_per_page != 0) { |
363 | wz_stop = first_rec + blocks_per_page - |
364 | (first_rec % blocks_per_page) - 1; |
365 | if (wz_stop > last_rec) |
366 | wz_stop = last_rec; |
367 | wz_count = wz_stop - first_rec + 1; |
368 | |
369 | ccw[-1].flags |= CCW_FLAG_CC; |
370 | locate_record(ccw: ccw++, data: LO_data++, WRITE, block_nr: cur_pos, block_ct: wz_count); |
371 | |
372 | ccw[-1].flags |= CCW_FLAG_CC; |
373 | ccw_write_zero(ccw: ccw++, count: wz_count * blksize); |
374 | |
375 | cur_pos = wz_count; |
376 | } |
377 | |
378 | /* We can do proper discard when we've got at least blocks_per_page blocks. */ |
379 | if (last_rec - (first_rec + cur_pos) + 1 >= blocks_per_page) { |
380 | /* is last record at page boundary? */ |
381 | if ((last_rec - blocks_per_page + 1) % blocks_per_page != 0) |
382 | d_stop = last_rec - ((last_rec - blocks_per_page + 1) % |
383 | blocks_per_page); |
384 | else |
385 | d_stop = last_rec; |
386 | |
387 | d_count = d_stop - (first_rec + cur_pos) + 1; |
388 | |
389 | ccw[-1].flags |= CCW_FLAG_CC; |
390 | locate_record(ccw: ccw++, data: LO_data++, WRITE, block_nr: cur_pos, block_ct: d_count); |
391 | |
392 | ccw[-1].flags |= CCW_FLAG_CC; |
393 | ccw_write_no_data(ccw: ccw++); |
394 | |
395 | cur_pos += d_count; |
396 | } |
397 | |
398 | /* We might still have some bits left which need to be zeroed. */ |
399 | if (cur_pos == 0 || first_rec + cur_pos - 1 < last_rec) { |
400 | if (d_stop != 0) |
401 | wz_count = last_rec - d_stop; |
402 | else if (wz_stop != 0) |
403 | wz_count = last_rec - wz_stop; |
404 | else |
405 | wz_count = count; |
406 | |
407 | ccw[-1].flags |= CCW_FLAG_CC; |
408 | locate_record(ccw: ccw++, data: LO_data++, WRITE, block_nr: cur_pos, block_ct: wz_count); |
409 | |
410 | ccw[-1].flags |= CCW_FLAG_CC; |
411 | ccw_write_zero(ccw: ccw++, count: wz_count * blksize); |
412 | } |
413 | |
414 | if (blk_noretry_request(req) || |
415 | block->base->features & DASD_FEATURE_FAILFAST) |
416 | set_bit(DASD_CQR_FLAGS_FAILFAST, addr: &cqr->flags); |
417 | |
418 | cqr->startdev = memdev; |
419 | cqr->memdev = memdev; |
420 | cqr->block = block; |
421 | cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ |
422 | cqr->retries = memdev->default_retries; |
423 | cqr->buildclk = get_tod_clock(); |
424 | cqr->status = DASD_CQR_FILLED; |
425 | |
426 | return cqr; |
427 | } |
428 | |
429 | static struct dasd_ccw_req *dasd_fba_build_cp_regular( |
430 | struct dasd_device *memdev, |
431 | struct dasd_block *block, |
432 | struct request *req) |
433 | { |
434 | struct dasd_fba_private *private = block->base->private; |
435 | unsigned long *idaws; |
436 | struct LO_fba_data *LO_data; |
437 | struct dasd_ccw_req *cqr; |
438 | struct ccw1 *ccw; |
439 | struct req_iterator iter; |
440 | struct bio_vec bv; |
441 | char *dst; |
442 | int count, cidaw, cplength, datasize; |
443 | sector_t recid, first_rec, last_rec; |
444 | unsigned int blksize, off; |
445 | unsigned char cmd; |
446 | |
447 | if (rq_data_dir(req) == READ) { |
448 | cmd = DASD_FBA_CCW_READ; |
449 | } else if (rq_data_dir(req) == WRITE) { |
450 | cmd = DASD_FBA_CCW_WRITE; |
451 | } else |
452 | return ERR_PTR(error: -EINVAL); |
453 | blksize = block->bp_block; |
454 | /* Calculate record id of first and last block. */ |
455 | first_rec = blk_rq_pos(rq: req) >> block->s2b_shift; |
456 | last_rec = |
457 | (blk_rq_pos(rq: req) + blk_rq_sectors(rq: req) - 1) >> block->s2b_shift; |
458 | /* Check struct bio and count the number of blocks for the request. */ |
459 | count = 0; |
460 | cidaw = 0; |
461 | rq_for_each_segment(bv, req, iter) { |
462 | if (bv.bv_len & (blksize - 1)) |
463 | /* Fba can only do full blocks. */ |
464 | return ERR_PTR(error: -EINVAL); |
465 | count += bv.bv_len >> (block->s2b_shift + 9); |
466 | if (idal_is_needed (page_address(bv.bv_page), bv.bv_len)) |
467 | cidaw += bv.bv_len / blksize; |
468 | } |
469 | /* Paranoia. */ |
470 | if (count != last_rec - first_rec + 1) |
471 | return ERR_PTR(error: -EINVAL); |
472 | /* 1x define extent + 1x locate record + number of blocks */ |
473 | cplength = 2 + count; |
474 | /* 1x define extent + 1x locate record */ |
475 | datasize = sizeof(struct DE_fba_data) + sizeof(struct LO_fba_data) + |
476 | cidaw * sizeof(unsigned long); |
477 | /* |
478 | * Find out number of additional locate record ccws if the device |
479 | * can't do data chaining. |
480 | */ |
481 | if (private->rdc_data.mode.bits.data_chain == 0) { |
482 | cplength += count - 1; |
483 | datasize += (count - 1)*sizeof(struct LO_fba_data); |
484 | } |
485 | /* Allocate the ccw request. */ |
486 | cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev, |
487 | blk_mq_rq_to_pdu(rq: req)); |
488 | if (IS_ERR(ptr: cqr)) |
489 | return cqr; |
490 | ccw = cqr->cpaddr; |
491 | /* First ccw is define extent. */ |
492 | define_extent(ccw: ccw++, data: cqr->data, rq_data_dir(req), |
493 | blksize: block->bp_block, beg: blk_rq_pos(rq: req), nr: blk_rq_sectors(rq: req)); |
494 | /* Build locate_record + read/write ccws. */ |
495 | idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data)); |
496 | LO_data = (struct LO_fba_data *) (idaws + cidaw); |
497 | /* Locate record for all blocks for smart devices. */ |
498 | if (private->rdc_data.mode.bits.data_chain != 0) { |
499 | ccw[-1].flags |= CCW_FLAG_CC; |
500 | locate_record(ccw: ccw++, data: LO_data++, rq_data_dir(req), block_nr: 0, block_ct: count); |
501 | } |
502 | recid = first_rec; |
503 | rq_for_each_segment(bv, req, iter) { |
504 | dst = bvec_virt(bvec: &bv); |
505 | if (dasd_page_cache) { |
506 | char *copy = kmem_cache_alloc(cachep: dasd_page_cache, |
507 | GFP_DMA | __GFP_NOWARN); |
508 | if (copy && rq_data_dir(req) == WRITE) |
509 | memcpy(copy + bv.bv_offset, dst, bv.bv_len); |
510 | if (copy) |
511 | dst = copy + bv.bv_offset; |
512 | } |
513 | for (off = 0; off < bv.bv_len; off += blksize) { |
514 | /* Locate record for stupid devices. */ |
515 | if (private->rdc_data.mode.bits.data_chain == 0) { |
516 | ccw[-1].flags |= CCW_FLAG_CC; |
517 | locate_record(ccw, data: LO_data++, |
518 | rq_data_dir(req), |
519 | block_nr: recid - first_rec, block_ct: 1); |
520 | ccw->flags = CCW_FLAG_CC; |
521 | ccw++; |
522 | } else { |
523 | if (recid > first_rec) |
524 | ccw[-1].flags |= CCW_FLAG_DC; |
525 | else |
526 | ccw[-1].flags |= CCW_FLAG_CC; |
527 | } |
528 | ccw->cmd_code = cmd; |
529 | ccw->count = block->bp_block; |
530 | if (idal_is_needed(dst, blksize)) { |
531 | ccw->cda = (__u32)virt_to_phys(address: idaws); |
532 | ccw->flags = CCW_FLAG_IDA; |
533 | idaws = idal_create_words(idaws, dst, blksize); |
534 | } else { |
535 | ccw->cda = (__u32)virt_to_phys(address: dst); |
536 | ccw->flags = 0; |
537 | } |
538 | ccw++; |
539 | dst += blksize; |
540 | recid++; |
541 | } |
542 | } |
543 | if (blk_noretry_request(req) || |
544 | block->base->features & DASD_FEATURE_FAILFAST) |
545 | set_bit(DASD_CQR_FLAGS_FAILFAST, addr: &cqr->flags); |
546 | cqr->startdev = memdev; |
547 | cqr->memdev = memdev; |
548 | cqr->block = block; |
549 | cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ |
550 | cqr->retries = memdev->default_retries; |
551 | cqr->buildclk = get_tod_clock(); |
552 | cqr->status = DASD_CQR_FILLED; |
553 | return cqr; |
554 | } |
555 | |
556 | static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device *memdev, |
557 | struct dasd_block *block, |
558 | struct request *req) |
559 | { |
560 | if (req_op(req) == REQ_OP_DISCARD || req_op(req) == REQ_OP_WRITE_ZEROES) |
561 | return dasd_fba_build_cp_discard(memdev, block, req); |
562 | else |
563 | return dasd_fba_build_cp_regular(memdev, block, req); |
564 | } |
565 | |
566 | static int |
567 | dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) |
568 | { |
569 | struct dasd_fba_private *private = cqr->block->base->private; |
570 | struct ccw1 *ccw; |
571 | struct req_iterator iter; |
572 | struct bio_vec bv; |
573 | char *dst, *cda; |
574 | unsigned int blksize, off; |
575 | int status; |
576 | |
577 | if (!dasd_page_cache) |
578 | goto out; |
579 | blksize = cqr->block->bp_block; |
580 | ccw = cqr->cpaddr; |
581 | /* Skip over define extent & locate record. */ |
582 | ccw++; |
583 | if (private->rdc_data.mode.bits.data_chain != 0) |
584 | ccw++; |
585 | rq_for_each_segment(bv, req, iter) { |
586 | dst = bvec_virt(bvec: &bv); |
587 | for (off = 0; off < bv.bv_len; off += blksize) { |
588 | /* Skip locate record. */ |
589 | if (private->rdc_data.mode.bits.data_chain == 0) |
590 | ccw++; |
591 | if (dst) { |
592 | if (ccw->flags & CCW_FLAG_IDA) |
593 | cda = *((char **)phys_to_virt(address: ccw->cda)); |
594 | else |
595 | cda = phys_to_virt(address: ccw->cda); |
596 | if (dst != cda) { |
597 | if (rq_data_dir(req) == READ) |
598 | memcpy(dst, cda, bv.bv_len); |
599 | kmem_cache_free(dasd_page_cache, |
600 | (void *)((addr_t)cda & PAGE_MASK)); |
601 | } |
602 | dst = NULL; |
603 | } |
604 | ccw++; |
605 | } |
606 | } |
607 | out: |
608 | status = cqr->status == DASD_CQR_DONE; |
609 | dasd_sfree_request(cqr, cqr->memdev); |
610 | return status; |
611 | } |
612 | |
613 | static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr) |
614 | { |
615 | if (cqr->retries < 0) |
616 | cqr->status = DASD_CQR_FAILED; |
617 | else |
618 | cqr->status = DASD_CQR_FILLED; |
619 | }; |
620 | |
621 | static int |
622 | dasd_fba_fill_info(struct dasd_device * device, |
623 | struct dasd_information2_t * info) |
624 | { |
625 | struct dasd_fba_private *private = device->private; |
626 | |
627 | info->label_block = 1; |
628 | info->FBA_layout = 1; |
629 | info->format = DASD_FORMAT_LDL; |
630 | info->characteristics_size = sizeof(private->rdc_data); |
631 | memcpy(info->characteristics, &private->rdc_data, |
632 | sizeof(private->rdc_data)); |
633 | info->confdata_size = 0; |
634 | return 0; |
635 | } |
636 | |
637 | static void |
638 | dasd_fba_dump_sense_dbf(struct dasd_device *device, struct irb *irb, |
639 | char *reason) |
640 | { |
641 | u64 *sense; |
642 | |
643 | sense = (u64 *) dasd_get_sense(irb); |
644 | if (sense) { |
645 | DBF_DEV_EVENT(DBF_EMERG, device, |
646 | "%s: %s %02x%02x%02x %016llx %016llx %016llx " |
647 | "%016llx" , reason, |
648 | scsw_is_tm(&irb->scsw) ? "t" : "c" , |
649 | scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw), |
650 | scsw_dstat(&irb->scsw), sense[0], sense[1], |
651 | sense[2], sense[3]); |
652 | } else { |
653 | DBF_DEV_EVENT(DBF_EMERG, device, "%s" , |
654 | "SORRY - NO VALID SENSE AVAILABLE\n" ); |
655 | } |
656 | } |
657 | |
658 | |
659 | static void |
660 | dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, |
661 | struct irb *irb) |
662 | { |
663 | char *page; |
664 | struct ccw1 *act, *end, *last; |
665 | int len, sl, sct, count; |
666 | |
667 | page = (char *) get_zeroed_page(GFP_ATOMIC); |
668 | if (page == NULL) { |
669 | DBF_DEV_EVENT(DBF_WARNING, device, "%s" , |
670 | "No memory to dump sense data" ); |
671 | return; |
672 | } |
673 | len = sprintf(buf: page, PRINTK_HEADER |
674 | " I/O status report for device %s:\n" , |
675 | dev_name(dev: &device->cdev->dev)); |
676 | len += sprintf(buf: page + len, PRINTK_HEADER |
677 | " in req: %p CS: 0x%02X DS: 0x%02X\n" , req, |
678 | irb->scsw.cmd.cstat, irb->scsw.cmd.dstat); |
679 | len += sprintf(page + len, PRINTK_HEADER |
680 | " device %s: Failing CCW: %p\n" , |
681 | dev_name(&device->cdev->dev), |
682 | (void *) (addr_t) irb->scsw.cmd.cpa); |
683 | if (irb->esw.esw0.erw.cons) { |
684 | for (sl = 0; sl < 4; sl++) { |
685 | len += sprintf(buf: page + len, PRINTK_HEADER |
686 | " Sense(hex) %2d-%2d:" , |
687 | (8 * sl), ((8 * sl) + 7)); |
688 | |
689 | for (sct = 0; sct < 8; sct++) { |
690 | len += sprintf(buf: page + len, fmt: " %02x" , |
691 | irb->ecw[8 * sl + sct]); |
692 | } |
693 | len += sprintf(buf: page + len, fmt: "\n" ); |
694 | } |
695 | } else { |
696 | len += sprintf(buf: page + len, PRINTK_HEADER |
697 | " SORRY - NO VALID SENSE AVAILABLE\n" ); |
698 | } |
699 | printk(KERN_ERR "%s" , page); |
700 | |
701 | /* dump the Channel Program */ |
702 | /* print first CCWs (maximum 8) */ |
703 | act = req->cpaddr; |
704 | for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); |
705 | end = min(act + 8, last); |
706 | len = sprintf(buf: page, PRINTK_HEADER " Related CP in req: %p\n" , req); |
707 | while (act <= end) { |
708 | len += sprintf(buf: page + len, PRINTK_HEADER |
709 | " CCW %p: %08X %08X DAT:" , |
710 | act, ((int *) act)[0], ((int *) act)[1]); |
711 | for (count = 0; count < 32 && count < act->count; |
712 | count += sizeof(int)) |
713 | len += sprintf(page + len, " %08X" , |
714 | ((int *) (addr_t) act->cda) |
715 | [(count>>2)]); |
716 | len += sprintf(buf: page + len, fmt: "\n" ); |
717 | act++; |
718 | } |
719 | printk(KERN_ERR "%s" , page); |
720 | |
721 | |
722 | /* print failing CCW area */ |
723 | len = 0; |
724 | if (act < ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2) { |
725 | act = ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2; |
726 | len += sprintf(buf: page + len, PRINTK_HEADER "......\n" ); |
727 | } |
728 | end = min((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa + 2, last); |
729 | while (act <= end) { |
730 | len += sprintf(buf: page + len, PRINTK_HEADER |
731 | " CCW %p: %08X %08X DAT:" , |
732 | act, ((int *) act)[0], ((int *) act)[1]); |
733 | for (count = 0; count < 32 && count < act->count; |
734 | count += sizeof(int)) |
735 | len += sprintf(page + len, " %08X" , |
736 | ((int *) (addr_t) act->cda) |
737 | [(count>>2)]); |
738 | len += sprintf(buf: page + len, fmt: "\n" ); |
739 | act++; |
740 | } |
741 | |
742 | /* print last CCWs */ |
743 | if (act < last - 2) { |
744 | act = last - 2; |
745 | len += sprintf(buf: page + len, PRINTK_HEADER "......\n" ); |
746 | } |
747 | while (act <= last) { |
748 | len += sprintf(buf: page + len, PRINTK_HEADER |
749 | " CCW %p: %08X %08X DAT:" , |
750 | act, ((int *) act)[0], ((int *) act)[1]); |
751 | for (count = 0; count < 32 && count < act->count; |
752 | count += sizeof(int)) |
753 | len += sprintf(page + len, " %08X" , |
754 | ((int *) (addr_t) act->cda) |
755 | [(count>>2)]); |
756 | len += sprintf(buf: page + len, fmt: "\n" ); |
757 | act++; |
758 | } |
759 | if (len > 0) |
760 | printk(KERN_ERR "%s" , page); |
761 | free_page((unsigned long) page); |
762 | } |
763 | |
764 | /* |
765 | * Initialize block layer request queue. |
766 | */ |
767 | static void dasd_fba_setup_blk_queue(struct dasd_block *block) |
768 | { |
769 | unsigned int logical_block_size = block->bp_block; |
770 | struct request_queue *q = block->gdp->queue; |
771 | unsigned int max_bytes, max_discard_sectors; |
772 | int max; |
773 | |
774 | max = DASD_FBA_MAX_BLOCKS << block->s2b_shift; |
775 | blk_queue_flag_set(QUEUE_FLAG_NONROT, q); |
776 | q->limits.max_dev_sectors = max; |
777 | blk_queue_logical_block_size(q, logical_block_size); |
778 | blk_queue_max_hw_sectors(q, max); |
779 | blk_queue_max_segments(q, USHRT_MAX); |
780 | /* With page sized segments each segment can be translated into one idaw/tidaw */ |
781 | blk_queue_max_segment_size(q, PAGE_SIZE); |
782 | blk_queue_segment_boundary(q, PAGE_SIZE - 1); |
783 | |
784 | q->limits.discard_granularity = logical_block_size; |
785 | |
786 | /* Calculate max_discard_sectors and make it PAGE aligned */ |
787 | max_bytes = USHRT_MAX * logical_block_size; |
788 | max_bytes = ALIGN_DOWN(max_bytes, PAGE_SIZE); |
789 | max_discard_sectors = max_bytes / logical_block_size; |
790 | |
791 | blk_queue_max_discard_sectors(q, max_discard_sectors); |
792 | blk_queue_max_write_zeroes_sectors(q, max_write_same_sectors: max_discard_sectors); |
793 | } |
794 | |
795 | static int dasd_fba_pe_handler(struct dasd_device *device, |
796 | __u8 tbvpm, __u8 fcsecpm) |
797 | { |
798 | return dasd_generic_verify_path(device, tbvpm); |
799 | } |
800 | |
801 | static struct dasd_discipline dasd_fba_discipline = { |
802 | .owner = THIS_MODULE, |
803 | .name = "FBA " , |
804 | .ebcname = "FBA " , |
805 | .check_device = dasd_fba_check_characteristics, |
806 | .do_analysis = dasd_fba_do_analysis, |
807 | .pe_handler = dasd_fba_pe_handler, |
808 | .setup_blk_queue = dasd_fba_setup_blk_queue, |
809 | .fill_geometry = dasd_fba_fill_geometry, |
810 | .start_IO = dasd_start_IO, |
811 | .term_IO = dasd_term_IO, |
812 | .handle_terminated_request = dasd_fba_handle_terminated_request, |
813 | .erp_action = dasd_fba_erp_action, |
814 | .erp_postaction = dasd_fba_erp_postaction, |
815 | .check_for_device_change = dasd_fba_check_for_device_change, |
816 | .build_cp = dasd_fba_build_cp, |
817 | .free_cp = dasd_fba_free_cp, |
818 | .dump_sense = dasd_fba_dump_sense, |
819 | .dump_sense_dbf = dasd_fba_dump_sense_dbf, |
820 | .fill_info = dasd_fba_fill_info, |
821 | }; |
822 | |
823 | static int __init |
824 | dasd_fba_init(void) |
825 | { |
826 | int ret; |
827 | |
828 | ASCEBC(dasd_fba_discipline.ebcname, 4); |
829 | |
830 | dasd_fba_zero_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
831 | if (!dasd_fba_zero_page) |
832 | return -ENOMEM; |
833 | |
834 | ret = ccw_driver_register(&dasd_fba_driver); |
835 | if (!ret) |
836 | wait_for_device_probe(); |
837 | |
838 | return ret; |
839 | } |
840 | |
841 | static void __exit |
842 | dasd_fba_cleanup(void) |
843 | { |
844 | ccw_driver_unregister(&dasd_fba_driver); |
845 | free_page((unsigned long)dasd_fba_zero_page); |
846 | } |
847 | |
848 | module_init(dasd_fba_init); |
849 | module_exit(dasd_fba_cleanup); |
850 | |