s390/dasd: check count address during online setting
authorStefan Haberland <stefan.haberland@de.ibm.com>
Thu, 20 Sep 2012 16:37:36 +0000 (18:37 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 26 Sep 2012 13:45:26 +0000 (15:45 +0200)
A common way to prepare a z/VM mini disk is to format the real device
with a z/VM tool like CPFMTXA and then define a mini disk that excludes
the first cylinder, i.e. the cylinder 0 of the virtual disk is located
at cylinder 1 of the real device.

The DASD device driver will recognize such a mini disk as formatted, as
the uniform record layout on the disk matches that of an LDL formatted
device. However, the cylinder value in the 'count' field of the ECKD
records matches the geometry of the real device, and not that of the
mini disk, so I/O requests will fail with 'record not found' errors.

To make the mini disk usable, it needs to be formatted with a tool like
dasdfmt. To enable tools like distribution installation tools to
recognize this situation, the DASD device driver should report such a
mini disk as 'not formatted'.
To this end we need to extend the device recognition code to check not
just for proper record sizes, but also for proper cylinder/head/record
values.

Signed-off-by: Stefan Haberland <stefan.haberland@de.ibm.com>
Reviewed-by: Stefan Weinhuber <wein@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/block/dasd_eckd.c

index e1bb930..108332b 100644 (file)
@@ -139,6 +139,10 @@ dasd_eckd_set_online(struct ccw_device *cdev)
 static const int sizes_trk0[] = { 28, 148, 84 };
 #define LABEL_SIZE 140
 
+/* head and record addresses of count_area read in analysis ccw */
+static const int count_area_head[] = { 0, 0, 0, 0, 2 };
+static const int count_area_rec[] = { 1, 2, 3, 4, 1 };
+
 static inline unsigned int
 round_up_multiple(unsigned int no, unsigned int mult)
 {
@@ -1939,7 +1943,10 @@ static int dasd_eckd_end_analysis(struct dasd_block *block)
        count_area = NULL;
        for (i = 0; i < 3; i++) {
                if (private->count_area[i].kl != 4 ||
-                   private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4) {
+                   private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4 ||
+                   private->count_area[i].cyl != 0 ||
+                   private->count_area[i].head != count_area_head[i] ||
+                   private->count_area[i].record != count_area_rec[i]) {
                        private->uses_cdl = 0;
                        break;
                }
@@ -1951,7 +1958,10 @@ static int dasd_eckd_end_analysis(struct dasd_block *block)
                for (i = 0; i < 5; i++) {
                        if ((private->count_area[i].kl != 0) ||
                            (private->count_area[i].dl !=
-                            private->count_area[0].dl))
+                            private->count_area[0].dl) ||
+                           private->count_area[i].cyl !=  0 ||
+                           private->count_area[i].head != count_area_head[i] ||
+                           private->count_area[i].record != count_area_rec[i])
                                break;
                }
                if (i == 5)