aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Weinhuber <wein@de.ibm.com>2011-01-05 06:48:02 -0500
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2011-01-05 06:47:29 -0500
commitef19298b406f93af4bb249f0776deb8366e97532 (patch)
tree8b729739a57fd50e9fe67232b1c3b1264bf678a5
parent9062014cb60194630272709da82d5879d563865e (diff)
[S390] dasd: add High Performance FICON multitrack support
Some storage systems support multitrack High Performance FICON requests, which read or write data to more than one track. This patch enables the DASD device driver to generate multitrack High Performance FICON requests. Signed-off-by: Stefan Weinhuber <wein@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r--drivers/s390/block/dasd.c4
-rw-r--r--drivers/s390/block/dasd_eckd.c158
-rw-r--r--drivers/s390/block/dasd_eckd.h6
3 files changed, 122 insertions, 46 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index faa7d425cb9c..605f96f154a5 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -745,10 +745,6 @@ struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength,
745 char *data; 745 char *data;
746 int size; 746 int size;
747 747
748 /* Sanity checks */
749 BUG_ON(datasize > PAGE_SIZE ||
750 (cplength*sizeof(struct ccw1)) > PAGE_SIZE);
751
752 size = (sizeof(struct dasd_ccw_req) + 7L) & -8L; 748 size = (sizeof(struct dasd_ccw_req) + 7L) & -8L;
753 if (cplength > 0) 749 if (cplength > 0)
754 size += cplength * sizeof(struct ccw1); 750 size += cplength * sizeof(struct ccw1);
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index bf61274af3bb..549443af121c 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1105,6 +1105,37 @@ static void dasd_eckd_validate_server(struct dasd_device *device)
1105 "returned rc=%d", private->uid.ssid, rc); 1105 "returned rc=%d", private->uid.ssid, rc);
1106} 1106}
1107 1107
1108static u32 get_fcx_max_data(struct dasd_device *device)
1109{
1110#if defined(CONFIG_64BIT)
1111 int tpm, mdc;
1112 int fcx_in_css, fcx_in_gneq, fcx_in_features;
1113 struct dasd_eckd_private *private;
1114
1115 if (dasd_nofcx)
1116 return 0;
1117 /* is transport mode supported? */
1118 private = (struct dasd_eckd_private *) device->private;
1119 fcx_in_css = css_general_characteristics.fcx;
1120 fcx_in_gneq = private->gneq->reserved2[7] & 0x04;
1121 fcx_in_features = private->features.feature[40] & 0x80;
1122 tpm = fcx_in_css && fcx_in_gneq && fcx_in_features;
1123
1124 if (!tpm)
1125 return 0;
1126
1127 mdc = ccw_device_get_mdc(device->cdev, 0);
1128 if (mdc < 0) {
1129 dev_warn(&device->cdev->dev, "Detecting the maximum supported"
1130 " data size for zHPF requests failed\n");
1131 return 0;
1132 } else
1133 return mdc * FCX_MAX_DATA_FACTOR;
1134#else
1135 return 0;
1136#endif
1137}
1138
1108/* 1139/*
1109 * Check device characteristics. 1140 * Check device characteristics.
1110 * If the device is accessible using ECKD discipline, the device is enabled. 1141 * If the device is accessible using ECKD discipline, the device is enabled.
@@ -1223,6 +1254,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
1223 else 1254 else
1224 private->real_cyl = private->rdc_data.no_cyl; 1255 private->real_cyl = private->rdc_data.no_cyl;
1225 1256
1257 private->fcx_max_data = get_fcx_max_data(device);
1258
1226 readonly = dasd_device_is_ro(device); 1259 readonly = dasd_device_is_ro(device);
1227 if (readonly) 1260 if (readonly)
1228 set_bit(DASD_FLAG_DEVICE_RO, &device->flags); 1261 set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
@@ -2326,6 +2359,12 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
2326 struct tidaw *last_tidaw = NULL; 2359 struct tidaw *last_tidaw = NULL;
2327 int itcw_op; 2360 int itcw_op;
2328 size_t itcw_size; 2361 size_t itcw_size;
2362 u8 tidaw_flags;
2363 unsigned int seg_len, part_len, len_to_track_end;
2364 unsigned char new_track;
2365 sector_t recid, trkid;
2366 unsigned int offs;
2367 unsigned int count, count_to_trk_end;
2329 2368
2330 basedev = block->base; 2369 basedev = block->base;
2331 private = (struct dasd_eckd_private *) basedev->private; 2370 private = (struct dasd_eckd_private *) basedev->private;
@@ -2341,12 +2380,16 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
2341 /* trackbased I/O needs address all memory via TIDAWs, 2380 /* trackbased I/O needs address all memory via TIDAWs,
2342 * not just for 64 bit addresses. This allows us to map 2381 * not just for 64 bit addresses. This allows us to map
2343 * each segment directly to one tidaw. 2382 * each segment directly to one tidaw.
2383 * In the case of write requests, additional tidaws may
2384 * be needed when a segment crosses a track boundary.
2344 */ 2385 */
2345 trkcount = last_trk - first_trk + 1; 2386 trkcount = last_trk - first_trk + 1;
2346 ctidaw = 0; 2387 ctidaw = 0;
2347 rq_for_each_segment(bv, req, iter) { 2388 rq_for_each_segment(bv, req, iter) {
2348 ++ctidaw; 2389 ++ctidaw;
2349 } 2390 }
2391 if (rq_data_dir(req) == WRITE)
2392 ctidaw += (last_trk - first_trk);
2350 2393
2351 /* Allocate the ccw request. */ 2394 /* Allocate the ccw request. */
2352 itcw_size = itcw_calc_size(0, ctidaw, 0); 2395 itcw_size = itcw_calc_size(0, ctidaw, 0);
@@ -2354,15 +2397,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
2354 if (IS_ERR(cqr)) 2397 if (IS_ERR(cqr))
2355 return cqr; 2398 return cqr;
2356 2399
2357 cqr->cpmode = 1;
2358 cqr->startdev = startdev;
2359 cqr->memdev = startdev;
2360 cqr->block = block;
2361 cqr->expires = 100*HZ;
2362 cqr->buildclk = get_clock();
2363 cqr->status = DASD_CQR_FILLED;
2364 cqr->retries = 10;
2365
2366 /* transfer length factor: how many bytes to read from the last track */ 2400 /* transfer length factor: how many bytes to read from the last track */
2367 if (first_trk == last_trk) 2401 if (first_trk == last_trk)
2368 tlf = last_offs - first_offs + 1; 2402 tlf = last_offs - first_offs + 1;
@@ -2371,8 +2405,11 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
2371 tlf *= blksize; 2405 tlf *= blksize;
2372 2406
2373 itcw = itcw_init(cqr->data, itcw_size, itcw_op, 0, ctidaw, 0); 2407 itcw = itcw_init(cqr->data, itcw_size, itcw_op, 0, ctidaw, 0);
2408 if (IS_ERR(itcw)) {
2409 dasd_sfree_request(cqr, startdev);
2410 return ERR_PTR(-EINVAL);
2411 }
2374 cqr->cpaddr = itcw_get_tcw(itcw); 2412 cqr->cpaddr = itcw_get_tcw(itcw);
2375
2376 if (prepare_itcw(itcw, first_trk, last_trk, 2413 if (prepare_itcw(itcw, first_trk, last_trk,
2377 cmd, basedev, startdev, 2414 cmd, basedev, startdev,
2378 first_offs + 1, 2415 first_offs + 1,
@@ -2385,26 +2422,64 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
2385 dasd_sfree_request(cqr, startdev); 2422 dasd_sfree_request(cqr, startdev);
2386 return ERR_PTR(-EAGAIN); 2423 return ERR_PTR(-EAGAIN);
2387 } 2424 }
2388
2389 /* 2425 /*
2390 * A tidaw can address 4k of memory, but must not cross page boundaries 2426 * A tidaw can address 4k of memory, but must not cross page boundaries
2391 * We can let the block layer handle this by setting 2427 * We can let the block layer handle this by setting
2392 * blk_queue_segment_boundary to page boundaries and 2428 * blk_queue_segment_boundary to page boundaries and
2393 * blk_max_segment_size to page size when setting up the request queue. 2429 * blk_max_segment_size to page size when setting up the request queue.
2430 * For write requests, a TIDAW must not cross track boundaries, because
2431 * we have to set the CBC flag on the last tidaw for each track.
2394 */ 2432 */
2395 rq_for_each_segment(bv, req, iter) { 2433 if (rq_data_dir(req) == WRITE) {
2396 dst = page_address(bv->bv_page) + bv->bv_offset; 2434 new_track = 1;
2397 last_tidaw = itcw_add_tidaw(itcw, 0x00, dst, bv->bv_len); 2435 recid = first_rec;
2398 if (IS_ERR(last_tidaw)) 2436 rq_for_each_segment(bv, req, iter) {
2399 return (struct dasd_ccw_req *)last_tidaw; 2437 dst = page_address(bv->bv_page) + bv->bv_offset;
2438 seg_len = bv->bv_len;
2439 while (seg_len) {
2440 if (new_track) {
2441 trkid = recid;
2442 offs = sector_div(trkid, blk_per_trk);
2443 count_to_trk_end = blk_per_trk - offs;
2444 count = min((last_rec - recid + 1),
2445 (sector_t)count_to_trk_end);
2446 len_to_track_end = count * blksize;
2447 recid += count;
2448 new_track = 0;
2449 }
2450 part_len = min(seg_len, len_to_track_end);
2451 seg_len -= part_len;
2452 len_to_track_end -= part_len;
2453 /* We need to end the tidaw at track end */
2454 if (!len_to_track_end) {
2455 new_track = 1;
2456 tidaw_flags = TIDAW_FLAGS_INSERT_CBC;
2457 } else
2458 tidaw_flags = 0;
2459 last_tidaw = itcw_add_tidaw(itcw, tidaw_flags,
2460 dst, part_len);
2461 if (IS_ERR(last_tidaw))
2462 return ERR_PTR(-EINVAL);
2463 dst += part_len;
2464 }
2465 }
2466 } else {
2467 rq_for_each_segment(bv, req, iter) {
2468 dst = page_address(bv->bv_page) + bv->bv_offset;
2469 last_tidaw = itcw_add_tidaw(itcw, 0x00,
2470 dst, bv->bv_len);
2471 if (IS_ERR(last_tidaw))
2472 return ERR_PTR(-EINVAL);
2473 }
2400 } 2474 }
2401 2475 last_tidaw->flags |= TIDAW_FLAGS_LAST;
2402 last_tidaw->flags |= 0x80; 2476 last_tidaw->flags &= ~TIDAW_FLAGS_INSERT_CBC;
2403 itcw_finalize(itcw); 2477 itcw_finalize(itcw);
2404 2478
2405 if (blk_noretry_request(req) || 2479 if (blk_noretry_request(req) ||
2406 block->base->features & DASD_FEATURE_FAILFAST) 2480 block->base->features & DASD_FEATURE_FAILFAST)
2407 set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); 2481 set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
2482 cqr->cpmode = 1;
2408 cqr->startdev = startdev; 2483 cqr->startdev = startdev;
2409 cqr->memdev = startdev; 2484 cqr->memdev = startdev;
2410 cqr->block = block; 2485 cqr->block = block;
@@ -2420,11 +2495,9 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
2420 struct dasd_block *block, 2495 struct dasd_block *block,
2421 struct request *req) 2496 struct request *req)
2422{ 2497{
2423 int tpm, cmdrtd, cmdwtd; 2498 int cmdrtd, cmdwtd;
2424 int use_prefix; 2499 int use_prefix;
2425#if defined(CONFIG_64BIT) 2500 int fcx_multitrack;
2426 int fcx_in_css, fcx_in_gneq, fcx_in_features;
2427#endif
2428 struct dasd_eckd_private *private; 2501 struct dasd_eckd_private *private;
2429 struct dasd_device *basedev; 2502 struct dasd_device *basedev;
2430 sector_t first_rec, last_rec; 2503 sector_t first_rec, last_rec;
@@ -2432,6 +2505,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
2432 unsigned int first_offs, last_offs; 2505 unsigned int first_offs, last_offs;
2433 unsigned int blk_per_trk, blksize; 2506 unsigned int blk_per_trk, blksize;
2434 int cdlspecial; 2507 int cdlspecial;
2508 unsigned int data_size;
2435 struct dasd_ccw_req *cqr; 2509 struct dasd_ccw_req *cqr;
2436 2510
2437 basedev = block->base; 2511 basedev = block->base;
@@ -2450,15 +2524,11 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
2450 last_offs = sector_div(last_trk, blk_per_trk); 2524 last_offs = sector_div(last_trk, blk_per_trk);
2451 cdlspecial = (private->uses_cdl && first_rec < 2*blk_per_trk); 2525 cdlspecial = (private->uses_cdl && first_rec < 2*blk_per_trk);
2452 2526
2453 /* is transport mode supported? */ 2527 fcx_multitrack = private->features.feature[40] & 0x20;
2454#if defined(CONFIG_64BIT) 2528 data_size = blk_rq_bytes(req);
2455 fcx_in_css = css_general_characteristics.fcx; 2529 /* tpm write request add CBC data on each track boundary */
2456 fcx_in_gneq = private->gneq->reserved2[7] & 0x04; 2530 if (rq_data_dir(req) == WRITE)
2457 fcx_in_features = private->features.feature[40] & 0x80; 2531 data_size += (last_trk - first_trk) * 4;
2458 tpm = fcx_in_css && fcx_in_gneq && fcx_in_features;
2459#else
2460 tpm = 0;
2461#endif
2462 2532
2463 /* is read track data and write track data in command mode supported? */ 2533 /* is read track data and write track data in command mode supported? */
2464 cmdrtd = private->features.feature[9] & 0x20; 2534 cmdrtd = private->features.feature[9] & 0x20;
@@ -2468,13 +2538,15 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
2468 cqr = NULL; 2538 cqr = NULL;
2469 if (cdlspecial || dasd_page_cache) { 2539 if (cdlspecial || dasd_page_cache) {
2470 /* do nothing, just fall through to the cmd mode single case */ 2540 /* do nothing, just fall through to the cmd mode single case */
2471 } else if (!dasd_nofcx && tpm && (first_trk == last_trk)) { 2541 } else if ((data_size <= private->fcx_max_data)
2542 && (fcx_multitrack || (first_trk == last_trk))) {
2472 cqr = dasd_eckd_build_cp_tpm_track(startdev, block, req, 2543 cqr = dasd_eckd_build_cp_tpm_track(startdev, block, req,
2473 first_rec, last_rec, 2544 first_rec, last_rec,
2474 first_trk, last_trk, 2545 first_trk, last_trk,
2475 first_offs, last_offs, 2546 first_offs, last_offs,
2476 blk_per_trk, blksize); 2547 blk_per_trk, blksize);
2477 if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN) 2548 if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) &&
2549 (PTR_ERR(cqr) != -ENOMEM))
2478 cqr = NULL; 2550 cqr = NULL;
2479 } else if (use_prefix && 2551 } else if (use_prefix &&
2480 (((rq_data_dir(req) == READ) && cmdrtd) || 2552 (((rq_data_dir(req) == READ) && cmdrtd) ||
@@ -2484,7 +2556,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
2484 first_trk, last_trk, 2556 first_trk, last_trk,
2485 first_offs, last_offs, 2557 first_offs, last_offs,
2486 blk_per_trk, blksize); 2558 blk_per_trk, blksize);
2487 if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN) 2559 if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) &&
2560 (PTR_ERR(cqr) != -ENOMEM))
2488 cqr = NULL; 2561 cqr = NULL;
2489 } 2562 }
2490 if (!cqr) 2563 if (!cqr)
@@ -3279,10 +3352,8 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
3279{ 3352{
3280 char *page; 3353 char *page;
3281 int len, sl, sct, residual; 3354 int len, sl, sct, residual;
3282
3283 struct tsb *tsb; 3355 struct tsb *tsb;
3284 u8 *sense; 3356 u8 *sense, *rcq;
3285
3286 3357
3287 page = (char *) get_zeroed_page(GFP_ATOMIC); 3358 page = (char *) get_zeroed_page(GFP_ATOMIC);
3288 if (page == NULL) { 3359 if (page == NULL) {
@@ -3348,12 +3419,15 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
3348 case 2: /* ts_ddpc */ 3419 case 2: /* ts_ddpc */
3349 len += sprintf(page + len, KERN_ERR PRINTK_HEADER 3420 len += sprintf(page + len, KERN_ERR PRINTK_HEADER
3350 " tsb->tsa.ddpc.rc %d\n", tsb->tsa.ddpc.rc); 3421 " tsb->tsa.ddpc.rc %d\n", tsb->tsa.ddpc.rc);
3351 len += sprintf(page + len, KERN_ERR PRINTK_HEADER 3422 for (sl = 0; sl < 2; sl++) {
3352 " tsb->tsa.ddpc.rcq: "); 3423 len += sprintf(page + len,
3353 for (sl = 0; sl < 16; sl++) { 3424 KERN_ERR PRINTK_HEADER
3425 " tsb->tsa.ddpc.rcq %2d-%2d: ",
3426 (8 * sl), ((8 * sl) + 7));
3427 rcq = tsb->tsa.ddpc.rcq;
3354 for (sct = 0; sct < 8; sct++) { 3428 for (sct = 0; sct < 8; sct++) {
3355 len += sprintf(page + len, " %02x", 3429 len += sprintf(page + len, " %02x",
3356 tsb->tsa.ddpc.rcq[sl]); 3430 rcq[8 * sl + sct]);
3357 } 3431 }
3358 len += sprintf(page + len, "\n"); 3432 len += sprintf(page + len, "\n");
3359 } 3433 }
@@ -3573,7 +3647,7 @@ static struct dasd_discipline dasd_eckd_discipline = {
3573 .owner = THIS_MODULE, 3647 .owner = THIS_MODULE,
3574 .name = "ECKD", 3648 .name = "ECKD",
3575 .ebcname = "ECKD", 3649 .ebcname = "ECKD",
3576 .max_blocks = 240, 3650 .max_blocks = 190,
3577 .check_device = dasd_eckd_check_characteristics, 3651 .check_device = dasd_eckd_check_characteristics,
3578 .uncheck_device = dasd_eckd_uncheck_device, 3652 .uncheck_device = dasd_eckd_uncheck_device,
3579 .do_analysis = dasd_eckd_do_analysis, 3653 .do_analysis = dasd_eckd_do_analysis,
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index 12097c24f2f5..2150aed541be 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -57,6 +57,10 @@
57 */ 57 */
58#define LV_COMPAT_CYL 0xFFFE 58#define LV_COMPAT_CYL 0xFFFE
59 59
60
61#define FCX_MAX_DATA_FACTOR 65536
62
63
60/***************************************************************************** 64/*****************************************************************************
61 * SECTION: Type Definitions 65 * SECTION: Type Definitions
62 ****************************************************************************/ 66 ****************************************************************************/
@@ -455,6 +459,8 @@ struct dasd_eckd_private {
455 struct alias_pav_group *pavgroup; 459 struct alias_pav_group *pavgroup;
456 struct alias_lcu *lcu; 460 struct alias_lcu *lcu;
457 int count; 461 int count;
462
463 u32 fcx_max_data;
458}; 464};
459 465
460 466