diff options
author | Stefan Weinhuber <wein@de.ibm.com> | 2009-03-26 10:23:47 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-03-26 10:24:05 -0400 |
commit | b44b0ab3bac16356f03e94b1b49ba9305710c445 (patch) | |
tree | 66dfc19e2164a6a30d19b958ccf4e4a5d210c8d8 /drivers/s390/block/dasd_eckd.c | |
parent | f9a28f7bc5225af476f8d4bb669038da8801b7c4 (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/block/dasd_eckd.c')
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 126 |
1 files changed, 69 insertions, 57 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 | ||
162 | static 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 | |||
162 | static int | 170 | static int |
163 | check_XRC (struct ccw1 *de_ccw, | 171 | check_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 | ||
188 | static int | 196 | static int |
189 | define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, | 197 | define_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 | ||
297 | static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk, | 303 | static 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 | ||
401 | static void | 405 | static void |
402 | locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, | 406 | locate_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; |