]> git.itanic.dy.fi Git - linux-stable/commitdiff
s390/dasd: add copy pair swap capability
authorStefan Haberland <sth@linux.ibm.com>
Tue, 20 Sep 2022 19:26:13 +0000 (21:26 +0200)
committerJens Axboe <axboe@kernel.dk>
Wed, 21 Sep 2022 14:32:51 +0000 (08:32 -0600)
In case of errors or misbehaviour of the primary device a controlled
failover to one of the configured secondary devices needs to be
performed.

The swap processing stops I/O on the primary device, all requests are
re-queued to the blocklayer queue, the entries in the copy relation are
swapped and finally the link to the blockdevice is moved from primary to
secondary dasd device.
After this, the secondary becomes the new primary device and I/O is
restarted on that device.

Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Jan Hoeppner <hoeppner@linux.ibm.com>
Link: https://lore.kernel.org/r/20220920192616.808070-5-sth@linux.ibm.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
drivers/s390/block/dasd.c
drivers/s390/block/dasd_devmap.c
drivers/s390/block/dasd_eckd.c
drivers/s390/block/dasd_int.h

index ea82821599f6e1ed9719daf384c01da12889bd93..c03f26e79f450580b84cd52bba2bb058074b1fa4 100644 (file)
@@ -3927,7 +3927,7 @@ EXPORT_SYMBOL_GPL(dasd_generic_space_avail);
 /*
  * clear active requests and requeue them to block layer if possible
  */
-static int dasd_generic_requeue_all_requests(struct dasd_device *device)
+int dasd_generic_requeue_all_requests(struct dasd_device *device)
 {
        struct list_head requeue_queue;
        struct dasd_ccw_req *cqr, *n;
@@ -4001,6 +4001,7 @@ static int dasd_generic_requeue_all_requests(struct dasd_device *device)
        dasd_schedule_device_bh(device);
        return rc;
 }
+EXPORT_SYMBOL_GPL(dasd_generic_requeue_all_requests);
 
 static void do_requeue_requests(struct work_struct *work)
 {
index 28c244aa75cf18dec8643bcd5806754eeece3bb5..ca5c9e9636628767ab5bc0c5ed47f14db0372661 100644 (file)
@@ -937,6 +937,7 @@ void dasd_add_link_to_gendisk(struct gendisk *gdp, struct dasd_device *device)
        gdp->private_data = devmap;
        spin_unlock(&dasd_devmap_lock);
 }
+EXPORT_SYMBOL(dasd_add_link_to_gendisk);
 
 struct dasd_device *dasd_device_from_gendisk(struct gendisk *gdp)
 {
index c8a226f070fa2190187bc2c670ac401a2baf4b1b..d4d3bd33553b01b420be8e0fc7d6ce16cd900a0a 100644 (file)
@@ -6119,6 +6119,99 @@ static int dasd_hosts_print(struct dasd_device *device, struct seq_file *m)
        return 0;
 }
 
+static struct dasd_device
+*copy_relation_find_device(struct dasd_copy_relation *copy,
+                          char *busid)
+{
+       int i;
+
+       for (i = 0; i < DASD_CP_ENTRIES; i++) {
+               if (copy->entry[i].configured &&
+                   strncmp(copy->entry[i].busid, busid, DASD_BUS_ID_SIZE) == 0)
+                       return copy->entry[i].device;
+       }
+       return NULL;
+}
+
+/*
+ * set the new active/primary device
+ */
+static void copy_pair_set_active(struct dasd_copy_relation *copy, char *new_busid,
+                                char *old_busid)
+{
+       int i;
+
+       for (i = 0; i < DASD_CP_ENTRIES; i++) {
+               if (copy->entry[i].configured &&
+                   strncmp(copy->entry[i].busid, new_busid,
+                           DASD_BUS_ID_SIZE) == 0) {
+                       copy->active = &copy->entry[i];
+                       copy->entry[i].primary = true;
+               } else if (copy->entry[i].configured &&
+                          strncmp(copy->entry[i].busid, old_busid,
+                                  DASD_BUS_ID_SIZE) == 0) {
+                       copy->entry[i].primary = false;
+               }
+       }
+}
+
+/*
+ * The function will swap the role of a given copy pair.
+ * During the swap operation the relation of the blockdevice is disconnected
+ * from the old primary and connected to the new.
+ *
+ * IO is paused on the block queue before swap and may be resumed afterwards.
+ */
+static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid,
+                                   char *sec_busid)
+{
+       struct dasd_device *primary, *secondary;
+       struct dasd_copy_relation *copy;
+       struct dasd_block *block;
+       struct gendisk *gdp;
+
+       copy = device->copy;
+       if (!copy)
+               return DASD_COPYPAIRSWAP_INVALID;
+       primary = copy->active->device;
+       if (!primary)
+               return DASD_COPYPAIRSWAP_INVALID;
+       /* double check if swap has correct primary */
+       if (strncmp(dev_name(&primary->cdev->dev), prim_busid, DASD_BUS_ID_SIZE) != 0)
+               return DASD_COPYPAIRSWAP_PRIMARY;
+
+       secondary = copy_relation_find_device(copy, sec_busid);
+       if (!secondary)
+               return DASD_COPYPAIRSWAP_SECONDARY;
+
+       /*
+        * usually the device should be quiesced for swap
+        * for paranoia stop device and requeue requests again
+        */
+       dasd_device_set_stop_bits(primary, DASD_STOPPED_PPRC);
+       dasd_device_set_stop_bits(secondary, DASD_STOPPED_PPRC);
+       dasd_generic_requeue_all_requests(primary);
+
+       /* swap DASD internal device <> block assignment */
+       block = primary->block;
+       primary->block = NULL;
+       secondary->block = block;
+       block->base = secondary;
+       /* set new primary device in COPY relation */
+       copy_pair_set_active(copy, sec_busid, prim_busid);
+
+       /* swap blocklayer device link */
+       gdp = block->gdp;
+       dasd_add_link_to_gendisk(gdp, secondary);
+
+       /* re-enable device */
+       dasd_device_remove_stop_bits(primary, DASD_STOPPED_PPRC);
+       dasd_device_remove_stop_bits(secondary, DASD_STOPPED_PPRC);
+       dasd_schedule_device_bh(secondary);
+
+       return DASD_COPYPAIRSWAP_SUCCESS;
+}
+
 /*
  * Perform Subsystem Function - Peer-to-Peer Remote Copy Extended Query
  */
@@ -6805,6 +6898,7 @@ static struct dasd_discipline dasd_eckd_discipline = {
        .ese_read = dasd_eckd_ese_read,
        .pprc_status = dasd_eckd_query_pprc_status,
        .pprc_enabled = dasd_eckd_pprc_enabled,
+       .copy_pair_swap = dasd_eckd_copy_pair_swap,
 };
 
 static int __init
index d9794ec037224167447c8bcec3fc55da4baa9960..3c55c29155efcf4734860731421305db4f4dfd58 100644 (file)
@@ -438,6 +438,7 @@ struct dasd_discipline {
        int (*ese_read)(struct dasd_ccw_req *, struct irb *);
        int (*pprc_status)(struct dasd_device *, struct dasd_pprc_data_sc4 *);
        bool (*pprc_enabled)(struct dasd_device *);
+       int (*copy_pair_swap)(struct dasd_device *, char *, char *);
 };
 
 extern struct dasd_discipline *dasd_diag_discipline_pointer;
@@ -681,6 +682,7 @@ struct dasd_queue {
 #define DASD_STOPPED_PENDING 4         /* long busy */
 #define DASD_STOPPED_DC_WAIT 8         /* disconnected, wait */
 #define DASD_STOPPED_SU      16        /* summary unit check handling */
+#define DASD_STOPPED_PPRC    32        /* PPRC swap */
 #define DASD_STOPPED_NOSPC   128       /* no space left */
 
 /* per device flags */
@@ -705,6 +707,22 @@ struct dasd_queue {
 
 void dasd_put_device_wake(struct dasd_device *);
 
+/*
+ * return values to be returned from the copy pair swap function
+ * 0x00: swap successful
+ * 0x01: swap data invalid
+ * 0x02: no active device found
+ * 0x03: wrong primary specified
+ * 0x04: secondary device not found
+ * 0x05: swap already running
+ */
+#define DASD_COPYPAIRSWAP_SUCCESS      0
+#define DASD_COPYPAIRSWAP_INVALID      1
+#define DASD_COPYPAIRSWAP_NOACTIVE     2
+#define DASD_COPYPAIRSWAP_PRIMARY      3
+#define DASD_COPYPAIRSWAP_SECONDARY    4
+#define DASD_COPYPAIRSWAP_MULTIPLE     5
+
 /*
  * Reference count inliners
  */
@@ -889,6 +907,8 @@ int dasd_generic_verify_path(struct dasd_device *, __u8);
 void dasd_generic_space_exhaust(struct dasd_device *, struct dasd_ccw_req *);
 void dasd_generic_space_avail(struct dasd_device *);
 
+int dasd_generic_requeue_all_requests(struct dasd_device *);
+
 int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int);
 char *dasd_get_sense(struct irb *);