aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorStefan Weinhuber <wein@de.ibm.com>2009-03-26 10:23:47 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-03-26 10:24:05 -0400
commitb44b0ab3bac16356f03e94b1b49ba9305710c445 (patch)
tree66dfc19e2164a6a30d19b958ccf4e4a5d210c8d8 /drivers/s390
parentf9a28f7bc5225af476f8d4bb669038da8801b7c4 (diff)
[S390] dasd: add large volume support
The dasd device driver will now support ECKD devices with more then 65520 cylinders. In the traditional ECKD adressing scheme each track is addressed by a 16-bit cylinder and 16-bit head number. The new addressing scheme makes use of the fact that the actual number of heads is never larger then 15, so 12 bits of the head number can be redefined to be part of the cylinder address. Signed-off-by: Stefan Weinhuber <wein@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/block/dasd_eckd.c126
-rw-r--r--drivers/s390/block/dasd_eckd.h9
-rw-r--r--drivers/s390/block/dasd_int.h2
-rw-r--r--drivers/s390/block/dasd_ioctl.c4
-rw-r--r--drivers/s390/block/dasd_proc.c2
5 files changed, 81 insertions, 62 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 0eb5e5888c42..69f93e626fd3 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -159,6 +159,14 @@ recs_per_track(struct dasd_eckd_characteristics * rdc,
159 return 0; 159 return 0;
160} 160}
161 161
162static void set_ch_t(struct ch_t *geo, __u32 cyl, __u8 head)
163{
164 geo->cyl = (__u16) cyl;
165 geo->head = cyl >> 16;
166 geo->head <<= 4;
167 geo->head |= head;
168}
169
162static int 170static int
163check_XRC (struct ccw1 *de_ccw, 171check_XRC (struct ccw1 *de_ccw,
164 struct DE_eckd_data *data, 172 struct DE_eckd_data *data,
@@ -186,11 +194,12 @@ check_XRC (struct ccw1 *de_ccw,
186} 194}
187 195
188static int 196static int
189define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, 197define_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk,
190 int totrk, int cmd, struct dasd_device * device) 198 unsigned int totrk, int cmd, struct dasd_device *device)
191{ 199{
192 struct dasd_eckd_private *private; 200 struct dasd_eckd_private *private;
193 struct ch_t geo, beg, end; 201 u32 begcyl, endcyl;
202 u16 heads, beghead, endhead;
194 int rc = 0; 203 int rc = 0;
195 204
196 private = (struct dasd_eckd_private *) device->private; 205 private = (struct dasd_eckd_private *) device->private;
@@ -248,27 +257,24 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
248 && !(private->uses_cdl && trk < 2)) 257 && !(private->uses_cdl && trk < 2))
249 data->ga_extended |= 0x40; /* Regular Data Format Mode */ 258 data->ga_extended |= 0x40; /* Regular Data Format Mode */
250 259
251 geo.cyl = private->rdc_data.no_cyl; 260 heads = private->rdc_data.trk_per_cyl;
252 geo.head = private->rdc_data.trk_per_cyl; 261 begcyl = trk / heads;
253 beg.cyl = trk / geo.head; 262 beghead = trk % heads;
254 beg.head = trk % geo.head; 263 endcyl = totrk / heads;
255 end.cyl = totrk / geo.head; 264 endhead = totrk % heads;
256 end.head = totrk % geo.head;
257 265
258 /* check for sequential prestage - enhance cylinder range */ 266 /* check for sequential prestage - enhance cylinder range */
259 if (data->attributes.operation == DASD_SEQ_PRESTAGE || 267 if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
260 data->attributes.operation == DASD_SEQ_ACCESS) { 268 data->attributes.operation == DASD_SEQ_ACCESS) {
261 269
262 if (end.cyl + private->attrib.nr_cyl < geo.cyl) 270 if (endcyl + private->attrib.nr_cyl < private->real_cyl)
263 end.cyl += private->attrib.nr_cyl; 271 endcyl += private->attrib.nr_cyl;
264 else 272 else
265 end.cyl = (geo.cyl - 1); 273 endcyl = (private->real_cyl - 1);
266 } 274 }
267 275
268 data->beg_ext.cyl = beg.cyl; 276 set_ch_t(&data->beg_ext, begcyl, beghead);
269 data->beg_ext.head = beg.head; 277 set_ch_t(&data->end_ext, endcyl, endhead);
270 data->end_ext.cyl = end.cyl;
271 data->end_ext.head = end.head;
272 return rc; 278 return rc;
273} 279}
274 280
@@ -294,13 +300,14 @@ static int check_XRC_on_prefix(struct PFX_eckd_data *pfxdata,
294 return rc; 300 return rc;
295} 301}
296 302
297static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk, 303static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata,
298 int totrk, int cmd, struct dasd_device *basedev, 304 unsigned int trk, unsigned int totrk, int cmd,
299 struct dasd_device *startdev) 305 struct dasd_device *basedev, struct dasd_device *startdev)
300{ 306{
301 struct dasd_eckd_private *basepriv, *startpriv; 307 struct dasd_eckd_private *basepriv, *startpriv;
302 struct DE_eckd_data *data; 308 struct DE_eckd_data *data;
303 struct ch_t geo, beg, end; 309 u32 begcyl, endcyl;
310 u16 heads, beghead, endhead;
304 int rc = 0; 311 int rc = 0;
305 312
306 basepriv = (struct dasd_eckd_private *) basedev->private; 313 basepriv = (struct dasd_eckd_private *) basedev->private;
@@ -374,33 +381,30 @@ static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk,
374 && !(basepriv->uses_cdl && trk < 2)) 381 && !(basepriv->uses_cdl && trk < 2))
375 data->ga_extended |= 0x40; /* Regular Data Format Mode */ 382 data->ga_extended |= 0x40; /* Regular Data Format Mode */
376 383
377 geo.cyl = basepriv->rdc_data.no_cyl; 384 heads = basepriv->rdc_data.trk_per_cyl;
378 geo.head = basepriv->rdc_data.trk_per_cyl; 385 begcyl = trk / heads;
379 beg.cyl = trk / geo.head; 386 beghead = trk % heads;
380 beg.head = trk % geo.head; 387 endcyl = totrk / heads;
381 end.cyl = totrk / geo.head; 388 endhead = totrk % heads;
382 end.head = totrk % geo.head;
383 389
384 /* check for sequential prestage - enhance cylinder range */ 390 /* check for sequential prestage - enhance cylinder range */
385 if (data->attributes.operation == DASD_SEQ_PRESTAGE || 391 if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
386 data->attributes.operation == DASD_SEQ_ACCESS) { 392 data->attributes.operation == DASD_SEQ_ACCESS) {
387 393
388 if (end.cyl + basepriv->attrib.nr_cyl < geo.cyl) 394 if (endcyl + basepriv->attrib.nr_cyl < basepriv->real_cyl)
389 end.cyl += basepriv->attrib.nr_cyl; 395 endcyl += basepriv->attrib.nr_cyl;
390 else 396 else
391 end.cyl = (geo.cyl - 1); 397 endcyl = (basepriv->real_cyl - 1);
392 } 398 }
393 399
394 data->beg_ext.cyl = beg.cyl; 400 set_ch_t(&data->beg_ext, begcyl, beghead);
395 data->beg_ext.head = beg.head; 401 set_ch_t(&data->end_ext, endcyl, endhead);
396 data->end_ext.cyl = end.cyl;
397 data->end_ext.head = end.head;
398 return rc; 402 return rc;
399} 403}
400 404
401static void 405static void
402locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, 406locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, unsigned int trk,
403 int rec_on_trk, int no_rec, int cmd, 407 unsigned int rec_on_trk, int no_rec, int cmd,
404 struct dasd_device * device, int reclen) 408 struct dasd_device * device, int reclen)
405{ 409{
406 struct dasd_eckd_private *private; 410 struct dasd_eckd_private *private;
@@ -493,10 +497,11 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
493 default: 497 default:
494 DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd); 498 DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd);
495 } 499 }
496 data->seek_addr.cyl = data->search_arg.cyl = 500 set_ch_t(&data->seek_addr,
497 trk / private->rdc_data.trk_per_cyl; 501 trk / private->rdc_data.trk_per_cyl,
498 data->seek_addr.head = data->search_arg.head = 502 trk % private->rdc_data.trk_per_cyl);
499 trk % private->rdc_data.trk_per_cyl; 503 data->search_arg.cyl = data->seek_addr.cyl;
504 data->search_arg.head = data->seek_addr.head;
500 data->search_arg.record = rec_on_trk; 505 data->search_arg.record = rec_on_trk;
501} 506}
502 507
@@ -1002,13 +1007,20 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
1002 "rc=%d", rc); 1007 "rc=%d", rc);
1003 goto out_err3; 1008 goto out_err3;
1004 } 1009 }
1010 /* find the vaild cylinder size */
1011 if (private->rdc_data.no_cyl == LV_COMPAT_CYL &&
1012 private->rdc_data.long_no_cyl)
1013 private->real_cyl = private->rdc_data.long_no_cyl;
1014 else
1015 private->real_cyl = private->rdc_data.no_cyl;
1016
1005 DEV_MESSAGE(KERN_INFO, device, 1017 DEV_MESSAGE(KERN_INFO, device,
1006 "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", 1018 "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d",
1007 private->rdc_data.dev_type, 1019 private->rdc_data.dev_type,
1008 private->rdc_data.dev_model, 1020 private->rdc_data.dev_model,
1009 private->rdc_data.cu_type, 1021 private->rdc_data.cu_type,
1010 private->rdc_data.cu_model.model, 1022 private->rdc_data.cu_model.model,
1011 private->rdc_data.no_cyl, 1023 private->real_cyl,
1012 private->rdc_data.trk_per_cyl, 1024 private->rdc_data.trk_per_cyl,
1013 private->rdc_data.sec_per_trk); 1025 private->rdc_data.sec_per_trk);
1014 return 0; 1026 return 0;
@@ -1157,8 +1169,6 @@ dasd_eckd_end_analysis(struct dasd_block *block)
1157 } 1169 }
1158 1170
1159 private->uses_cdl = 1; 1171 private->uses_cdl = 1;
1160 /* Calculate number of blocks/records per track. */
1161 blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block);
1162 /* Check Track 0 for Compatible Disk Layout */ 1172 /* Check Track 0 for Compatible Disk Layout */
1163 count_area = NULL; 1173 count_area = NULL;
1164 for (i = 0; i < 3; i++) { 1174 for (i = 0; i < 3; i++) {
@@ -1200,14 +1210,14 @@ dasd_eckd_end_analysis(struct dasd_block *block)
1200 block->s2b_shift++; 1210 block->s2b_shift++;
1201 1211
1202 blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); 1212 blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block);
1203 block->blocks = (private->rdc_data.no_cyl * 1213 block->blocks = (private->real_cyl *
1204 private->rdc_data.trk_per_cyl * 1214 private->rdc_data.trk_per_cyl *
1205 blk_per_trk); 1215 blk_per_trk);
1206 1216
1207 DEV_MESSAGE(KERN_INFO, device, 1217 DEV_MESSAGE(KERN_INFO, device,
1208 "(%dkB blks): %dkB at %dkB/trk %s", 1218 "(%dkB blks): %dkB at %dkB/trk %s",
1209 (block->bp_block >> 10), 1219 (block->bp_block >> 10),
1210 ((private->rdc_data.no_cyl * 1220 ((private->real_cyl *
1211 private->rdc_data.trk_per_cyl * 1221 private->rdc_data.trk_per_cyl *
1212 blk_per_trk * (block->bp_block >> 9)) >> 1), 1222 blk_per_trk * (block->bp_block >> 9)) >> 1),
1213 ((blk_per_trk * block->bp_block) >> 10), 1223 ((blk_per_trk * block->bp_block) >> 10),
@@ -1262,7 +1272,8 @@ dasd_eckd_format_device(struct dasd_device * device,
1262 struct eckd_count *ect; 1272 struct eckd_count *ect;
1263 struct ccw1 *ccw; 1273 struct ccw1 *ccw;
1264 void *data; 1274 void *data;
1265 int rpt, cyl, head; 1275 int rpt;
1276 struct ch_t address;
1266 int cplength, datasize; 1277 int cplength, datasize;
1267 int i; 1278 int i;
1268 int intensity = 0; 1279 int intensity = 0;
@@ -1270,24 +1281,25 @@ dasd_eckd_format_device(struct dasd_device * device,
1270 1281
1271 private = (struct dasd_eckd_private *) device->private; 1282 private = (struct dasd_eckd_private *) device->private;
1272 rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize); 1283 rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize);
1273 cyl = fdata->start_unit / private->rdc_data.trk_per_cyl; 1284 set_ch_t(&address,
1274 head = fdata->start_unit % private->rdc_data.trk_per_cyl; 1285 fdata->start_unit / private->rdc_data.trk_per_cyl,
1286 fdata->start_unit % private->rdc_data.trk_per_cyl);
1275 1287
1276 /* Sanity checks. */ 1288 /* Sanity checks. */
1277 if (fdata->start_unit >= 1289 if (fdata->start_unit >=
1278 (private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl)) { 1290 (private->real_cyl * private->rdc_data.trk_per_cyl)) {
1279 DEV_MESSAGE(KERN_INFO, device, "Track no %d too big!", 1291 DEV_MESSAGE(KERN_INFO, device, "Track no %u too big!",
1280 fdata->start_unit); 1292 fdata->start_unit);
1281 return ERR_PTR(-EINVAL); 1293 return ERR_PTR(-EINVAL);
1282 } 1294 }
1283 if (fdata->start_unit > fdata->stop_unit) { 1295 if (fdata->start_unit > fdata->stop_unit) {
1284 DEV_MESSAGE(KERN_INFO, device, "Track %d reached! ending.", 1296 DEV_MESSAGE(KERN_INFO, device, "Track %u reached! ending.",
1285 fdata->start_unit); 1297 fdata->start_unit);
1286 return ERR_PTR(-EINVAL); 1298 return ERR_PTR(-EINVAL);
1287 } 1299 }
1288 if (dasd_check_blocksize(fdata->blksize) != 0) { 1300 if (dasd_check_blocksize(fdata->blksize) != 0) {
1289 DEV_MESSAGE(KERN_WARNING, device, 1301 DEV_MESSAGE(KERN_WARNING, device,
1290 "Invalid blocksize %d...terminating!", 1302 "Invalid blocksize %u...terminating!",
1291 fdata->blksize); 1303 fdata->blksize);
1292 return ERR_PTR(-EINVAL); 1304 return ERR_PTR(-EINVAL);
1293 } 1305 }
@@ -1389,8 +1401,8 @@ dasd_eckd_format_device(struct dasd_device * device,
1389 if (intensity & 0x01) { /* write record zero */ 1401 if (intensity & 0x01) { /* write record zero */
1390 ect = (struct eckd_count *) data; 1402 ect = (struct eckd_count *) data;
1391 data += sizeof(struct eckd_count); 1403 data += sizeof(struct eckd_count);
1392 ect->cyl = cyl; 1404 ect->cyl = address.cyl;
1393 ect->head = head; 1405 ect->head = address.head;
1394 ect->record = 0; 1406 ect->record = 0;
1395 ect->kl = 0; 1407 ect->kl = 0;
1396 ect->dl = 8; 1408 ect->dl = 8;
@@ -1404,8 +1416,8 @@ dasd_eckd_format_device(struct dasd_device * device,
1404 if ((intensity & ~0x08) & 0x04) { /* erase track */ 1416 if ((intensity & ~0x08) & 0x04) { /* erase track */
1405 ect = (struct eckd_count *) data; 1417 ect = (struct eckd_count *) data;
1406 data += sizeof(struct eckd_count); 1418 data += sizeof(struct eckd_count);
1407 ect->cyl = cyl; 1419 ect->cyl = address.cyl;
1408 ect->head = head; 1420 ect->head = address.head;
1409 ect->record = 1; 1421 ect->record = 1;
1410 ect->kl = 0; 1422 ect->kl = 0;
1411 ect->dl = 0; 1423 ect->dl = 0;
@@ -1418,8 +1430,8 @@ dasd_eckd_format_device(struct dasd_device * device,
1418 for (i = 0; i < rpt; i++) { 1430 for (i = 0; i < rpt; i++) {
1419 ect = (struct eckd_count *) data; 1431 ect = (struct eckd_count *) data;
1420 data += sizeof(struct eckd_count); 1432 data += sizeof(struct eckd_count);
1421 ect->cyl = cyl; 1433 ect->cyl = address.cyl;
1422 ect->head = head; 1434 ect->head = address.head;
1423 ect->record = i + 1; 1435 ect->record = i + 1;
1424 ect->kl = 0; 1436 ect->kl = 0;
1425 ect->dl = fdata->blksize; 1437 ect->dl = fdata->blksize;
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index 2476f87d21d0..eecfa776db15 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -48,6 +48,11 @@
48#define PSF_ORDER_PRSSD 0x18 48#define PSF_ORDER_PRSSD 0x18
49#define PSF_ORDER_SSC 0x1D 49#define PSF_ORDER_SSC 0x1D
50 50
51/*
52 * Size that is reportet for large volumes in the old 16-bit no_cyl field
53 */
54#define LV_COMPAT_CYL 0xFFFE
55
51/***************************************************************************** 56/*****************************************************************************
52 * SECTION: Type Definitions 57 * SECTION: Type Definitions
53 ****************************************************************************/ 58 ****************************************************************************/
@@ -228,7 +233,8 @@ struct dasd_eckd_characteristics {
228 __u8 factor7; 233 __u8 factor7;
229 __u8 factor8; 234 __u8 factor8;
230 __u8 reserved2[3]; 235 __u8 reserved2[3];
231 __u8 reserved3[10]; 236 __u8 reserved3[6];
237 __u32 long_no_cyl;
232} __attribute__ ((packed)); 238} __attribute__ ((packed));
233 239
234/* elements of the configuration data */ 240/* elements of the configuration data */
@@ -406,6 +412,7 @@ struct dasd_eckd_private {
406 int uses_cdl; 412 int uses_cdl;
407 struct attrib_data_t attrib; /* e.g. cache operations */ 413 struct attrib_data_t attrib; /* e.g. cache operations */
408 struct dasd_rssd_features features; 414 struct dasd_rssd_features features;
415 u32 real_cyl;
409 416
410 /* alias managemnet */ 417 /* alias managemnet */
411 struct dasd_uid uid; 418 struct dasd_uid uid;
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index 4a39084d9c95..29991a9fa07a 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -378,7 +378,7 @@ struct dasd_block {
378 struct block_device *bdev; 378 struct block_device *bdev;
379 atomic_t open_count; 379 atomic_t open_count;
380 380
381 unsigned long blocks; /* size of volume in blocks */ 381 unsigned long long blocks; /* size of volume in blocks */
382 unsigned int bp_block; /* bytes per block */ 382 unsigned int bp_block; /* bytes per block */
383 unsigned int s2b_shift; /* log2 (bp_block/512) */ 383 unsigned int s2b_shift; /* log2 (bp_block/512) */
384 384
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index 16e6ba462cb6..a3bbdb807bad 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -146,7 +146,7 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
146 } 146 }
147 147
148 DBF_DEV_EVENT(DBF_NOTICE, base, 148 DBF_DEV_EVENT(DBF_NOTICE, base,
149 "formatting units %d to %d (%d B blocks) flags %d", 149 "formatting units %u to %u (%u B blocks) flags %u",
150 fdata->start_unit, 150 fdata->start_unit,
151 fdata->stop_unit, fdata->blksize, fdata->intensity); 151 fdata->stop_unit, fdata->blksize, fdata->intensity);
152 152
@@ -170,7 +170,7 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
170 if (rc) { 170 if (rc) {
171 if (rc != -ERESTARTSYS) 171 if (rc != -ERESTARTSYS)
172 DEV_MESSAGE(KERN_ERR, base, 172 DEV_MESSAGE(KERN_ERR, base,
173 " Formatting of unit %d failed " 173 " Formatting of unit %u failed "
174 "with rc = %d", 174 "with rc = %d",
175 fdata->start_unit, rc); 175 fdata->start_unit, rc);
176 return rc; 176 return rc;
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index bf6fd348f20e..0aa569419d57 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -112,7 +112,7 @@ dasd_devices_show(struct seq_file *m, void *v)
112 seq_printf(m, "n/f "); 112 seq_printf(m, "n/f ");
113 else 113 else
114 seq_printf(m, 114 seq_printf(m,
115 "at blocksize: %d, %ld blocks, %ld MB", 115 "at blocksize: %d, %lld blocks, %lld MB",
116 block->bp_block, block->blocks, 116 block->bp_block, block->blocks,
117 ((block->bp_block >> 9) * 117 ((block->bp_block >> 9) *
118 block->blocks) >> 11); 118 block->blocks) >> 11);