Commit 558b9ef0 authored by Stefan Weinhuber's avatar Stefan Weinhuber Committed by Martin Schwidefsky

s390/dasd: enable raw_track_access reads without direct I/O

The ECKD protocol supports reading of tracks with arbitrary format as
raw track images. The DASD device driver supports this in its
raw_track_access mode. In this mode it maps each track to sixteen 4096
byte sectors and rejects all requests that are not properly aligned to
this mapping.

An application that wants to use a DASD in raw_track_access mode will
usually use direct I/O to make sure that properly aligned requests are
directly submitted to the driver. However, applications that are not
aware of this mode, e.g. udev, will encounter I/O errors.

To make the use without direct I/O possible and avoid this kind of
alignment errors, we now pad unaligned read requests with a dummy
page, so that we can always read full tracks.  Please note that
writing is still only possible for full track images that are properly
aligned.
Signed-off-by: default avatarStefan Weinhuber <wein@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 5c474a1e
...@@ -85,6 +85,8 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids); ...@@ -85,6 +85,8 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);
static struct ccw_driver dasd_eckd_driver; /* see below */ static struct ccw_driver dasd_eckd_driver; /* see below */
static void *rawpadpage;
#define INIT_CQR_OK 0 #define INIT_CQR_OK 0
#define INIT_CQR_UNFORMATTED 1 #define INIT_CQR_UNFORMATTED 1
#define INIT_CQR_ERROR 2 #define INIT_CQR_ERROR 2
...@@ -3237,18 +3239,26 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, ...@@ -3237,18 +3239,26 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev,
unsigned int seg_len, len_to_track_end; unsigned int seg_len, len_to_track_end;
unsigned int first_offs; unsigned int first_offs;
unsigned int cidaw, cplength, datasize; unsigned int cidaw, cplength, datasize;
sector_t first_trk, last_trk; sector_t first_trk, last_trk, sectors;
sector_t start_padding_sectors, end_sector_offset, end_padding_sectors;
unsigned int pfx_datasize; unsigned int pfx_datasize;
/* /*
* raw track access needs to be mutiple of 64k and on 64k boundary * raw track access needs to be mutiple of 64k and on 64k boundary
* For read requests we can fix an incorrect alignment by padding
* the request with dummy pages.
*/ */
if ((blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK) != 0) { start_padding_sectors = blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK;
cqr = ERR_PTR(-EINVAL); end_sector_offset = (blk_rq_pos(req) + blk_rq_sectors(req)) %
goto out; DASD_RAW_SECTORS_PER_TRACK;
} end_padding_sectors = (DASD_RAW_SECTORS_PER_TRACK - end_sector_offset) %
if (((blk_rq_pos(req) + blk_rq_sectors(req)) % DASD_RAW_SECTORS_PER_TRACK;
DASD_RAW_SECTORS_PER_TRACK) != 0) { basedev = block->base;
if ((start_padding_sectors || end_padding_sectors) &&
(rq_data_dir(req) == WRITE)) {
DBF_DEV_EVENT(DBF_ERR, basedev,
"raw write not track aligned (%lu,%lu) req %p",
start_padding_sectors, end_padding_sectors, req);
cqr = ERR_PTR(-EINVAL); cqr = ERR_PTR(-EINVAL);
goto out; goto out;
} }
...@@ -3258,7 +3268,6 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, ...@@ -3258,7 +3268,6 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev,
DASD_RAW_SECTORS_PER_TRACK; DASD_RAW_SECTORS_PER_TRACK;
trkcount = last_trk - first_trk + 1; trkcount = last_trk - first_trk + 1;
first_offs = 0; first_offs = 0;
basedev = block->base;
if (rq_data_dir(req) == READ) if (rq_data_dir(req) == READ)
cmd = DASD_ECKD_CCW_READ_TRACK; cmd = DASD_ECKD_CCW_READ_TRACK;
...@@ -3307,12 +3316,26 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, ...@@ -3307,12 +3316,26 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev,
} }
idaws = (unsigned long *)(cqr->data + pfx_datasize); idaws = (unsigned long *)(cqr->data + pfx_datasize);
len_to_track_end = 0; len_to_track_end = 0;
if (start_padding_sectors) {
ccw[-1].flags |= CCW_FLAG_CC;
ccw->cmd_code = cmd;
/* maximum 3390 track size */
ccw->count = 57326;
/* 64k map to one track */
len_to_track_end = 65536 - start_padding_sectors * 512;
ccw->cda = (__u32)(addr_t)idaws;
ccw->flags |= CCW_FLAG_IDA;
ccw->flags |= CCW_FLAG_SLI;
ccw++;
for (sectors = 0; sectors < start_padding_sectors; sectors += 8)
idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE);
}
rq_for_each_segment(bv, req, iter) { rq_for_each_segment(bv, req, iter) {
dst = page_address(bv->bv_page) + bv->bv_offset; dst = page_address(bv->bv_page) + bv->bv_offset;
seg_len = bv->bv_len; seg_len = bv->bv_len;
if (cmd == DASD_ECKD_CCW_READ_TRACK)
memset(dst, 0, seg_len);
if (!len_to_track_end) { if (!len_to_track_end) {
ccw[-1].flags |= CCW_FLAG_CC; ccw[-1].flags |= CCW_FLAG_CC;
ccw->cmd_code = cmd; ccw->cmd_code = cmd;
...@@ -3328,7 +3351,8 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, ...@@ -3328,7 +3351,8 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev,
len_to_track_end -= seg_len; len_to_track_end -= seg_len;
idaws = idal_create_words(idaws, dst, seg_len); idaws = idal_create_words(idaws, dst, seg_len);
} }
for (sectors = 0; sectors < end_padding_sectors; sectors += 8)
idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE);
if (blk_noretry_request(req) || if (blk_noretry_request(req) ||
block->base->features & DASD_FEATURE_FAILFAST) block->base->features & DASD_FEATURE_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
...@@ -4479,12 +4503,19 @@ dasd_eckd_init(void) ...@@ -4479,12 +4503,19 @@ dasd_eckd_init(void)
kfree(dasd_reserve_req); kfree(dasd_reserve_req);
return -ENOMEM; return -ENOMEM;
} }
rawpadpage = (void *)__get_free_page(GFP_KERNEL);
if (!rawpadpage) {
kfree(path_verification_worker);
kfree(dasd_reserve_req);
return -ENOMEM;
}
ret = ccw_driver_register(&dasd_eckd_driver); ret = ccw_driver_register(&dasd_eckd_driver);
if (!ret) if (!ret)
wait_for_device_probe(); wait_for_device_probe();
else { else {
kfree(path_verification_worker); kfree(path_verification_worker);
kfree(dasd_reserve_req); kfree(dasd_reserve_req);
free_page((unsigned long)rawpadpage);
} }
return ret; return ret;
} }
...@@ -4495,6 +4526,7 @@ dasd_eckd_cleanup(void) ...@@ -4495,6 +4526,7 @@ dasd_eckd_cleanup(void)
ccw_driver_unregister(&dasd_eckd_driver); ccw_driver_unregister(&dasd_eckd_driver);
kfree(path_verification_worker); kfree(path_verification_worker);
kfree(dasd_reserve_req); kfree(dasd_reserve_req);
free_page((unsigned long)rawpadpage);
} }
module_init(dasd_eckd_init); module_init(dasd_eckd_init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment