diff options
| author | Jan Höppner <hoeppner@linux.ibm.com> | 2023-10-25 15:24:37 +0200 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2023-12-08 08:43:24 +0100 |
| commit | f75617cc8df4155374132f0b500b0b3ebb967458 (patch) | |
| tree | 846b5e8ec0e47de6b2de9a592946bee1aaa66d2a | |
| parent | 3f47ef1b1c14cb304dd0713cceb7be166dd09197 (diff) | |
| download | linux-f75617cc8df4155374132f0b500b0b3ebb967458.tar.gz linux-f75617cc8df4155374132f0b500b0b3ebb967458.tar.bz2 linux-f75617cc8df4155374132f0b500b0b3ebb967458.zip | |
s390/dasd: protect device queue against concurrent access
commit db46cd1e0426f52999d50fa72cfa97fa39952885 upstream.
In dasd_profile_start() the amount of requests on the device queue are
counted. The access to the device queue is unprotected against
concurrent access. With a lot of parallel I/O, especially with alias
devices enabled, the device queue can change while dasd_profile_start()
is accessing the queue. In the worst case this leads to a kernel panic
due to incorrect pointer accesses.
Fix this by taking the device lock before accessing the queue and
counting the requests. Additionally the check for a valid profile data
pointer can be done earlier to avoid unnecessary locking in a hot path.
Cc: <stable@vger.kernel.org>
Fixes: 4fa52aa7a82f ("[S390] dasd: add enhanced DASD statistics interface")
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
Link: https://lore.kernel.org/r/20231025132437.1223363-3-sth@linux.ibm.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/s390/block/dasd.c | 24 |
1 files changed, 13 insertions, 11 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index c0eee7b00442..1161bbd7eb78 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -725,18 +725,20 @@ static void dasd_profile_start(struct dasd_block *block, * we count each request only once. */ device = cqr->startdev; - if (device->profile.data) { - counter = 1; /* request is not yet queued on the start device */ - list_for_each(l, &device->ccw_queue) - if (++counter >= 31) - break; - } + if (!device->profile.data) + return; + + spin_lock(get_ccwdev_lock(device->cdev)); + counter = 1; /* request is not yet queued on the start device */ + list_for_each(l, &device->ccw_queue) + if (++counter >= 31) + break; + spin_unlock(get_ccwdev_lock(device->cdev)); + spin_lock(&device->profile.lock); - if (device->profile.data) { - device->profile.data->dasd_io_nr_req[counter]++; - if (rq_data_dir(req) == READ) - device->profile.data->dasd_read_nr_req[counter]++; - } + device->profile.data->dasd_io_nr_req[counter]++; + if (rq_data_dir(req) == READ) + device->profile.data->dasd_read_nr_req[counter]++; spin_unlock(&device->profile.lock); } |
