diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 54 |
1 files changed, 43 insertions, 11 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index e61a6deea3c0..5adb2042e824 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c | |||
@@ -85,6 +85,8 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids); | |||
85 | 85 | ||
86 | static struct ccw_driver dasd_eckd_driver; /* see below */ | 86 | static struct ccw_driver dasd_eckd_driver; /* see below */ |
87 | 87 | ||
88 | static void *rawpadpage; | ||
89 | |||
88 | #define INIT_CQR_OK 0 | 90 | #define INIT_CQR_OK 0 |
89 | #define INIT_CQR_UNFORMATTED 1 | 91 | #define INIT_CQR_UNFORMATTED 1 |
90 | #define INIT_CQR_ERROR 2 | 92 | #define INIT_CQR_ERROR 2 |
@@ -3237,18 +3239,26 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, | |||
3237 | unsigned int seg_len, len_to_track_end; | 3239 | unsigned int seg_len, len_to_track_end; |
3238 | unsigned int first_offs; | 3240 | unsigned int first_offs; |
3239 | unsigned int cidaw, cplength, datasize; | 3241 | unsigned int cidaw, cplength, datasize; |
3240 | sector_t first_trk, last_trk; | 3242 | sector_t first_trk, last_trk, sectors; |
3243 | sector_t start_padding_sectors, end_sector_offset, end_padding_sectors; | ||
3241 | unsigned int pfx_datasize; | 3244 | unsigned int pfx_datasize; |
3242 | 3245 | ||
3243 | /* | 3246 | /* |
3244 | * raw track access needs to be mutiple of 64k and on 64k boundary | 3247 | * raw track access needs to be mutiple of 64k and on 64k boundary |
3248 | * For read requests we can fix an incorrect alignment by padding | ||
3249 | * the request with dummy pages. | ||
3245 | */ | 3250 | */ |
3246 | if ((blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK) != 0) { | 3251 | start_padding_sectors = blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK; |
3247 | cqr = ERR_PTR(-EINVAL); | 3252 | end_sector_offset = (blk_rq_pos(req) + blk_rq_sectors(req)) % |
3248 | goto out; | 3253 | DASD_RAW_SECTORS_PER_TRACK; |
3249 | } | 3254 | end_padding_sectors = (DASD_RAW_SECTORS_PER_TRACK - end_sector_offset) % |
3250 | if (((blk_rq_pos(req) + blk_rq_sectors(req)) % | 3255 | DASD_RAW_SECTORS_PER_TRACK; |
3251 | DASD_RAW_SECTORS_PER_TRACK) != 0) { | 3256 | basedev = block->base; |
3257 | if ((start_padding_sectors || end_padding_sectors) && | ||
3258 | (rq_data_dir(req) == WRITE)) { | ||
3259 | DBF_DEV_EVENT(DBF_ERR, basedev, | ||
3260 | "raw write not track aligned (%lu,%lu) req %p", | ||
3261 | start_padding_sectors, end_padding_sectors, req); | ||
3252 | cqr = ERR_PTR(-EINVAL); | 3262 | cqr = ERR_PTR(-EINVAL); |
3253 | goto out; | 3263 | goto out; |
3254 | } | 3264 | } |
@@ -3258,7 +3268,6 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, | |||
3258 | DASD_RAW_SECTORS_PER_TRACK; | 3268 | DASD_RAW_SECTORS_PER_TRACK; |
3259 | trkcount = last_trk - first_trk + 1; | 3269 | trkcount = last_trk - first_trk + 1; |
3260 | first_offs = 0; | 3270 | first_offs = 0; |
3261 | basedev = block->base; | ||
3262 | 3271 | ||
3263 | if (rq_data_dir(req) == READ) | 3272 | if (rq_data_dir(req) == READ) |
3264 | cmd = DASD_ECKD_CCW_READ_TRACK; | 3273 | cmd = DASD_ECKD_CCW_READ_TRACK; |
@@ -3307,12 +3316,26 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, | |||
3307 | } | 3316 | } |
3308 | 3317 | ||
3309 | idaws = (unsigned long *)(cqr->data + pfx_datasize); | 3318 | idaws = (unsigned long *)(cqr->data + pfx_datasize); |
3310 | |||
3311 | len_to_track_end = 0; | 3319 | len_to_track_end = 0; |
3312 | 3320 | if (start_padding_sectors) { | |
3321 | ccw[-1].flags |= CCW_FLAG_CC; | ||
3322 | ccw->cmd_code = cmd; | ||
3323 | /* maximum 3390 track size */ | ||
3324 | ccw->count = 57326; | ||
3325 | /* 64k map to one track */ | ||
3326 | len_to_track_end = 65536 - start_padding_sectors * 512; | ||
3327 | ccw->cda = (__u32)(addr_t)idaws; | ||
3328 | ccw->flags |= CCW_FLAG_IDA; | ||
3329 | ccw->flags |= CCW_FLAG_SLI; | ||
3330 | ccw++; | ||
3331 | for (sectors = 0; sectors < start_padding_sectors; sectors += 8) | ||
3332 | idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE); | ||
3333 | } | ||
3313 | rq_for_each_segment(bv, req, iter) { | 3334 | rq_for_each_segment(bv, req, iter) { |
3314 | dst = page_address(bv->bv_page) + bv->bv_offset; | 3335 | dst = page_address(bv->bv_page) + bv->bv_offset; |
3315 | seg_len = bv->bv_len; | 3336 | seg_len = bv->bv_len; |
3337 | if (cmd == DASD_ECKD_CCW_READ_TRACK) | ||
3338 | memset(dst, 0, seg_len); | ||
3316 | if (!len_to_track_end) { | 3339 | if (!len_to_track_end) { |
3317 | ccw[-1].flags |= CCW_FLAG_CC; | 3340 | ccw[-1].flags |= CCW_FLAG_CC; |
3318 | ccw->cmd_code = cmd; | 3341 | ccw->cmd_code = cmd; |
@@ -3328,7 +3351,8 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, | |||
3328 | len_to_track_end -= seg_len; | 3351 | len_to_track_end -= seg_len; |
3329 | idaws = idal_create_words(idaws, dst, seg_len); | 3352 | idaws = idal_create_words(idaws, dst, seg_len); |
3330 | } | 3353 | } |
3331 | 3354 | for (sectors = 0; sectors < end_padding_sectors; sectors += 8) | |
3355 | idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE); | ||
3332 | if (blk_noretry_request(req) || | 3356 | if (blk_noretry_request(req) || |
3333 | block->base->features & DASD_FEATURE_FAILFAST) | 3357 | block->base->features & DASD_FEATURE_FAILFAST) |
3334 | set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); | 3358 | set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); |
@@ -4479,12 +4503,19 @@ dasd_eckd_init(void) | |||
4479 | kfree(dasd_reserve_req); | 4503 | kfree(dasd_reserve_req); |
4480 | return -ENOMEM; | 4504 | return -ENOMEM; |
4481 | } | 4505 | } |
4506 | rawpadpage = (void *)__get_free_page(GFP_KERNEL); | ||
4507 | if (!rawpadpage) { | ||
4508 | kfree(path_verification_worker); | ||
4509 | kfree(dasd_reserve_req); | ||
4510 | return -ENOMEM; | ||
4511 | } | ||
4482 | ret = ccw_driver_register(&dasd_eckd_driver); | 4512 | ret = ccw_driver_register(&dasd_eckd_driver); |
4483 | if (!ret) | 4513 | if (!ret) |
4484 | wait_for_device_probe(); | 4514 | wait_for_device_probe(); |
4485 | else { | 4515 | else { |
4486 | kfree(path_verification_worker); | 4516 | kfree(path_verification_worker); |
4487 | kfree(dasd_reserve_req); | 4517 | kfree(dasd_reserve_req); |
4518 | free_page((unsigned long)rawpadpage); | ||
4488 | } | 4519 | } |
4489 | return ret; | 4520 | return ret; |
4490 | } | 4521 | } |
@@ -4495,6 +4526,7 @@ dasd_eckd_cleanup(void) | |||
4495 | ccw_driver_unregister(&dasd_eckd_driver); | 4526 | ccw_driver_unregister(&dasd_eckd_driver); |
4496 | kfree(path_verification_worker); | 4527 | kfree(path_verification_worker); |
4497 | kfree(dasd_reserve_req); | 4528 | kfree(dasd_reserve_req); |
4529 | free_page((unsigned long)rawpadpage); | ||
4498 | } | 4530 | } |
4499 | 4531 | ||
4500 | module_init(dasd_eckd_init); | 4532 | module_init(dasd_eckd_init); |