Skip to content

Commit 71f3871

Browse files
Stefan Haberlandaxboe
authored andcommitted
s390/dasd: prevent double format of tracks for ESE devices
For ESE devices we get an error for write operations on an unformatted track. Afterwards the track will be formatted and the IO operation restarted. When using alias devices a track might be accessed by multiple requests simultaneously and there is a race window that a track gets formatted twice resulting in data loss. Prevent this by remembering the amount of formatted tracks when starting a request and comparing this number before actually formatting a track on the fly. If the number has changed there is a chance that the current track was finally formatted in between. As a result do not format the track and restart the current IO to check. The number of formatted tracks does not match the overall number of formatted tracks on the device and it might wrap around but this is no problem. It is only needed to recognize that a track has been formatted at all in between. Fixes: 5e2b17e ("s390/dasd: Add dynamic formatting support for ESE volumes") Cc: stable@vger.kernel.org # 5.3+ Signed-off-by: Stefan Haberland <sth@linux.ibm.com> Reviewed-by: Jan Hoeppner <hoeppner@linux.ibm.com> Link: https://lore.kernel.org/r/20220505141733.1989450-3-sth@linux.ibm.com Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 5b53a40 commit 71f3871

3 files changed

Lines changed: 26 additions & 2 deletions

File tree

drivers/s390/block/dasd.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,13 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
14221422
if (!cqr->lpm)
14231423
cqr->lpm = dasd_path_get_opm(device);
14241424
}
1425+
/*
1426+
* remember the amount of formatted tracks to prevent double format on
1427+
* ESE devices
1428+
*/
1429+
if (cqr->block)
1430+
cqr->trkcount = atomic_read(&cqr->block->trkcount);
1431+
14251432
if (cqr->cpmode == 1) {
14261433
rc = ccw_device_tm_start(device->cdev, cqr->cpaddr,
14271434
(long) cqr, cqr->lpm);

drivers/s390/block/dasd_eckd.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3083,13 +3083,24 @@ static int dasd_eckd_format_device(struct dasd_device *base,
30833083
}
30843084

30853085
static bool test_and_set_format_track(struct dasd_format_entry *to_format,
3086-
struct dasd_block *block)
3086+
struct dasd_ccw_req *cqr)
30873087
{
3088+
struct dasd_block *block = cqr->block;
30883089
struct dasd_format_entry *format;
30893090
unsigned long flags;
30903091
bool rc = false;
30913092

30923093
spin_lock_irqsave(&block->format_lock, flags);
3094+
if (cqr->trkcount != atomic_read(&block->trkcount)) {
3095+
/*
3096+
* The number of formatted tracks has changed after request
3097+
* start and we can not tell if the current track was involved.
3098+
* To avoid data corruption treat it as if the current track is
3099+
* involved
3100+
*/
3101+
rc = true;
3102+
goto out;
3103+
}
30933104
list_for_each_entry(format, &block->format_list, list) {
30943105
if (format->track == to_format->track) {
30953106
rc = true;
@@ -3109,6 +3120,7 @@ static void clear_format_track(struct dasd_format_entry *format,
31093120
unsigned long flags;
31103121

31113122
spin_lock_irqsave(&block->format_lock, flags);
3123+
atomic_inc(&block->trkcount);
31123124
list_del_init(&format->list);
31133125
spin_unlock_irqrestore(&block->format_lock, flags);
31143126
}
@@ -3170,8 +3182,11 @@ dasd_eckd_ese_format(struct dasd_device *startdev, struct dasd_ccw_req *cqr,
31703182
}
31713183
format->track = curr_trk;
31723184
/* test if track is already in formatting by another thread */
3173-
if (test_and_set_format_track(format, block))
3185+
if (test_and_set_format_track(format, cqr)) {
3186+
/* this is no real error so do not count down retries */
3187+
cqr->retries++;
31743188
return ERR_PTR(-EEXIST);
3189+
}
31753190

31763191
fdata.start_unit = curr_trk;
31773192
fdata.stop_unit = curr_trk;

drivers/s390/block/dasd_int.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ struct dasd_ccw_req {
187187
void (*callback)(struct dasd_ccw_req *, void *data);
188188
void *callback_data;
189189
unsigned int proc_bytes; /* bytes for partial completion */
190+
unsigned int trkcount; /* count formatted tracks */
190191
};
191192

192193
/*
@@ -610,6 +611,7 @@ struct dasd_block {
610611

611612
struct list_head format_list;
612613
spinlock_t format_lock;
614+
atomic_t trkcount;
613615
};
614616

615617
struct dasd_attention_data {

0 commit comments

Comments
 (0)