]> git.itanic.dy.fi Git - linux-stable/commitdiff
s390/dasd: protect device queue against concurrent access
authorJan Höppner <hoeppner@linux.ibm.com>
Wed, 25 Oct 2023 13:24:37 +0000 (15:24 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 8 Dec 2023 07:46:10 +0000 (08:46 +0100)
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>
drivers/s390/block/dasd.c

index 09e932a7b17f41424f69dec932b90ed424f8af8e..81de5c98221a68714e5b7a476d695a015092e95c 100644 (file)
@@ -737,18 +737,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);
 }