diff options
author | Jan Höppner <hoeppner@linux.ibm.com> | 2018-04-27 16:51:22 +0200 |
---|---|---|
committer | Vasily Gorbik <gor@linux.ibm.com> | 2019-07-11 20:39:53 +0200 |
commit | 5e2b17e712cf10cc3cc98fde28a88e8f1a1267e9 (patch) | |
tree | 62dbb4a3d1a83bead5ec3c1b57d21d16afa24b89 /drivers/s390/block/dasd_eckd.c | |
parent | c729696bcf8b23450043dd9c9972c15e53419ae4 (diff) | |
download | lwn-5e2b17e712cf10cc3cc98fde28a88e8f1a1267e9.tar.gz lwn-5e2b17e712cf10cc3cc98fde28a88e8f1a1267e9.zip |
s390/dasd: Add dynamic formatting support for ESE volumes
A dynamic formatting is issued whenever a write request returns with
either a No Record Found error (Command Mode), Incorrect Length error
(Transport Mode), or File Protected error (Transport Mode). All three
cases mean that the tracks in question haven't been initialized in a
desired format yet.
The part of the volume that was tried to be written on is then formatted
and the original request is re-queued.
As the formatting will happen during normal I/O operations, it is quite
likely that there won't be any memory available to build the respective
request. Another two pages of memory are allocated per volume
specifically for the dynamic formatting.
The dasd_eckd_build_format() function is extended to make sure that the
original startdev is reused. Also, all formatting and format check
functions use the new memory pool exclusively now to reduce complexity.
Read operations will always return zero data when unformatted areas are
read.
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Diffstat (limited to 'drivers/s390/block/dasd_eckd.c')
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 134 |
1 files changed, 121 insertions, 13 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 67156d46c236..6109a0e68911 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2335,8 +2335,7 @@ dasd_eckd_build_check_tcw(struct dasd_device *base, struct format_data_t *fdata, */ itcw_size = itcw_calc_size(0, count, 0); - cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev, - NULL); + cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev); if (IS_ERR(cqr)) return cqr; @@ -2429,8 +2428,7 @@ dasd_eckd_build_check(struct dasd_device *base, struct format_data_t *fdata, } cplength += count; - cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, - startdev, NULL); + cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev); if (IS_ERR(cqr)) return cqr; @@ -2477,13 +2475,11 @@ dasd_eckd_build_check(struct dasd_device *base, struct format_data_t *fdata, } static struct dasd_ccw_req * -dasd_eckd_build_format(struct dasd_device *base, - struct format_data_t *fdata, - int enable_pav) +dasd_eckd_build_format(struct dasd_device *base, struct dasd_device *startdev, + struct format_data_t *fdata, int enable_pav) { struct dasd_eckd_private *base_priv; struct dasd_eckd_private *start_priv; - struct dasd_device *startdev = NULL; struct dasd_ccw_req *fcp; struct eckd_count *ect; struct ch_t address; @@ -2574,9 +2570,8 @@ dasd_eckd_build_format(struct dasd_device *base, fdata->intensity); return ERR_PTR(-EINVAL); } - /* Allocate the format ccw request. */ - fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, - datasize, startdev, NULL); + + fcp = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev); if (IS_ERR(fcp)) return fcp; @@ -2749,7 +2744,7 @@ dasd_eckd_format_build_ccw_req(struct dasd_device *base, struct dasd_ccw_req *ccw_req; if (!fmt_buffer) { - ccw_req = dasd_eckd_build_format(base, fdata, enable_pav); + ccw_req = dasd_eckd_build_format(base, NULL, fdata, enable_pav); } else { if (tpm) ccw_req = dasd_eckd_build_check_tcw(base, fdata, @@ -2895,7 +2890,7 @@ out_err: rc = -EIO; } list_del_init(&cqr->blocklist); - dasd_sfree_request(cqr, device); + dasd_ffree_request(cqr, device); private->count--; } @@ -2935,6 +2930,96 @@ static int dasd_eckd_format_device(struct dasd_device *base, } /* + * Callback function to free ESE format requests. + */ +static void dasd_eckd_ese_format_cb(struct dasd_ccw_req *cqr, void *data) +{ + struct dasd_device *device = cqr->startdev; + struct dasd_eckd_private *private = device->private; + + private->count--; + dasd_ffree_request(cqr, device); +} + +static struct dasd_ccw_req * +dasd_eckd_ese_format(struct dasd_device *startdev, struct dasd_ccw_req *cqr) +{ + struct dasd_eckd_private *private; + struct format_data_t fdata; + unsigned int recs_per_trk; + struct dasd_ccw_req *fcqr; + struct dasd_device *base; + struct dasd_block *block; + unsigned int blksize; + struct request *req; + sector_t first_trk; + sector_t last_trk; + int rc; + + req = cqr->callback_data; + base = cqr->block->base; + private = base->private; + block = base->block; + blksize = block->bp_block; + recs_per_trk = recs_per_track(&private->rdc_data, 0, blksize); + + first_trk = blk_rq_pos(req) >> block->s2b_shift; + sector_div(first_trk, recs_per_trk); + last_trk = + (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift; + sector_div(last_trk, recs_per_trk); + + fdata.start_unit = first_trk; + fdata.stop_unit = last_trk; + fdata.blksize = blksize; + fdata.intensity = private->uses_cdl ? DASD_FMT_INT_COMPAT : 0; + + rc = dasd_eckd_format_sanity_checks(base, &fdata); + if (rc) + return ERR_PTR(-EINVAL); + + /* + * We're building the request with PAV disabled as we're reusing + * the former startdev. + */ + fcqr = dasd_eckd_build_format(base, startdev, &fdata, 0); + if (IS_ERR(fcqr)) + return fcqr; + + fcqr->callback = dasd_eckd_ese_format_cb; + + return fcqr; +} + +/* + * When data is read from an unformatted area of an ESE volume, this function + * returns zeroed data and thereby mimics a read of zero data. + */ +static void dasd_eckd_ese_read(struct dasd_ccw_req *cqr) +{ + unsigned int blksize, off; + struct dasd_device *base; + struct req_iterator iter; + struct request *req; + struct bio_vec bv; + char *dst; + + req = (struct request *) cqr->callback_data; + base = cqr->block->base; + blksize = base->block->bp_block; + + rq_for_each_segment(bv, req, iter) { + dst = page_address(bv.bv_page) + bv.bv_offset; + for (off = 0; off < bv.bv_len; off += blksize) { + if (dst && rq_data_dir(req) == READ) { + dst += off; + memset(dst, 0, blksize); + } + } + } +} + +/* * Helper function to count consecutive records of a single track. */ static int dasd_eckd_count_records(struct eckd_count *fmt_buffer, int start, @@ -3450,6 +3535,14 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; + + /* Set flags to suppress output for expected errors */ + if (dasd_eckd_is_ese(basedev)) { + set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags); + set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags); + set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); + } + return cqr; } @@ -3621,6 +3714,11 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; + + /* Set flags to suppress output for expected errors */ + if (dasd_eckd_is_ese(basedev)) + set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); + return cqr; } @@ -3940,6 +4038,14 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; + + /* Set flags to suppress output for expected errors */ + if (dasd_eckd_is_ese(basedev)) { + set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags); + set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags); + set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); + } + return cqr; out_error: dasd_sfree_request(cqr, startdev); @@ -6061,6 +6167,8 @@ static struct dasd_discipline dasd_eckd_discipline = { .ext_pool_cap_at_warnlevel = dasd_eckd_ext_pool_cap_at_warnlevel, .ext_pool_warn_thrshld = dasd_eckd_ext_pool_warn_thrshld, .ext_pool_oos = dasd_eckd_ext_pool_oos, + .ese_format = dasd_eckd_ese_format, + .ese_read = dasd_eckd_ese_read, }; static int __init |