diff options
-rw-r--r-- | drivers/s390/block/dasd.c | 4 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 158 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.h | 6 |
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 | ||
1108 | static 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 | ||