--- /dev/null
+From e836007089ba8fdf24e636ef2b007651fb4582e6 Mon Sep 17 00:00:00 2001
+From: Jason Baron <jbaron@akamai.com>
+Date: Fri, 23 Jun 2023 14:05:23 -0400
+Subject: md/raid0: add discard support for the 'original' layout
+
+From: Jason Baron <jbaron@akamai.com>
+
+commit e836007089ba8fdf24e636ef2b007651fb4582e6 upstream.
+
+We've found that using raid0 with the 'original' layout and discard
+enabled with different disk sizes (such that at least two zones are
+created) can result in data corruption. This is due to the fact that
+the discard handling in 'raid0_handle_discard()' assumes the 'alternate'
+layout. We've seen this corruption using ext4 but other filesystems are
+likely susceptible as well.
+
+More specifically, while multiple zones are necessary to create the
+corruption, the corruption may not occur with multiple zones if they
+layout in such a way the layout matches what the 'alternate' layout
+would have produced. Thus, not all raid0 devices with the 'original'
+layout, different size disks and discard enabled will encounter this
+corruption.
+
+The 3.14 kernel inadvertently changed the raid0 disk layout for different
+size disks. Thus, running a pre-3.14 kernel and post-3.14 kernel on the
+same raid0 array could corrupt data. This lead to the creation of the
+'original' layout (to match the pre-3.14 layout) and the 'alternate' layout
+(to match the post 3.14 layout) in the 5.4 kernel time frame and an option
+to tell the kernel which layout to use (since it couldn't be autodetected).
+However, when the 'original' layout was added back to 5.4 discard support
+for the 'original' layout was not added leading this issue.
+
+I've been able to reliably reproduce the corruption with the following
+test case:
+
+1. create raid0 array with different size disks using original layout
+2. mkfs
+3. mount -o discard
+4. create lots of files
+5. remove 1/2 the files
+6. fstrim -a (or just the mount point for the raid0 array)
+7. umount
+8. fsck -fn /dev/md0 (spews all sorts of corruptions)
+
+Let's fix this by adding proper discard support to the 'original' layout.
+The fix 'maps' the 'original' layout disks to the order in which they are
+read/written such that we can compare the disks in the same way that the
+current 'alternate' layout does. A 'disk_shift' field is added to
+'struct strip_zone'. This could be computed on the fly in
+raid0_handle_discard() but by adding this field, we save some computation
+in the discard path.
+
+Note we could also potentially fix this by re-ordering the disks in the
+zones that follow the first one, and then always read/writing them using
+the 'alternate' layout. However, that is seen as a more substantial change,
+and we are attempting the least invasive fix at this time to remedy the
+corruption.
+
+I've verified the change using the reproducer mentioned above. Typically,
+the corruption is seen after less than 3 iterations, while the patch has
+run 500+ iterations.
+
+Cc: NeilBrown <neilb@suse.de>
+Cc: Song Liu <song@kernel.org>
+Fixes: c84a1372df92 ("md/raid0: avoid RAID0 data corruption due to layout confusion.")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jason Baron <jbaron@akamai.com>
+Signed-off-by: Song Liu <song@kernel.org>
+Link: https://lore.kernel.org/r/20230623180523.1901230-1-jbaron@akamai.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/md/raid0.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++-------
+ drivers/md/raid0.h | 1
+ 2 files changed, 55 insertions(+), 8 deletions(-)
+
+--- a/drivers/md/raid0.c
++++ b/drivers/md/raid0.c
+@@ -270,6 +270,18 @@ static int create_strip_zones(struct mdd
+ goto abort;
+ }
+
++ if (conf->layout == RAID0_ORIG_LAYOUT) {
++ for (i = 1; i < conf->nr_strip_zones; i++) {
++ sector_t first_sector = conf->strip_zone[i-1].zone_end;
++
++ sector_div(first_sector, mddev->chunk_sectors);
++ zone = conf->strip_zone + i;
++ /* disk_shift is first disk index used in the zone */
++ zone->disk_shift = sector_div(first_sector,
++ zone->nb_dev);
++ }
++ }
++
+ pr_debug("md/raid0:%s: done.\n", mdname(mddev));
+ *private_conf = conf;
+
+@@ -431,6 +443,20 @@ exit_acct_set:
+ return ret;
+ }
+
++/*
++ * Convert disk_index to the disk order in which it is read/written.
++ * For example, if we have 4 disks, they are numbered 0,1,2,3. If we
++ * write the disks starting at disk 3, then the read/write order would
++ * be disk 3, then 0, then 1, and then disk 2 and we want map_disk_shift()
++ * to map the disks as follows 0,1,2,3 => 1,2,3,0. So disk 0 would map
++ * to 1, 1 to 2, 2 to 3, and 3 to 0. That way we can compare disks in
++ * that 'output' space to understand the read/write disk ordering.
++ */
++static int map_disk_shift(int disk_index, int num_disks, int disk_shift)
++{
++ return ((disk_index + num_disks - disk_shift) % num_disks);
++}
++
+ static void raid0_handle_discard(struct mddev *mddev, struct bio *bio)
+ {
+ struct r0conf *conf = mddev->private;
+@@ -444,7 +470,9 @@ static void raid0_handle_discard(struct
+ sector_t end_disk_offset;
+ unsigned int end_disk_index;
+ unsigned int disk;
++ sector_t orig_start, orig_end;
+
++ orig_start = start;
+ zone = find_zone(conf, &start);
+
+ if (bio_end_sector(bio) > zone->zone_end) {
+@@ -458,6 +486,7 @@ static void raid0_handle_discard(struct
+ } else
+ end = bio_end_sector(bio);
+
++ orig_end = end;
+ if (zone != conf->strip_zone)
+ end = end - zone[-1].zone_end;
+
+@@ -469,13 +498,26 @@ static void raid0_handle_discard(struct
+ last_stripe_index = end;
+ sector_div(last_stripe_index, stripe_size);
+
+- start_disk_index = (int)(start - first_stripe_index * stripe_size) /
+- mddev->chunk_sectors;
++ /* In the first zone the original and alternate layouts are the same */
++ if ((conf->layout == RAID0_ORIG_LAYOUT) && (zone != conf->strip_zone)) {
++ sector_div(orig_start, mddev->chunk_sectors);
++ start_disk_index = sector_div(orig_start, zone->nb_dev);
++ start_disk_index = map_disk_shift(start_disk_index,
++ zone->nb_dev,
++ zone->disk_shift);
++ sector_div(orig_end, mddev->chunk_sectors);
++ end_disk_index = sector_div(orig_end, zone->nb_dev);
++ end_disk_index = map_disk_shift(end_disk_index,
++ zone->nb_dev, zone->disk_shift);
++ } else {
++ start_disk_index = (int)(start - first_stripe_index * stripe_size) /
++ mddev->chunk_sectors;
++ end_disk_index = (int)(end - last_stripe_index * stripe_size) /
++ mddev->chunk_sectors;
++ }
+ start_disk_offset = ((int)(start - first_stripe_index * stripe_size) %
+ mddev->chunk_sectors) +
+ first_stripe_index * mddev->chunk_sectors;
+- end_disk_index = (int)(end - last_stripe_index * stripe_size) /
+- mddev->chunk_sectors;
+ end_disk_offset = ((int)(end - last_stripe_index * stripe_size) %
+ mddev->chunk_sectors) +
+ last_stripe_index * mddev->chunk_sectors;
+@@ -483,18 +525,22 @@ static void raid0_handle_discard(struct
+ for (disk = 0; disk < zone->nb_dev; disk++) {
+ sector_t dev_start, dev_end;
+ struct md_rdev *rdev;
++ int compare_disk;
++
++ compare_disk = map_disk_shift(disk, zone->nb_dev,
++ zone->disk_shift);
+
+- if (disk < start_disk_index)
++ if (compare_disk < start_disk_index)
+ dev_start = (first_stripe_index + 1) *
+ mddev->chunk_sectors;
+- else if (disk > start_disk_index)
++ else if (compare_disk > start_disk_index)
+ dev_start = first_stripe_index * mddev->chunk_sectors;
+ else
+ dev_start = start_disk_offset;
+
+- if (disk < end_disk_index)
++ if (compare_disk < end_disk_index)
+ dev_end = (last_stripe_index + 1) * mddev->chunk_sectors;
+- else if (disk > end_disk_index)
++ else if (compare_disk > end_disk_index)
+ dev_end = last_stripe_index * mddev->chunk_sectors;
+ else
+ dev_end = end_disk_offset;
+--- a/drivers/md/raid0.h
++++ b/drivers/md/raid0.h
+@@ -6,6 +6,7 @@ struct strip_zone {
+ sector_t zone_end; /* Start of the next zone (in sectors) */
+ sector_t dev_start; /* Zone offset in real dev (in sectors) */
+ int nb_dev; /* # of devices attached to the zone */
++ int disk_shift; /* start disk for the original layout */
+ };
+
+ /* Linux 3.14 (20d0189b101) made an unintended change to
--- /dev/null
+From f61b7634a3249d12b9daa36ffbdb9965b6f24c6c Mon Sep 17 00:00:00 2001
+From: Damien Le Moal <dlemoal@kernel.org>
+Date: Sat, 15 Apr 2023 11:35:39 +0900
+Subject: misc: pci_endpoint_test: Free IRQs before removing the device
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+commit f61b7634a3249d12b9daa36ffbdb9965b6f24c6c upstream.
+
+In pci_endpoint_test_remove(), freeing the IRQs after removing the device
+creates a small race window for IRQs to be received with the test device
+memory already released, causing the IRQ handler to access invalid memory,
+resulting in an oops.
+
+Free the device IRQs before removing the device to avoid this issue.
+
+Link: https://lore.kernel.org/r/20230415023542.77601-15-dlemoal@kernel.org
+Fixes: e03327122e2c ("pci_endpoint_test: Add 2 ioctl commands")
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/misc/pci_endpoint_test.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/misc/pci_endpoint_test.c
++++ b/drivers/misc/pci_endpoint_test.c
+@@ -938,6 +938,9 @@ static void pci_endpoint_test_remove(str
+ if (id < 0)
+ return;
+
++ pci_endpoint_test_release_irq(test);
++ pci_endpoint_test_free_irq_vectors(test);
++
+ misc_deregister(&test->miscdev);
+ kfree(misc_device->name);
+ kfree(test->name);
+@@ -947,9 +950,6 @@ static void pci_endpoint_test_remove(str
+ pci_iounmap(pdev, test->bar[bar]);
+ }
+
+- pci_endpoint_test_release_irq(test);
+- pci_endpoint_test_free_irq_vectors(test);
+-
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ }
--- /dev/null
+From fb620ae73b70c2f57b9d3e911fc24c024ba2324f Mon Sep 17 00:00:00 2001
+From: Damien Le Moal <dlemoal@kernel.org>
+Date: Sat, 15 Apr 2023 11:35:40 +0900
+Subject: misc: pci_endpoint_test: Re-init completion for every test
+
+From: Damien Le Moal <dlemoal@kernel.org>
+
+commit fb620ae73b70c2f57b9d3e911fc24c024ba2324f upstream.
+
+The irq_raised completion used to detect the end of a test case is
+initialized when the test device is probed, but never reinitialized again
+before a test case. As a result, the irq_raised completion synchronization
+is effective only for the first ioctl test case executed. Any subsequent
+call to wait_for_completion() by another ioctl() call will immediately
+return, potentially too early, leading to false positive failures.
+
+Fix this by reinitializing the irq_raised completion before starting a new
+ioctl() test command.
+
+Link: https://lore.kernel.org/r/20230415023542.77601-16-dlemoal@kernel.org
+Fixes: 2c156ac71c6b ("misc: Add host side PCI driver for PCI test function device")
+Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/misc/pci_endpoint_test.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/misc/pci_endpoint_test.c
++++ b/drivers/misc/pci_endpoint_test.c
+@@ -729,6 +729,10 @@ static long pci_endpoint_test_ioctl(stru
+ struct pci_dev *pdev = test->pdev;
+
+ mutex_lock(&test->mutex);
++
++ reinit_completion(&test->irq_raised);
++ test->last_irq = -ENODATA;
++
+ switch (cmd) {
+ case PCITEST_BAR:
+ bar = arg;