[kpartx] add DASD large volume support
authorStefan Weinhuber <wein@de.ibm.com>
Tue, 7 Jul 2009 21:51:29 +0000 (23:51 +0200)
committerChristophe Varoqui <christophe.varoqui@free.fr>
Tue, 7 Jul 2009 21:51:29 +0000 (23:51 +0200)
With kernel 2.6.30 the DASD device driver supports devices with more
then 65520 cylinders. As the traditional record layouts and hardware
interfaces only allow for 16-bit cylinder values, the new larger
cylinder addresses have to be partially encoded into the head part
of a cylinder/head address. To make things complicated, old kernels
will recognize a large volume device, but with a maximum of 65535
cylinders, so a large volume that has been formatted with old tools
will only be partially formatted. To handle these issues our disk
layouts and partition detection code had to be extended, and to use
large volumes with the multipath tools, the DASD partition detection
code in kpartx needs to be extended as well.

compatible disk layout (VOL1 label):
We use the same address encoding as the hardware interfaces.
To prevent old tools and kernels from misinterpreting the encoded
partition sizes, the new VTOC entries have the format number 8
instead of 1.

linux disk layout (LNX1 label):
Here we will still create one partition for the whole disk.
To make sure that the whole disk has been formatted, large volumes
use a new version of the disk label, which contains the number of
formatted blocks. If the disk contains an old volume label, we know
it was formatted with the number of cylinders as reported by the
HDIO_GETGEO ioctl.

CMS disk layout (CMS1 label):
Already contains the number of formatted blocks in the label, we
just have to use it.

Signed-off-by: Stefan Weinhuber <wein@de.ibm.com>
kpartx/dasd.c
kpartx/dasd.h

index f31111f..dcdf678 100644 (file)
@@ -6,6 +6,7 @@
  * Mostly taken from drivers/s390/block/dasd.c
  *
  * Copyright (c) 2005, Hannes Reinecke, SUSE Linux Products GmbH
+ * Copyright IBM Corporation, 2009
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -40,7 +41,7 @@
 #include "byteorder.h"
 #include "dasd.h"
 
-unsigned long sectors512(unsigned long sectors, int blocksize)
+unsigned long long sectors512(unsigned long long sectors, int blocksize)
 {
        return sectors * (blocksize >> 9);
 }
@@ -52,16 +53,14 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
 {
        int retval = -1;
        int blocksize;
-       long disksize;
-       unsigned long offset, size;
+       uint64_t disksize;
+       uint64_t offset, size, fmt_size;
        dasd_information_t info;
        struct hd_geometry geo;
        char type[5] = {0,};
-       char name[7] = {0,};
-       unsigned char *label_raw;
        volume_label_t vlabel;
        unsigned char *data = NULL;
-       unsigned int blk;
+       uint64_t blk;
        int fd_dasd = -1;
        struct stat sbuf;
        dev_t dev;
@@ -129,9 +128,10 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
        if (ioctl(fd_dasd, HDIO_GETGEO, (unsigned long)&geo) != 0) {
                goto out;
        }
-       
-       if (ioctl(fd_dasd, BLKGETSIZE, &disksize) != 0)
+
+       if (ioctl(fd_dasd, BLKGETSIZE64, &disksize) != 0)
                goto out;
+       disksize >>= 9;
 
        if (ioctl(fd_dasd, BLKSSZGET, &blocksize) != 0)
                goto out;
@@ -153,17 +153,14 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
                perror("read");
                goto out;
        }
-       vtoc_ebcdic_dec(data, type, 4);
 
        if ((!info.FBA_layout) && (!strcmp(info.type, "ECKD")))
-               label_raw = &data[8];
-       else
-               label_raw = &data[4];
-
-       name[6] = '\0';
-       vtoc_ebcdic_dec(label_raw, name, 6);
-
-       memcpy (&vlabel, data, sizeof(volume_label_t));
+               memcpy (&vlabel, data, sizeof(vlabel));
+       else {
+               bzero(&vlabel,4);
+               memcpy (&vlabel.vollbl, data, sizeof(vlabel) - 4);
+       }
+       vtoc_ebcdic_dec(vlabel.vollbl, type, 4);
 
        /*
         * Three different types: CMS1, VOL1 and LNX1/unlabeled
@@ -172,16 +169,16 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
                /*
                 * VM style CMS1 labeled disk
                 */
-               int *label = (int *) &vlabel;
+               unsigned int *label = (unsigned int *) &vlabel;
 
-               if (label[13] != 0) {
+               blocksize = label[4];
+               if (label[14] != 0) {
                        /* disk is reserved minidisk */
-                       blocksize = label[3];
-                       offset = label[13];
-                       size   = sectors512(label[7] - 1, blocksize);
+                       offset = label[14];
+                       size   = sectors512(label[8] - 1, blocksize);
                } else {
                        offset = info.label_block + 1;
-                       size   = disksize;
+                       size   = sectors512(label[8], blocksize);
                }
                sp[0].start = sectors512(offset, blocksize);
                sp[0].size  = size - sp[0].start;
@@ -207,18 +204,20 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
                        /* skip FMT4 / FMT5 / FMT7 labels */
                        if (EBCtoASC[f1.DS1FMTID] == '4'
                            || EBCtoASC[f1.DS1FMTID] == '5'
-                           || EBCtoASC[f1.DS1FMTID] == '7') {
+                           || EBCtoASC[f1.DS1FMTID] == '7'
+                           || EBCtoASC[f1.DS1FMTID] == '9') {
                                blk++;
                                continue;
                        }
 
-                       /* only FMT1 valid at this point */
-                       if (EBCtoASC[f1.DS1FMTID] != '1')
+                       /* only FMT1 and FMT8 valid at this point */
+                       if (EBCtoASC[f1.DS1FMTID] != '1' &&
+                           EBCtoASC[f1.DS1FMTID] != '8')
                                break;
 
                        /* OK, we got valid partition data */
                        offset = cchh2blk(&f1.DS1EXT1.llimit, &geo);
-                       size  = cchh2blk(&f1.DS1EXT1.ulimit, &geo) - 
+                       size  = cchh2blk(&f1.DS1EXT1.ulimit, &geo) -
                                offset + geo.sectors;
                        sp[counter].start = sectors512(offset, blocksize);
                        sp[counter].size  = sectors512(size, blocksize);
@@ -230,8 +229,27 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
                /*
                 * Old style LNX1 or unlabeled disk
                 */
+               if (strncmp(type, "LNX1", 4) == 0) {
+                       if (vlabel.ldl_version == 0xf2) {
+                               fmt_size = sectors512(vlabel.formatted_blocks,
+                                                     blocksize);
+                       } else if (!strcmp(info.type, "ECKD")) {
+                               /* formated w/o large volume support */
+                               fmt_size = geo.cylinders * geo.heads
+                                       * geo.sectors * (blocksize >> 9);
+                       } else {
+                               /* old label and no usable disk geometry
+                                * (e.g. DIAG) */
+                               fmt_size = disksize;
+                       }
+                       size = disksize;
+                       if (fmt_size < size)
+                               size = fmt_size;
+               } else
+                       size = disksize;
+
                sp[0].start = sectors512(info.label_block + 1, blocksize);
-               sp[0].size  = disksize - sp[0].start;
+               sp[0].size  = size - sp[0].start;
                retval = 1;
        }
 
index 4a5ba86..0ed7c80 100644 (file)
@@ -6,6 +6,7 @@
  * Mostly taken from drivers/s390/block/dasd.c
  *
  * Copyright (c) 2005, Hannes Reinecke, SUSE Linux Products GmbH
+ * Copyright IBM Corporation, 2009
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -66,7 +67,9 @@ typedef struct volume_label
        char labperci[4];       /* no of labels per CI (FBA), blanks for CKD */
        char res2[4];           /* reserved                                  */
        char lvtoc[14];         /* owner code for LVTOC                      */
-       char res3[29];          /* reserved                                  */
+       char res3[28];          /* reserved                                  */
+       char ldl_version;       /* version number, valid for ldl format      */
+       uint64_t formatted_blocks; /* valid when ldl_version >= f2           */
 } __attribute__ ((packed)) volume_label_t;
 
 
@@ -160,6 +163,7 @@ typedef struct dasd_information_t {
 #define BIODASDINFO _IOR(DASD_IOCTL_LETTER,1,dasd_information_t)
 #define BLKGETSIZE _IO(0x12,96)
 #define BLKSSZGET _IO(0x12,104)
+#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* device size in bytes (u64 *arg)*/
 
 /*
  * Only compile this on S/390. Doesn't make any sense
@@ -239,7 +243,7 @@ static unsigned char EBCtoASC[256] =
 };
 
 static inline void 
-vtoc_ebcdic_dec (const unsigned char *source, char *target, int l) 
+vtoc_ebcdic_dec (const char *source, char *target, int l)
 {
        int i;
 
@@ -248,24 +252,41 @@ vtoc_ebcdic_dec (const unsigned char *source, char *target, int l)
 }
 
 /*
- * compute the block number from a 
+ * compute the block number from a
  * cyl-cyl-head-head structure
  */
-static inline int
-cchh2blk (cchh_t *ptr, struct hd_geometry *geo) {
-        return ptr->cc * geo->heads * geo->sectors +
-              ptr->hh * geo->sectors;
-}
+static inline uint64_t
+cchh2blk (cchh_t *ptr, struct hd_geometry *geo)
+{
+       uint64_t cyl;
+       uint16_t head;
 
+       /*decode cylinder and heads for large volumes */
+       cyl = ptr->hh & 0xFFF0;
+       cyl <<= 12;
+       cyl |= ptr->cc;
+       head = ptr->hh & 0x000F;
+       return cyl * geo->heads * geo->sectors +
+              head * geo->sectors;
+}
 
 /*
- * compute the block number from a 
+ * compute the block number from a
  * cyl-cyl-head-head-block structure
  */
-static inline int
-cchhb2blk (cchhb_t *ptr, struct hd_geometry *geo) {
-        return ptr->cc * geo->heads * geo->sectors +
-               ptr->hh * geo->sectors +
+static inline uint64_t
+cchhb2blk (cchhb_t *ptr, struct hd_geometry *geo)
+{
+       uint64_t cyl;
+       uint16_t head;
+
+       /*decode cylinder and heads for large volumes */
+       cyl = ptr->hh & 0xFFF0;
+       cyl <<= 12;
+       cyl |= ptr->cc;
+       head = ptr->hh & 0x000F;
+       return  cyl * geo->heads * geo->sectors +
+               head * geo->sectors +
                ptr->b;
 }