aboutsummaryrefslogtreecommitdiffstats
path: root/block
diff options
context:
space:
mode:
authorStefan Weinhuber <wein@de.ibm.com>2012-08-31 04:52:13 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2012-09-26 09:45:05 -0400
commit46e8894786327caf52cf686e27ba0795bddfcd63 (patch)
tree3f5ccb1fbad4e29915ed028063eed7ed6bbe30ba /block
parent3d5787c9976889bcf26cbd8f3124b91678a33275 (diff)
s390/partitions: make partition detection independent from DASD ioctls
In some usage scenarios it is desireable to work with disk images or virtualized DASD devices. One problem that prevents such applications is the partition detection in ibm.c. Currently it works only for devices that support the BIODASDINFO2 ioctl, in other words, it only works for devices that belong to the DASD device driver. The information gained from the BIODASDINFO2 ioctl is only for a small set of legacy cases abolutely necessary. All current VOL1, LNX1 and CMS1 type of disk labels can be interpreted correctly without this information, as long as the generic HDIO_GETGEO ioctl works and provides a correct disk geometry. This patch makes the ibm.c partition detection as independent as possible from the BIODASDINFO2 ioctl. Only the following two cases are still restricted to real DASDs: - An FBA DASD, or LDL formatted ECKD DASD without any disk label. - An old style LNX1 label (without large volume support) on a disk with inconsistent device geometry. Signed-off-by: Stefan Weinhuber <wein@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'block')
-rw-r--r--block/partitions/ibm.c455
1 files changed, 272 insertions, 183 deletions
diff --git a/block/partitions/ibm.c b/block/partitions/ibm.c
index 1104acac780b..47a61474e795 100644
--- a/block/partitions/ibm.c
+++ b/block/partitions/ibm.c
@@ -1,9 +1,8 @@
1/* 1/*
2 * File...........: linux/fs/partitions/ibm.c
3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 2 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
4 * Volker Sameske <sameske@de.ibm.com> 3 * Volker Sameske <sameske@de.ibm.com>
5 * Bugreports.to..: <Linux390@de.ibm.com> 4 * Bugreports.to..: <Linux390@de.ibm.com>
6 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 5 * Copyright IBM Corp. 1999, 2012
7 */ 6 */
8 7
9#include <linux/buffer_head.h> 8#include <linux/buffer_head.h>
@@ -17,17 +16,23 @@
17#include "check.h" 16#include "check.h"
18#include "ibm.h" 17#include "ibm.h"
19 18
19
20union label_t {
21 struct vtoc_volume_label_cdl vol;
22 struct vtoc_volume_label_ldl lnx;
23 struct vtoc_cms_label cms;
24};
25
20/* 26/*
21 * compute the block number from a 27 * compute the block number from a
22 * cyl-cyl-head-head structure 28 * cyl-cyl-head-head structure
23 */ 29 */
24static sector_t 30static sector_t cchh2blk(struct vtoc_cchh *ptr, struct hd_geometry *geo)
25cchh2blk (struct vtoc_cchh *ptr, struct hd_geometry *geo) { 31{
26
27 sector_t cyl; 32 sector_t cyl;
28 __u16 head; 33 __u16 head;
29 34
30 /*decode cylinder and heads for large volumes */ 35 /* decode cylinder and heads for large volumes */
31 cyl = ptr->hh & 0xFFF0; 36 cyl = ptr->hh & 0xFFF0;
32 cyl <<= 12; 37 cyl <<= 12;
33 cyl |= ptr->cc; 38 cyl |= ptr->cc;
@@ -40,13 +45,12 @@ cchh2blk (struct vtoc_cchh *ptr, struct hd_geometry *geo) {
40 * compute the block number from a 45 * compute the block number from a
41 * cyl-cyl-head-head-block structure 46 * cyl-cyl-head-head-block structure
42 */ 47 */
43static sector_t 48static sector_t cchhb2blk(struct vtoc_cchhb *ptr, struct hd_geometry *geo)
44cchhb2blk (struct vtoc_cchhb *ptr, struct hd_geometry *geo) { 49{
45
46 sector_t cyl; 50 sector_t cyl;
47 __u16 head; 51 __u16 head;
48 52
49 /*decode cylinder and heads for large volumes */ 53 /* decode cylinder and heads for large volumes */
50 cyl = ptr->hh & 0xFFF0; 54 cyl = ptr->hh & 0xFFF0;
51 cyl <<= 12; 55 cyl <<= 12;
52 cyl |= ptr->cc; 56 cyl |= ptr->cc;
@@ -56,26 +60,243 @@ cchhb2blk (struct vtoc_cchhb *ptr, struct hd_geometry *geo) {
56 ptr->b; 60 ptr->b;
57} 61}
58 62
63static int find_label(struct parsed_partitions *state,
64 dasd_information2_t *info,
65 struct hd_geometry *geo,
66 int blocksize,
67 sector_t *labelsect,
68 char name[],
69 char type[],
70 union label_t *label)
71{
72 Sector sect;
73 unsigned char *data;
74 sector_t testsect[3];
75 unsigned char temp[5];
76 int found = 0;
77 int i, testcount;
78
79 /* There a three places where we may find a valid label:
80 * - on an ECKD disk it's block 2
81 * - on an FBA disk it's block 1
82 * - on an CMS formatted FBA disk it is sector 1, even if the block size
83 * is larger than 512 bytes (possible if the DIAG discipline is used)
84 * If we have a valid info structure, then we know exactly which case we
85 * have, otherwise we just search through all possebilities.
86 */
87 if (info) {
88 if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) ||
89 (info->cu_type == 0x3880 && info->dev_type == 0x3370))
90 testsect[0] = info->label_block;
91 else
92 testsect[0] = info->label_block * (blocksize >> 9);
93 testcount = 1;
94 } else {
95 testsect[0] = 1;
96 testsect[1] = (blocksize >> 9);
97 testsect[2] = 2 * (blocksize >> 9);
98 testcount = 3;
99 }
100 for (i = 0; i < testcount; ++i) {
101 data = read_part_sector(state, testsect[i], &sect);
102 if (data == NULL)
103 continue;
104 memcpy(label, data, sizeof(*label));
105 memcpy(temp, data, 4);
106 temp[4] = 0;
107 EBCASC(temp, 4);
108 put_dev_sector(sect);
109 if (!strcmp(temp, "VOL1") ||
110 !strcmp(temp, "LNX1") ||
111 !strcmp(temp, "CMS1")) {
112 if (!strcmp(temp, "VOL1")) {
113 strncpy(type, label->vol.vollbl, 4);
114 strncpy(name, label->vol.volid, 6);
115 } else {
116 strncpy(type, label->lnx.vollbl, 4);
117 strncpy(name, label->lnx.volid, 6);
118 }
119 EBCASC(type, 4);
120 EBCASC(name, 6);
121 *labelsect = testsect[i];
122 found = 1;
123 break;
124 }
125 }
126 if (!found)
127 memset(label, 0, sizeof(*label));
128
129 return found;
130}
131
132static int find_vol1_partitions(struct parsed_partitions *state,
133 struct hd_geometry *geo,
134 int blocksize,
135 char name[],
136 union label_t *label)
137{
138 sector_t blk;
139 int counter;
140 char tmp[64];
141 Sector sect;
142 unsigned char *data;
143 loff_t offset, size;
144 struct vtoc_format1_label f1;
145 int secperblk;
146
147 snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name);
148 strlcat(state->pp_buf, tmp, PAGE_SIZE);
149 /*
150 * get start of VTOC from the disk label and then search for format1
151 * and format8 labels
152 */
153 secperblk = blocksize >> 9;
154 blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
155 counter = 0;
156 data = read_part_sector(state, blk * secperblk, &sect);
157 while (data != NULL) {
158 memcpy(&f1, data, sizeof(struct vtoc_format1_label));
159 put_dev_sector(sect);
160 /* skip FMT4 / FMT5 / FMT7 labels */
161 if (f1.DS1FMTID == _ascebc['4']
162 || f1.DS1FMTID == _ascebc['5']
163 || f1.DS1FMTID == _ascebc['7']
164 || f1.DS1FMTID == _ascebc['9']) {
165 blk++;
166 data = read_part_sector(state, blk * secperblk, &sect);
167 continue;
168 }
169 /* only FMT1 and 8 labels valid at this point */
170 if (f1.DS1FMTID != _ascebc['1'] &&
171 f1.DS1FMTID != _ascebc['8'])
172 break;
173 /* OK, we got valid partition data */
174 offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
175 size = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
176 offset + geo->sectors;
177 offset *= secperblk;
178 size *= secperblk;
179 if (counter >= state->limit)
180 break;
181 put_partition(state, counter + 1, offset, size);
182 counter++;
183 blk++;
184 data = read_part_sector(state, blk * secperblk, &sect);
185 }
186 strlcat(state->pp_buf, "\n", PAGE_SIZE);
187
188 if (!data)
189 return -1;
190
191 return 1;
192}
193
194static int find_lnx1_partitions(struct parsed_partitions *state,
195 struct hd_geometry *geo,
196 int blocksize,
197 char name[],
198 union label_t *label,
199 sector_t labelsect,
200 loff_t i_size,
201 dasd_information2_t *info)
202{
203 loff_t offset, geo_size, size;
204 char tmp[64];
205 int secperblk;
206
207 snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name);
208 strlcat(state->pp_buf, tmp, PAGE_SIZE);
209 secperblk = blocksize >> 9;
210 if (label->lnx.ldl_version == 0xf2) {
211 size = label->lnx.formatted_blocks * secperblk;
212 } else {
213 /*
214 * Formated w/o large volume support. If the sanity check
215 * 'size based on geo == size based on i_size' is true, then
216 * we can safely assume that we know the formatted size of
217 * the disk, otherwise we need additional information
218 * that we can only get from a real DASD device.
219 */
220 geo_size = geo->cylinders * geo->heads
221 * geo->sectors * secperblk;
222 size = i_size >> 9;
223 if (size != geo_size) {
224 if (!info) {
225 strlcat(state->pp_buf, "\n", PAGE_SIZE);
226 return 1;
227 }
228 if (!strcmp(info->type, "ECKD"))
229 if (geo_size < size)
230 size = geo_size;
231 /* else keep size based on i_size */
232 }
233 }
234 /* first and only partition starts in the first block after the label */
235 offset = labelsect + secperblk;
236 put_partition(state, 1, offset, size - offset);
237 strlcat(state->pp_buf, "\n", PAGE_SIZE);
238 return 1;
239}
240
241static int find_cms1_partitions(struct parsed_partitions *state,
242 struct hd_geometry *geo,
243 int blocksize,
244 char name[],
245 union label_t *label,
246 sector_t labelsect)
247{
248 loff_t offset, size;
249 char tmp[64];
250 int secperblk;
251
252 /*
253 * VM style CMS1 labeled disk
254 */
255 blocksize = label->cms.block_size;
256 secperblk = blocksize >> 9;
257 if (label->cms.disk_offset != 0) {
258 snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name);
259 strlcat(state->pp_buf, tmp, PAGE_SIZE);
260 /* disk is reserved minidisk */
261 offset = label->cms.disk_offset * secperblk;
262 size = (label->cms.block_count - 1) * secperblk;
263 } else {
264 snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name);
265 strlcat(state->pp_buf, tmp, PAGE_SIZE);
266 /*
267 * Special case for FBA devices:
268 * If an FBA device is CMS formatted with blocksize > 512 byte
269 * and the DIAG discipline is used, then the CMS label is found
270 * in sector 1 instead of block 1. However, the partition is
271 * still supposed to start in block 2.
272 */
273 if (labelsect == 1)
274 offset = 2 * secperblk;
275 else
276 offset = labelsect + secperblk;
277 size = label->cms.block_count * secperblk;
278 }
279
280 put_partition(state, 1, offset, size-offset);
281 strlcat(state->pp_buf, "\n", PAGE_SIZE);
282 return 1;
283}
284
285
59/* 286/*
287 * This is the main function, called by check.c
60 */ 288 */
61int ibm_partition(struct parsed_partitions *state) 289int ibm_partition(struct parsed_partitions *state)
62{ 290{
63 struct block_device *bdev = state->bdev; 291 struct block_device *bdev = state->bdev;
64 int blocksize, res; 292 int blocksize, res;
65 loff_t i_size, offset, size, fmt_size; 293 loff_t i_size, offset, size;
66 dasd_information2_t *info; 294 dasd_information2_t *info;
67 struct hd_geometry *geo; 295 struct hd_geometry *geo;
68 char type[5] = {0,}; 296 char type[5] = {0,};
69 char name[7] = {0,}; 297 char name[7] = {0,};
70 union label_t {
71 struct vtoc_volume_label_cdl vol;
72 struct vtoc_volume_label_ldl lnx;
73 struct vtoc_cms_label cms;
74 } *label;
75 unsigned char *data;
76 Sector sect;
77 sector_t labelsect; 298 sector_t labelsect;
78 char tmp[64]; 299 union label_t *label;
79 300
80 res = 0; 301 res = 0;
81 blocksize = bdev_logical_block_size(bdev); 302 blocksize = bdev_logical_block_size(bdev);
@@ -84,7 +305,6 @@ int ibm_partition(struct parsed_partitions *state)
84 i_size = i_size_read(bdev->bd_inode); 305 i_size = i_size_read(bdev->bd_inode);
85 if (i_size == 0) 306 if (i_size == 0)
86 goto out_exit; 307 goto out_exit;
87
88 info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL); 308 info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL);
89 if (info == NULL) 309 if (info == NULL)
90 goto out_exit; 310 goto out_exit;
@@ -94,176 +314,45 @@ int ibm_partition(struct parsed_partitions *state)
94 label = kmalloc(sizeof(union label_t), GFP_KERNEL); 314 label = kmalloc(sizeof(union label_t), GFP_KERNEL);
95 if (label == NULL) 315 if (label == NULL)
96 goto out_nolab; 316 goto out_nolab;
97 317 if (ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
98 if (ioctl_by_bdev(bdev, BIODASDINFO2, (unsigned long)info) != 0 ||
99 ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
100 goto out_freeall; 318 goto out_freeall;
101 319 if (ioctl_by_bdev(bdev, BIODASDINFO2, (unsigned long)info) != 0) {
102 /* 320 kfree(info);
103 * Special case for FBA disks: label sector does not depend on 321 info = NULL;
104 * blocksize.
105 */
106 if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) ||
107 (info->cu_type == 0x3880 && info->dev_type == 0x3370))
108 labelsect = info->label_block;
109 else
110 labelsect = info->label_block * (blocksize >> 9);
111
112 /*
113 * Get volume label, extract name and type.
114 */
115 data = read_part_sector(state, labelsect, &sect);
116 if (data == NULL)
117 goto out_readerr;
118
119 memcpy(label, data, sizeof(union label_t));
120 put_dev_sector(sect);
121
122 if ((!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) {
123 strncpy(type, label->vol.vollbl, 4);
124 strncpy(name, label->vol.volid, 6);
125 } else {
126 strncpy(type, label->lnx.vollbl, 4);
127 strncpy(name, label->lnx.volid, 6);
128 } 322 }
129 EBCASC(type, 4);
130 EBCASC(name, 6);
131
132 res = 1;
133 323
134 /* 324 if (find_label(state, info, geo, blocksize, &labelsect, name, type,
135 * Three different formats: LDL, CDL and unformated disk 325 label)) {
136 * 326 if (!strncmp(type, "VOL1", 4)) {
137 * identified by info->format 327 res = find_vol1_partitions(state, geo, blocksize, name,
138 * 328 label);
139 * unformated disks we do not have to care about 329 } else if (!strncmp(type, "LNX1", 4)) {
140 */ 330 res = find_lnx1_partitions(state, geo, blocksize, name,
141 if (info->format == DASD_FORMAT_LDL) { 331 label, labelsect, i_size,
142 if (strncmp(type, "CMS1", 4) == 0) { 332 info);
143 /* 333 } else if (!strncmp(type, "CMS1", 4)) {
144 * VM style CMS1 labeled disk 334 res = find_cms1_partitions(state, geo, blocksize, name,
145 */ 335 label, labelsect);
146 blocksize = label->cms.block_size;
147 if (label->cms.disk_offset != 0) {
148 snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name);
149 strlcat(state->pp_buf, tmp, PAGE_SIZE);
150 /* disk is reserved minidisk */
151 offset = label->cms.disk_offset;
152 size = (label->cms.block_count - 1)
153 * (blocksize >> 9);
154 } else {
155 snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name);
156 strlcat(state->pp_buf, tmp, PAGE_SIZE);
157 offset = (info->label_block + 1);
158 size = label->cms.block_count
159 * (blocksize >> 9);
160 }
161 put_partition(state, 1, offset*(blocksize >> 9),
162 size-offset*(blocksize >> 9));
163 } else {
164 if (strncmp(type, "LNX1", 4) == 0) {
165 snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name);
166 strlcat(state->pp_buf, tmp, PAGE_SIZE);
167 if (label->lnx.ldl_version == 0xf2) {
168 fmt_size = label->lnx.formatted_blocks
169 * (blocksize >> 9);
170 } else if (!strcmp(info->type, "ECKD")) {
171 /* formated w/o large volume support */
172 fmt_size = geo->cylinders * geo->heads
173 * geo->sectors * (blocksize >> 9);
174 } else {
175 /* old label and no usable disk geometry
176 * (e.g. DIAG) */
177 fmt_size = i_size >> 9;
178 }
179 size = i_size >> 9;
180 if (fmt_size < size)
181 size = fmt_size;
182 offset = (info->label_block + 1);
183 } else {
184 /* unlabeled disk */
185 strlcat(state->pp_buf, "(nonl)", PAGE_SIZE);
186 size = i_size >> 9;
187 offset = (info->label_block + 1);
188 }
189 put_partition(state, 1, offset*(blocksize >> 9),
190 size-offset*(blocksize >> 9));
191 } 336 }
192 } else if (info->format == DASD_FORMAT_CDL) { 337 } else if (info) {
193 /*
194 * New style CDL formatted disk
195 */
196 sector_t blk;
197 int counter;
198
199 /* 338 /*
200 * check if VOL1 label is available 339 * ugly but needed for backward compatibility:
201 * if not, something is wrong, skipping partition detection 340 * If the block device is a DASD (i.e. BIODASDINFO2 works),
341 * then we claim it in any case, even though it has no valid
342 * label. If it has the LDL format, then we simply define a
343 * partition as if it had an LNX1 label.
202 */ 344 */
203 if (strncmp(type, "VOL1", 4) == 0) { 345 res = 1;
204 snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name); 346 if (info->format == DASD_FORMAT_LDL) {
205 strlcat(state->pp_buf, tmp, PAGE_SIZE); 347 strlcat(state->pp_buf, "(nonl)", PAGE_SIZE);
206 /* 348 size = i_size >> 9;
207 * get block number and read then go through format1 349 offset = (info->label_block + 1) * (blocksize >> 9);
208 * labels 350 put_partition(state, 1, offset, size-offset);
209 */ 351 strlcat(state->pp_buf, "\n", PAGE_SIZE);
210 blk = cchhb2blk(&label->vol.vtoc, geo) + 1; 352 }
211 counter = 0; 353 } else
212 data = read_part_sector(state, blk * (blocksize/512), 354 res = 0;
213 &sect);
214 while (data != NULL) {
215 struct vtoc_format1_label f1;
216
217 memcpy(&f1, data,
218 sizeof(struct vtoc_format1_label));
219 put_dev_sector(sect);
220
221 /* skip FMT4 / FMT5 / FMT7 labels */
222 if (f1.DS1FMTID == _ascebc['4']
223 || f1.DS1FMTID == _ascebc['5']
224 || f1.DS1FMTID == _ascebc['7']
225 || f1.DS1FMTID == _ascebc['9']) {
226 blk++;
227 data = read_part_sector(state,
228 blk * (blocksize/512), &sect);
229 continue;
230 }
231
232 /* only FMT1 and 8 labels valid at this point */
233 if (f1.DS1FMTID != _ascebc['1'] &&
234 f1.DS1FMTID != _ascebc['8'])
235 break;
236
237 /* OK, we got valid partition data */
238 offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
239 size = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
240 offset + geo->sectors;
241 if (counter >= state->limit)
242 break;
243 put_partition(state, counter + 1,
244 offset * (blocksize >> 9),
245 size * (blocksize >> 9));
246 counter++;
247 blk++;
248 data = read_part_sector(state,
249 blk * (blocksize/512), &sect);
250 }
251
252 if (!data)
253 /* Are we not supposed to report this ? */
254 goto out_readerr;
255 } else
256 printk(KERN_INFO "Expected Label VOL1 not "
257 "found, treating as CDL formated Disk");
258
259 }
260
261 strlcat(state->pp_buf, "\n", PAGE_SIZE);
262 goto out_freeall;
263
264 355
265out_readerr:
266 res = -1;
267out_freeall: 356out_freeall:
268 kfree(label); 357 kfree(label);
269out_nolab: 358out_nolab: