Skip to content

Commit 5e2b17e

Browse files
hoeppnerjVasily Gorbik
authored andcommitted
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 <[email protected]> Reviewed-by: Stefan Haberland <[email protected]> Signed-off-by: Vasily Gorbik <[email protected]>
1 parent c729696 commit 5e2b17e

File tree

3 files changed

+239
-14
lines changed

3 files changed

+239
-14
lines changed

drivers/s390/block/dasd.c

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,18 @@ struct dasd_device *dasd_alloc_device(void)
120120
kfree(device);
121121
return ERR_PTR(-ENOMEM);
122122
}
123+
/* Get two pages for ese format. */
124+
device->ese_mem = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA, 1);
125+
if (!device->ese_mem) {
126+
free_page((unsigned long) device->erp_mem);
127+
free_pages((unsigned long) device->ccw_mem, 1);
128+
kfree(device);
129+
return ERR_PTR(-ENOMEM);
130+
}
123131

124132
dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2);
125133
dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE);
134+
dasd_init_chunklist(&device->ese_chunks, device->ese_mem, PAGE_SIZE * 2);
126135
spin_lock_init(&device->mem_lock);
127136
atomic_set(&device->tasklet_scheduled, 0);
128137
tasklet_init(&device->tasklet, dasd_device_tasklet,
@@ -146,6 +155,7 @@ struct dasd_device *dasd_alloc_device(void)
146155
void dasd_free_device(struct dasd_device *device)
147156
{
148157
kfree(device->private);
158+
free_pages((unsigned long) device->ese_mem, 1);
149159
free_page((unsigned long) device->erp_mem);
150160
free_pages((unsigned long) device->ccw_mem, 1);
151161
kfree(device);
@@ -1258,6 +1268,49 @@ struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength, int datasize,
12581268
}
12591269
EXPORT_SYMBOL(dasd_smalloc_request);
12601270

1271+
struct dasd_ccw_req *dasd_fmalloc_request(int magic, int cplength,
1272+
int datasize,
1273+
struct dasd_device *device)
1274+
{
1275+
struct dasd_ccw_req *cqr;
1276+
unsigned long flags;
1277+
int size, cqr_size;
1278+
char *data;
1279+
1280+
cqr_size = (sizeof(*cqr) + 7L) & -8L;
1281+
size = cqr_size;
1282+
if (cplength > 0)
1283+
size += cplength * sizeof(struct ccw1);
1284+
if (datasize > 0)
1285+
size += datasize;
1286+
1287+
spin_lock_irqsave(&device->mem_lock, flags);
1288+
cqr = dasd_alloc_chunk(&device->ese_chunks, size);
1289+
spin_unlock_irqrestore(&device->mem_lock, flags);
1290+
if (!cqr)
1291+
return ERR_PTR(-ENOMEM);
1292+
memset(cqr, 0, sizeof(*cqr));
1293+
data = (char *)cqr + cqr_size;
1294+
cqr->cpaddr = NULL;
1295+
if (cplength > 0) {
1296+
cqr->cpaddr = data;
1297+
data += cplength * sizeof(struct ccw1);
1298+
memset(cqr->cpaddr, 0, cplength * sizeof(struct ccw1));
1299+
}
1300+
cqr->data = NULL;
1301+
if (datasize > 0) {
1302+
cqr->data = data;
1303+
memset(cqr->data, 0, datasize);
1304+
}
1305+
1306+
cqr->magic = magic;
1307+
set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
1308+
dasd_get_device(device);
1309+
1310+
return cqr;
1311+
}
1312+
EXPORT_SYMBOL(dasd_fmalloc_request);
1313+
12611314
void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
12621315
{
12631316
unsigned long flags;
@@ -1269,6 +1322,17 @@ void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
12691322
}
12701323
EXPORT_SYMBOL(dasd_sfree_request);
12711324

1325+
void dasd_ffree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
1326+
{
1327+
unsigned long flags;
1328+
1329+
spin_lock_irqsave(&device->mem_lock, flags);
1330+
dasd_free_chunk(&device->ese_chunks, cqr);
1331+
spin_unlock_irqrestore(&device->mem_lock, flags);
1332+
dasd_put_device(device);
1333+
}
1334+
EXPORT_SYMBOL(dasd_ffree_request);
1335+
12721336
/*
12731337
* Check discipline magic in cqr.
12741338
*/
@@ -1573,13 +1637,35 @@ static int dasd_check_hpf_error(struct irb *irb)
15731637
irb->scsw.tm.sesq == SCSW_SESQ_PATH_NOFCX));
15741638
}
15751639

1640+
static int dasd_ese_needs_format(struct dasd_block *block, struct irb *irb)
1641+
{
1642+
struct dasd_device *device = NULL;
1643+
u8 *sense = NULL;
1644+
1645+
if (!block)
1646+
return 0;
1647+
device = block->base;
1648+
if (!device || !device->discipline->is_ese)
1649+
return 0;
1650+
if (!device->discipline->is_ese(device))
1651+
return 0;
1652+
1653+
sense = dasd_get_sense(irb);
1654+
if (!sense)
1655+
return 0;
1656+
1657+
return !!(sense[1] & SNS1_NO_REC_FOUND) ||
1658+
!!(sense[1] & SNS1_FILE_PROTECTED) ||
1659+
scsw_cstat(&irb->scsw) == SCHN_STAT_INCORR_LEN;
1660+
}
1661+
15761662
/*
15771663
* Interrupt handler for "normal" ssch-io based dasd devices.
15781664
*/
15791665
void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
15801666
struct irb *irb)
15811667
{
1582-
struct dasd_ccw_req *cqr, *next;
1668+
struct dasd_ccw_req *cqr, *next, *fcqr;
15831669
struct dasd_device *device;
15841670
unsigned long now;
15851671
int nrf_suppressed = 0;
@@ -1672,6 +1758,31 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
16721758
return;
16731759
}
16741760

1761+
if (dasd_ese_needs_format(cqr->block, irb)) {
1762+
if (rq_data_dir((struct request *)cqr->callback_data) == READ) {
1763+
device->discipline->ese_read(cqr);
1764+
cqr->status = DASD_CQR_SUCCESS;
1765+
cqr->stopclk = now;
1766+
dasd_device_clear_timer(device);
1767+
dasd_schedule_device_bh(device);
1768+
return;
1769+
}
1770+
fcqr = device->discipline->ese_format(device, cqr);
1771+
if (IS_ERR(fcqr)) {
1772+
/*
1773+
* If we can't format now, let the request go
1774+
* one extra round. Maybe we can format later.
1775+
*/
1776+
cqr->status = DASD_CQR_QUEUED;
1777+
} else {
1778+
fcqr->status = DASD_CQR_QUEUED;
1779+
cqr->status = DASD_CQR_QUEUED;
1780+
list_add(&fcqr->devlist, &device->ccw_queue);
1781+
dasd_schedule_device_bh(device);
1782+
return;
1783+
}
1784+
}
1785+
16751786
/* Check for clear pending */
16761787
if (cqr->status == DASD_CQR_CLEAR_PENDING &&
16771788
scsw_fctl(&irb->scsw) & SCSW_FCTL_CLEAR_FUNC) {

drivers/s390/block/dasd_eckd.c

Lines changed: 121 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2335,8 +2335,7 @@ dasd_eckd_build_check_tcw(struct dasd_device *base, struct format_data_t *fdata,
23352335
*/
23362336
itcw_size = itcw_calc_size(0, count, 0);
23372337

2338-
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev,
2339-
NULL);
2338+
cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev);
23402339
if (IS_ERR(cqr))
23412340
return cqr;
23422341

@@ -2429,8 +2428,7 @@ dasd_eckd_build_check(struct dasd_device *base, struct format_data_t *fdata,
24292428
}
24302429
cplength += count;
24312430

2432-
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize,
2433-
startdev, NULL);
2431+
cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev);
24342432
if (IS_ERR(cqr))
24352433
return cqr;
24362434

@@ -2477,13 +2475,11 @@ dasd_eckd_build_check(struct dasd_device *base, struct format_data_t *fdata,
24772475
}
24782476

24792477
static struct dasd_ccw_req *
2480-
dasd_eckd_build_format(struct dasd_device *base,
2481-
struct format_data_t *fdata,
2482-
int enable_pav)
2478+
dasd_eckd_build_format(struct dasd_device *base, struct dasd_device *startdev,
2479+
struct format_data_t *fdata, int enable_pav)
24832480
{
24842481
struct dasd_eckd_private *base_priv;
24852482
struct dasd_eckd_private *start_priv;
2486-
struct dasd_device *startdev = NULL;
24872483
struct dasd_ccw_req *fcp;
24882484
struct eckd_count *ect;
24892485
struct ch_t address;
@@ -2574,9 +2570,8 @@ dasd_eckd_build_format(struct dasd_device *base,
25742570
fdata->intensity);
25752571
return ERR_PTR(-EINVAL);
25762572
}
2577-
/* Allocate the format ccw request. */
2578-
fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength,
2579-
datasize, startdev, NULL);
2573+
2574+
fcp = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev);
25802575
if (IS_ERR(fcp))
25812576
return fcp;
25822577

@@ -2749,7 +2744,7 @@ dasd_eckd_format_build_ccw_req(struct dasd_device *base,
27492744
struct dasd_ccw_req *ccw_req;
27502745

27512746
if (!fmt_buffer) {
2752-
ccw_req = dasd_eckd_build_format(base, fdata, enable_pav);
2747+
ccw_req = dasd_eckd_build_format(base, NULL, fdata, enable_pav);
27532748
} else {
27542749
if (tpm)
27552750
ccw_req = dasd_eckd_build_check_tcw(base, fdata,
@@ -2895,7 +2890,7 @@ static int dasd_eckd_format_process_data(struct dasd_device *base,
28952890
rc = -EIO;
28962891
}
28972892
list_del_init(&cqr->blocklist);
2898-
dasd_sfree_request(cqr, device);
2893+
dasd_ffree_request(cqr, device);
28992894
private->count--;
29002895
}
29012896

@@ -2934,6 +2929,96 @@ static int dasd_eckd_format_device(struct dasd_device *base,
29342929
0, NULL);
29352930
}
29362931

2932+
/*
2933+
* Callback function to free ESE format requests.
2934+
*/
2935+
static void dasd_eckd_ese_format_cb(struct dasd_ccw_req *cqr, void *data)
2936+
{
2937+
struct dasd_device *device = cqr->startdev;
2938+
struct dasd_eckd_private *private = device->private;
2939+
2940+
private->count--;
2941+
dasd_ffree_request(cqr, device);
2942+
}
2943+
2944+
static struct dasd_ccw_req *
2945+
dasd_eckd_ese_format(struct dasd_device *startdev, struct dasd_ccw_req *cqr)
2946+
{
2947+
struct dasd_eckd_private *private;
2948+
struct format_data_t fdata;
2949+
unsigned int recs_per_trk;
2950+
struct dasd_ccw_req *fcqr;
2951+
struct dasd_device *base;
2952+
struct dasd_block *block;
2953+
unsigned int blksize;
2954+
struct request *req;
2955+
sector_t first_trk;
2956+
sector_t last_trk;
2957+
int rc;
2958+
2959+
req = cqr->callback_data;
2960+
base = cqr->block->base;
2961+
private = base->private;
2962+
block = base->block;
2963+
blksize = block->bp_block;
2964+
recs_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
2965+
2966+
first_trk = blk_rq_pos(req) >> block->s2b_shift;
2967+
sector_div(first_trk, recs_per_trk);
2968+
last_trk =
2969+
(blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;
2970+
sector_div(last_trk, recs_per_trk);
2971+
2972+
fdata.start_unit = first_trk;
2973+
fdata.stop_unit = last_trk;
2974+
fdata.blksize = blksize;
2975+
fdata.intensity = private->uses_cdl ? DASD_FMT_INT_COMPAT : 0;
2976+
2977+
rc = dasd_eckd_format_sanity_checks(base, &fdata);
2978+
if (rc)
2979+
return ERR_PTR(-EINVAL);
2980+
2981+
/*
2982+
* We're building the request with PAV disabled as we're reusing
2983+
* the former startdev.
2984+
*/
2985+
fcqr = dasd_eckd_build_format(base, startdev, &fdata, 0);
2986+
if (IS_ERR(fcqr))
2987+
return fcqr;
2988+
2989+
fcqr->callback = dasd_eckd_ese_format_cb;
2990+
2991+
return fcqr;
2992+
}
2993+
2994+
/*
2995+
* When data is read from an unformatted area of an ESE volume, this function
2996+
* returns zeroed data and thereby mimics a read of zero data.
2997+
*/
2998+
static void dasd_eckd_ese_read(struct dasd_ccw_req *cqr)
2999+
{
3000+
unsigned int blksize, off;
3001+
struct dasd_device *base;
3002+
struct req_iterator iter;
3003+
struct request *req;
3004+
struct bio_vec bv;
3005+
char *dst;
3006+
3007+
req = (struct request *) cqr->callback_data;
3008+
base = cqr->block->base;
3009+
blksize = base->block->bp_block;
3010+
3011+
rq_for_each_segment(bv, req, iter) {
3012+
dst = page_address(bv.bv_page) + bv.bv_offset;
3013+
for (off = 0; off < bv.bv_len; off += blksize) {
3014+
if (dst && rq_data_dir(req) == READ) {
3015+
dst += off;
3016+
memset(dst, 0, blksize);
3017+
}
3018+
}
3019+
}
3020+
}
3021+
29373022
/*
29383023
* Helper function to count consecutive records of a single track.
29393024
*/
@@ -3450,6 +3535,14 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single(
34503535
cqr->retries = startdev->default_retries;
34513536
cqr->buildclk = get_tod_clock();
34523537
cqr->status = DASD_CQR_FILLED;
3538+
3539+
/* Set flags to suppress output for expected errors */
3540+
if (dasd_eckd_is_ese(basedev)) {
3541+
set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags);
3542+
set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags);
3543+
set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
3544+
}
3545+
34533546
return cqr;
34543547
}
34553548

@@ -3621,6 +3714,11 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
36213714
cqr->retries = startdev->default_retries;
36223715
cqr->buildclk = get_tod_clock();
36233716
cqr->status = DASD_CQR_FILLED;
3717+
3718+
/* Set flags to suppress output for expected errors */
3719+
if (dasd_eckd_is_ese(basedev))
3720+
set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
3721+
36243722
return cqr;
36253723
}
36263724

@@ -3940,6 +4038,14 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
39404038
cqr->retries = startdev->default_retries;
39414039
cqr->buildclk = get_tod_clock();
39424040
cqr->status = DASD_CQR_FILLED;
4041+
4042+
/* Set flags to suppress output for expected errors */
4043+
if (dasd_eckd_is_ese(basedev)) {
4044+
set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags);
4045+
set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags);
4046+
set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
4047+
}
4048+
39434049
return cqr;
39444050
out_error:
39454051
dasd_sfree_request(cqr, startdev);
@@ -6061,6 +6167,8 @@ static struct dasd_discipline dasd_eckd_discipline = {
60616167
.ext_pool_cap_at_warnlevel = dasd_eckd_ext_pool_cap_at_warnlevel,
60626168
.ext_pool_warn_thrshld = dasd_eckd_ext_pool_warn_thrshld,
60636169
.ext_pool_oos = dasd_eckd_ext_pool_oos,
6170+
.ese_format = dasd_eckd_ese_format,
6171+
.ese_read = dasd_eckd_ese_read,
60646172
};
60656173

60666174
static int __init

0 commit comments

Comments
 (0)