summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Weinhuber <wein@de.ibm.com>2009-07-07 23:51:29 +0200
committerChristophe Varoqui <christophe.varoqui@free.fr>2009-07-07 23:51:29 +0200
commitd678c139719d5631194b50e49f16ca97162ecd0f (patch)
tree56d8024cf254660e698b84a0e4ef23525d5717ae
parent5032e81e991b4cb4829b878066790faa36d888e0 (diff)
downloadmultipath-tools-d678c139719d5631194b50e49f16ca97162ecd0f.tar.gz
multipath-tools-d678c139719d5631194b50e49f16ca97162ecd0f.tar.bz2
multipath-tools-d678c139719d5631194b50e49f16ca97162ecd0f.zip
[kpartx] add DASD large volume support
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>
-rw-r--r--kpartx/dasd.c74
-rw-r--r--kpartx/dasd.h47
2 files changed, 80 insertions, 41 deletions
diff --git a/kpartx/dasd.c b/kpartx/dasd.c
index f31111f..dcdf678 100644
--- a/kpartx/dasd.c
+++ b/kpartx/dasd.c
@@ -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;
}
diff --git a/kpartx/dasd.h b/kpartx/dasd.h
index 4a5ba86..0ed7c80 100644
--- a/kpartx/dasd.h
+++ b/kpartx/dasd.h
@@ -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;
}