diff options
author | Stefan Haberland <stefan.haberland@de.ibm.com> | 2013-04-15 10:22:23 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-04-17 08:07:34 -0400 |
commit | d42e17129b9f473386d67c6a6549c28bd0e2b52e (patch) | |
tree | cc410d624cd4ede3dcc88ac93d6793d8e566fa93 /drivers/s390 | |
parent | e5dcf0025d7af58f525590ac86ac27cb44714e8d (diff) |
s390/dasd: improve speed of dasdfmt
Reorganize format IO requests and enable usage of PAV.
Signed-off-by: Stefan Haberland <stefan.haberland@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/block/dasd.c | 109 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 344 | ||||
-rw-r--r-- | drivers/s390/block/dasd_int.h | 10 | ||||
-rw-r--r-- | drivers/s390/block/dasd_ioctl.c | 31 |
4 files changed, 351 insertions, 143 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index f1b7fdc58a5f..4195cc05efeb 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c | |||
@@ -246,7 +246,7 @@ static struct dentry *dasd_debugfs_setup(const char *name, | |||
246 | static int dasd_state_known_to_basic(struct dasd_device *device) | 246 | static int dasd_state_known_to_basic(struct dasd_device *device) |
247 | { | 247 | { |
248 | struct dasd_block *block = device->block; | 248 | struct dasd_block *block = device->block; |
249 | int rc; | 249 | int rc = 0; |
250 | 250 | ||
251 | /* Allocate and register gendisk structure. */ | 251 | /* Allocate and register gendisk structure. */ |
252 | if (block) { | 252 | if (block) { |
@@ -273,7 +273,8 @@ static int dasd_state_known_to_basic(struct dasd_device *device) | |||
273 | DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created"); | 273 | DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created"); |
274 | 274 | ||
275 | device->state = DASD_STATE_BASIC; | 275 | device->state = DASD_STATE_BASIC; |
276 | return 0; | 276 | |
277 | return rc; | ||
277 | } | 278 | } |
278 | 279 | ||
279 | /* | 280 | /* |
@@ -282,6 +283,7 @@ static int dasd_state_known_to_basic(struct dasd_device *device) | |||
282 | static int dasd_state_basic_to_known(struct dasd_device *device) | 283 | static int dasd_state_basic_to_known(struct dasd_device *device) |
283 | { | 284 | { |
284 | int rc; | 285 | int rc; |
286 | |||
285 | if (device->block) { | 287 | if (device->block) { |
286 | dasd_profile_exit(&device->block->profile); | 288 | dasd_profile_exit(&device->block->profile); |
287 | if (device->block->debugfs_dentry) | 289 | if (device->block->debugfs_dentry) |
@@ -332,8 +334,10 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) | |||
332 | if (block->base->discipline->do_analysis != NULL) | 334 | if (block->base->discipline->do_analysis != NULL) |
333 | rc = block->base->discipline->do_analysis(block); | 335 | rc = block->base->discipline->do_analysis(block); |
334 | if (rc) { | 336 | if (rc) { |
335 | if (rc != -EAGAIN) | 337 | if (rc != -EAGAIN) { |
336 | device->state = DASD_STATE_UNFMT; | 338 | device->state = DASD_STATE_UNFMT; |
339 | goto out; | ||
340 | } | ||
337 | return rc; | 341 | return rc; |
338 | } | 342 | } |
339 | dasd_setup_queue(block); | 343 | dasd_setup_queue(block); |
@@ -341,11 +345,16 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) | |||
341 | block->blocks << block->s2b_shift); | 345 | block->blocks << block->s2b_shift); |
342 | device->state = DASD_STATE_READY; | 346 | device->state = DASD_STATE_READY; |
343 | rc = dasd_scan_partitions(block); | 347 | rc = dasd_scan_partitions(block); |
344 | if (rc) | 348 | if (rc) { |
345 | device->state = DASD_STATE_BASIC; | 349 | device->state = DASD_STATE_BASIC; |
350 | return rc; | ||
351 | } | ||
346 | } else { | 352 | } else { |
347 | device->state = DASD_STATE_READY; | 353 | device->state = DASD_STATE_READY; |
348 | } | 354 | } |
355 | out: | ||
356 | if (device->discipline->basic_to_ready) | ||
357 | rc = device->discipline->basic_to_ready(device); | ||
349 | return rc; | 358 | return rc; |
350 | } | 359 | } |
351 | 360 | ||
@@ -368,6 +377,11 @@ static int dasd_state_ready_to_basic(struct dasd_device *device) | |||
368 | { | 377 | { |
369 | int rc; | 378 | int rc; |
370 | 379 | ||
380 | if (device->discipline->ready_to_basic) { | ||
381 | rc = device->discipline->ready_to_basic(device); | ||
382 | if (rc) | ||
383 | return rc; | ||
384 | } | ||
371 | device->state = DASD_STATE_BASIC; | 385 | device->state = DASD_STATE_BASIC; |
372 | if (device->block) { | 386 | if (device->block) { |
373 | struct dasd_block *block = device->block; | 387 | struct dasd_block *block = device->block; |
@@ -402,16 +416,10 @@ static int dasd_state_unfmt_to_basic(struct dasd_device *device) | |||
402 | static int | 416 | static int |
403 | dasd_state_ready_to_online(struct dasd_device * device) | 417 | dasd_state_ready_to_online(struct dasd_device * device) |
404 | { | 418 | { |
405 | int rc; | ||
406 | struct gendisk *disk; | 419 | struct gendisk *disk; |
407 | struct disk_part_iter piter; | 420 | struct disk_part_iter piter; |
408 | struct hd_struct *part; | 421 | struct hd_struct *part; |
409 | 422 | ||
410 | if (device->discipline->ready_to_online) { | ||
411 | rc = device->discipline->ready_to_online(device); | ||
412 | if (rc) | ||
413 | return rc; | ||
414 | } | ||
415 | device->state = DASD_STATE_ONLINE; | 423 | device->state = DASD_STATE_ONLINE; |
416 | if (device->block) { | 424 | if (device->block) { |
417 | dasd_schedule_block_bh(device->block); | 425 | dasd_schedule_block_bh(device->block); |
@@ -444,6 +452,7 @@ static int dasd_state_online_to_ready(struct dasd_device *device) | |||
444 | if (rc) | 452 | if (rc) |
445 | return rc; | 453 | return rc; |
446 | } | 454 | } |
455 | |||
447 | device->state = DASD_STATE_READY; | 456 | device->state = DASD_STATE_READY; |
448 | if (device->block && !(device->features & DASD_FEATURE_USERAW)) { | 457 | if (device->block && !(device->features & DASD_FEATURE_USERAW)) { |
449 | disk = device->block->bdev->bd_disk; | 458 | disk = device->block->bdev->bd_disk; |
@@ -2223,6 +2232,77 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) | |||
2223 | return rc; | 2232 | return rc; |
2224 | } | 2233 | } |
2225 | 2234 | ||
2235 | static inline int _wait_for_wakeup_queue(struct list_head *ccw_queue) | ||
2236 | { | ||
2237 | struct dasd_ccw_req *cqr; | ||
2238 | |||
2239 | list_for_each_entry(cqr, ccw_queue, blocklist) { | ||
2240 | if (cqr->callback_data != DASD_SLEEPON_END_TAG) | ||
2241 | return 0; | ||
2242 | } | ||
2243 | |||
2244 | return 1; | ||
2245 | } | ||
2246 | |||
2247 | static int _dasd_sleep_on_queue(struct list_head *ccw_queue, int interruptible) | ||
2248 | { | ||
2249 | struct dasd_device *device; | ||
2250 | int rc; | ||
2251 | struct dasd_ccw_req *cqr, *n; | ||
2252 | |||
2253 | retry: | ||
2254 | list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { | ||
2255 | device = cqr->startdev; | ||
2256 | if (cqr->status != DASD_CQR_FILLED) /*could be failed*/ | ||
2257 | continue; | ||
2258 | |||
2259 | if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags) && | ||
2260 | !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { | ||
2261 | cqr->status = DASD_CQR_FAILED; | ||
2262 | cqr->intrc = -EPERM; | ||
2263 | continue; | ||
2264 | } | ||
2265 | /*Non-temporary stop condition will trigger fail fast*/ | ||
2266 | if (device->stopped & ~DASD_STOPPED_PENDING && | ||
2267 | test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && | ||
2268 | !dasd_eer_enabled(device)) { | ||
2269 | cqr->status = DASD_CQR_FAILED; | ||
2270 | cqr->intrc = -EAGAIN; | ||
2271 | continue; | ||
2272 | } | ||
2273 | |||
2274 | /*Don't try to start requests if device is stopped*/ | ||
2275 | if (interruptible) { | ||
2276 | rc = wait_event_interruptible( | ||
2277 | generic_waitq, !device->stopped); | ||
2278 | if (rc == -ERESTARTSYS) { | ||
2279 | cqr->status = DASD_CQR_FAILED; | ||
2280 | cqr->intrc = rc; | ||
2281 | continue; | ||
2282 | } | ||
2283 | } else | ||
2284 | wait_event(generic_waitq, !(device->stopped)); | ||
2285 | |||
2286 | if (!cqr->callback) | ||
2287 | cqr->callback = dasd_wakeup_cb; | ||
2288 | cqr->callback_data = DASD_SLEEPON_START_TAG; | ||
2289 | dasd_add_request_tail(cqr); | ||
2290 | } | ||
2291 | |||
2292 | wait_event(generic_waitq, _wait_for_wakeup_queue(ccw_queue)); | ||
2293 | |||
2294 | rc = 0; | ||
2295 | list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { | ||
2296 | if (__dasd_sleep_on_erp(cqr)) | ||
2297 | rc = 1; | ||
2298 | } | ||
2299 | if (rc) | ||
2300 | goto retry; | ||
2301 | |||
2302 | |||
2303 | return 0; | ||
2304 | } | ||
2305 | |||
2226 | /* | 2306 | /* |
2227 | * Queue a request to the tail of the device ccw_queue and wait for | 2307 | * Queue a request to the tail of the device ccw_queue and wait for |
2228 | * it's completion. | 2308 | * it's completion. |
@@ -2233,6 +2313,15 @@ int dasd_sleep_on(struct dasd_ccw_req *cqr) | |||
2233 | } | 2313 | } |
2234 | 2314 | ||
2235 | /* | 2315 | /* |
2316 | * Start requests from a ccw_queue and wait for their completion. | ||
2317 | */ | ||
2318 | int dasd_sleep_on_queue(struct list_head *ccw_queue) | ||
2319 | { | ||
2320 | return _dasd_sleep_on_queue(ccw_queue, 0); | ||
2321 | } | ||
2322 | EXPORT_SYMBOL(dasd_sleep_on_queue); | ||
2323 | |||
2324 | /* | ||
2236 | * Queue a request to the tail of the device ccw_queue and wait | 2325 | * Queue a request to the tail of the device ccw_queue and wait |
2237 | * interruptible for it's completion. | 2326 | * interruptible for it's completion. |
2238 | */ | 2327 | */ |
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 6999fd919e94..6a44b27623ed 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c | |||
@@ -2022,7 +2022,7 @@ static int dasd_eckd_do_analysis(struct dasd_block *block) | |||
2022 | return dasd_eckd_end_analysis(block); | 2022 | return dasd_eckd_end_analysis(block); |
2023 | } | 2023 | } |
2024 | 2024 | ||
2025 | static int dasd_eckd_ready_to_online(struct dasd_device *device) | 2025 | static int dasd_eckd_basic_to_ready(struct dasd_device *device) |
2026 | { | 2026 | { |
2027 | return dasd_alias_add_device(device); | 2027 | return dasd_alias_add_device(device); |
2028 | }; | 2028 | }; |
@@ -2031,6 +2031,11 @@ static int dasd_eckd_online_to_ready(struct dasd_device *device) | |||
2031 | { | 2031 | { |
2032 | cancel_work_sync(&device->reload_device); | 2032 | cancel_work_sync(&device->reload_device); |
2033 | cancel_work_sync(&device->kick_validate); | 2033 | cancel_work_sync(&device->kick_validate); |
2034 | return 0; | ||
2035 | }; | ||
2036 | |||
2037 | static int dasd_eckd_ready_to_basic(struct dasd_device *device) | ||
2038 | { | ||
2034 | return dasd_alias_remove_device(device); | 2039 | return dasd_alias_remove_device(device); |
2035 | }; | 2040 | }; |
2036 | 2041 | ||
@@ -2050,45 +2055,34 @@ dasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) | |||
2050 | } | 2055 | } |
2051 | 2056 | ||
2052 | static struct dasd_ccw_req * | 2057 | static struct dasd_ccw_req * |
2053 | dasd_eckd_format_device(struct dasd_device * device, | 2058 | dasd_eckd_build_format(struct dasd_device *base, |
2054 | struct format_data_t * fdata) | 2059 | struct format_data_t *fdata) |
2055 | { | 2060 | { |
2056 | struct dasd_eckd_private *private; | 2061 | struct dasd_eckd_private *base_priv; |
2062 | struct dasd_eckd_private *start_priv; | ||
2063 | struct dasd_device *startdev; | ||
2057 | struct dasd_ccw_req *fcp; | 2064 | struct dasd_ccw_req *fcp; |
2058 | struct eckd_count *ect; | 2065 | struct eckd_count *ect; |
2066 | struct ch_t address; | ||
2059 | struct ccw1 *ccw; | 2067 | struct ccw1 *ccw; |
2060 | void *data; | 2068 | void *data; |
2061 | int rpt; | 2069 | int rpt; |
2062 | struct ch_t address; | ||
2063 | int cplength, datasize; | 2070 | int cplength, datasize; |
2064 | int i; | 2071 | int i, j; |
2065 | int intensity = 0; | 2072 | int intensity = 0; |
2066 | int r0_perm; | 2073 | int r0_perm; |
2074 | int nr_tracks; | ||
2067 | 2075 | ||
2068 | private = (struct dasd_eckd_private *) device->private; | 2076 | startdev = dasd_alias_get_start_dev(base); |
2069 | rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize); | 2077 | if (!startdev) |
2070 | set_ch_t(&address, | 2078 | startdev = base; |
2071 | fdata->start_unit / private->rdc_data.trk_per_cyl, | ||
2072 | fdata->start_unit % private->rdc_data.trk_per_cyl); | ||
2073 | 2079 | ||
2074 | /* Sanity checks. */ | 2080 | start_priv = (struct dasd_eckd_private *) startdev->private; |
2075 | if (fdata->start_unit >= | 2081 | base_priv = (struct dasd_eckd_private *) base->private; |
2076 | (private->real_cyl * private->rdc_data.trk_per_cyl)) { | 2082 | |
2077 | dev_warn(&device->cdev->dev, "Start track number %d used in " | 2083 | rpt = recs_per_track(&base_priv->rdc_data, 0, fdata->blksize); |
2078 | "formatting is too big\n", fdata->start_unit); | 2084 | |
2079 | return ERR_PTR(-EINVAL); | 2085 | nr_tracks = fdata->stop_unit - fdata->start_unit + 1; |
2080 | } | ||
2081 | if (fdata->start_unit > fdata->stop_unit) { | ||
2082 | dev_warn(&device->cdev->dev, "Start track %d used in " | ||
2083 | "formatting exceeds end track\n", fdata->start_unit); | ||
2084 | return ERR_PTR(-EINVAL); | ||
2085 | } | ||
2086 | if (dasd_check_blocksize(fdata->blksize) != 0) { | ||
2087 | dev_warn(&device->cdev->dev, | ||
2088 | "The DASD cannot be formatted with block size %d\n", | ||
2089 | fdata->blksize); | ||
2090 | return ERR_PTR(-EINVAL); | ||
2091 | } | ||
2092 | 2086 | ||
2093 | /* | 2087 | /* |
2094 | * fdata->intensity is a bit string that tells us what to do: | 2088 | * fdata->intensity is a bit string that tells us what to do: |
@@ -2106,149 +2100,282 @@ dasd_eckd_format_device(struct dasd_device * device, | |||
2106 | r0_perm = 1; | 2100 | r0_perm = 1; |
2107 | intensity = fdata->intensity; | 2101 | intensity = fdata->intensity; |
2108 | } | 2102 | } |
2103 | |||
2109 | switch (intensity) { | 2104 | switch (intensity) { |
2110 | case 0x00: /* Normal format */ | 2105 | case 0x00: /* Normal format */ |
2111 | case 0x08: /* Normal format, use cdl. */ | 2106 | case 0x08: /* Normal format, use cdl. */ |
2112 | cplength = 2 + rpt; | 2107 | cplength = 2 + (rpt*nr_tracks); |
2113 | datasize = sizeof(struct DE_eckd_data) + | 2108 | datasize = sizeof(struct PFX_eckd_data) + |
2114 | sizeof(struct LO_eckd_data) + | 2109 | sizeof(struct LO_eckd_data) + |
2115 | rpt * sizeof(struct eckd_count); | 2110 | rpt * nr_tracks * sizeof(struct eckd_count); |
2116 | break; | 2111 | break; |
2117 | case 0x01: /* Write record zero and format track. */ | 2112 | case 0x01: /* Write record zero and format track. */ |
2118 | case 0x09: /* Write record zero and format track, use cdl. */ | 2113 | case 0x09: /* Write record zero and format track, use cdl. */ |
2119 | cplength = 3 + rpt; | 2114 | cplength = 2 + rpt * nr_tracks; |
2120 | datasize = sizeof(struct DE_eckd_data) + | 2115 | datasize = sizeof(struct PFX_eckd_data) + |
2121 | sizeof(struct LO_eckd_data) + | 2116 | sizeof(struct LO_eckd_data) + |
2122 | sizeof(struct eckd_count) + | 2117 | sizeof(struct eckd_count) + |
2123 | rpt * sizeof(struct eckd_count); | 2118 | rpt * nr_tracks * sizeof(struct eckd_count); |
2124 | break; | 2119 | break; |
2125 | case 0x04: /* Invalidate track. */ | 2120 | case 0x04: /* Invalidate track. */ |
2126 | case 0x0c: /* Invalidate track, use cdl. */ | 2121 | case 0x0c: /* Invalidate track, use cdl. */ |
2127 | cplength = 3; | 2122 | cplength = 3; |
2128 | datasize = sizeof(struct DE_eckd_data) + | 2123 | datasize = sizeof(struct PFX_eckd_data) + |
2129 | sizeof(struct LO_eckd_data) + | 2124 | sizeof(struct LO_eckd_data) + |
2130 | sizeof(struct eckd_count); | 2125 | sizeof(struct eckd_count); |
2131 | break; | 2126 | break; |
2132 | default: | 2127 | default: |
2133 | dev_warn(&device->cdev->dev, "An I/O control call used " | 2128 | dev_warn(&startdev->cdev->dev, |
2134 | "incorrect flags 0x%x\n", fdata->intensity); | 2129 | "An I/O control call used incorrect flags 0x%x\n", |
2130 | fdata->intensity); | ||
2135 | return ERR_PTR(-EINVAL); | 2131 | return ERR_PTR(-EINVAL); |
2136 | } | 2132 | } |
2137 | /* Allocate the format ccw request. */ | 2133 | /* Allocate the format ccw request. */ |
2138 | fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device); | 2134 | fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, |
2135 | datasize, startdev); | ||
2139 | if (IS_ERR(fcp)) | 2136 | if (IS_ERR(fcp)) |
2140 | return fcp; | 2137 | return fcp; |
2141 | 2138 | ||
2139 | start_priv->count++; | ||
2142 | data = fcp->data; | 2140 | data = fcp->data; |
2143 | ccw = fcp->cpaddr; | 2141 | ccw = fcp->cpaddr; |
2144 | 2142 | ||
2145 | switch (intensity & ~0x08) { | 2143 | switch (intensity & ~0x08) { |
2146 | case 0x00: /* Normal format. */ | 2144 | case 0x00: /* Normal format. */ |
2147 | define_extent(ccw++, (struct DE_eckd_data *) data, | 2145 | prefix(ccw++, (struct PFX_eckd_data *) data, |
2148 | fdata->start_unit, fdata->start_unit, | 2146 | fdata->start_unit, fdata->stop_unit, |
2149 | DASD_ECKD_CCW_WRITE_CKD, device); | 2147 | DASD_ECKD_CCW_WRITE_CKD, base, startdev); |
2150 | /* grant subsystem permission to format R0 */ | 2148 | /* grant subsystem permission to format R0 */ |
2151 | if (r0_perm) | 2149 | if (r0_perm) |
2152 | ((struct DE_eckd_data *)data)->ga_extended |= 0x04; | 2150 | ((struct PFX_eckd_data *)data) |
2153 | data += sizeof(struct DE_eckd_data); | 2151 | ->define_extent.ga_extended |= 0x04; |
2152 | data += sizeof(struct PFX_eckd_data); | ||
2154 | ccw[-1].flags |= CCW_FLAG_CC; | 2153 | ccw[-1].flags |= CCW_FLAG_CC; |
2155 | locate_record(ccw++, (struct LO_eckd_data *) data, | 2154 | locate_record(ccw++, (struct LO_eckd_data *) data, |
2156 | fdata->start_unit, 0, rpt, | 2155 | fdata->start_unit, 0, rpt*nr_tracks, |
2157 | DASD_ECKD_CCW_WRITE_CKD, device, | 2156 | DASD_ECKD_CCW_WRITE_CKD, base, |
2158 | fdata->blksize); | 2157 | fdata->blksize); |
2159 | data += sizeof(struct LO_eckd_data); | 2158 | data += sizeof(struct LO_eckd_data); |
2160 | break; | 2159 | break; |
2161 | case 0x01: /* Write record zero + format track. */ | 2160 | case 0x01: /* Write record zero + format track. */ |
2162 | define_extent(ccw++, (struct DE_eckd_data *) data, | 2161 | prefix(ccw++, (struct PFX_eckd_data *) data, |
2163 | fdata->start_unit, fdata->start_unit, | 2162 | fdata->start_unit, fdata->stop_unit, |
2164 | DASD_ECKD_CCW_WRITE_RECORD_ZERO, | 2163 | DASD_ECKD_CCW_WRITE_RECORD_ZERO, |
2165 | device); | 2164 | base, startdev); |
2166 | data += sizeof(struct DE_eckd_data); | 2165 | data += sizeof(struct PFX_eckd_data); |
2167 | ccw[-1].flags |= CCW_FLAG_CC; | 2166 | ccw[-1].flags |= CCW_FLAG_CC; |
2168 | locate_record(ccw++, (struct LO_eckd_data *) data, | 2167 | locate_record(ccw++, (struct LO_eckd_data *) data, |
2169 | fdata->start_unit, 0, rpt + 1, | 2168 | fdata->start_unit, 0, rpt * nr_tracks + 1, |
2170 | DASD_ECKD_CCW_WRITE_RECORD_ZERO, device, | 2169 | DASD_ECKD_CCW_WRITE_RECORD_ZERO, base, |
2171 | device->block->bp_block); | 2170 | base->block->bp_block); |
2172 | data += sizeof(struct LO_eckd_data); | 2171 | data += sizeof(struct LO_eckd_data); |
2173 | break; | 2172 | break; |
2174 | case 0x04: /* Invalidate track. */ | 2173 | case 0x04: /* Invalidate track. */ |
2175 | define_extent(ccw++, (struct DE_eckd_data *) data, | 2174 | prefix(ccw++, (struct PFX_eckd_data *) data, |
2176 | fdata->start_unit, fdata->start_unit, | 2175 | fdata->start_unit, fdata->stop_unit, |
2177 | DASD_ECKD_CCW_WRITE_CKD, device); | 2176 | DASD_ECKD_CCW_WRITE_CKD, base, startdev); |
2178 | data += sizeof(struct DE_eckd_data); | 2177 | data += sizeof(struct PFX_eckd_data); |
2179 | ccw[-1].flags |= CCW_FLAG_CC; | 2178 | ccw[-1].flags |= CCW_FLAG_CC; |
2180 | locate_record(ccw++, (struct LO_eckd_data *) data, | 2179 | locate_record(ccw++, (struct LO_eckd_data *) data, |
2181 | fdata->start_unit, 0, 1, | 2180 | fdata->start_unit, 0, 1, |
2182 | DASD_ECKD_CCW_WRITE_CKD, device, 8); | 2181 | DASD_ECKD_CCW_WRITE_CKD, base, 8); |
2183 | data += sizeof(struct LO_eckd_data); | 2182 | data += sizeof(struct LO_eckd_data); |
2184 | break; | 2183 | break; |
2185 | } | 2184 | } |
2186 | if (intensity & 0x01) { /* write record zero */ | 2185 | |
2187 | ect = (struct eckd_count *) data; | 2186 | for (j = 0; j < nr_tracks; j++) { |
2188 | data += sizeof(struct eckd_count); | 2187 | /* calculate cylinder and head for the current track */ |
2189 | ect->cyl = address.cyl; | 2188 | set_ch_t(&address, |
2190 | ect->head = address.head; | 2189 | (fdata->start_unit + j) / |
2191 | ect->record = 0; | 2190 | base_priv->rdc_data.trk_per_cyl, |
2192 | ect->kl = 0; | 2191 | (fdata->start_unit + j) % |
2193 | ect->dl = 8; | 2192 | base_priv->rdc_data.trk_per_cyl); |
2194 | ccw[-1].flags |= CCW_FLAG_CC; | 2193 | if (intensity & 0x01) { /* write record zero */ |
2195 | ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO; | ||
2196 | ccw->flags = CCW_FLAG_SLI; | ||
2197 | ccw->count = 8; | ||
2198 | ccw->cda = (__u32)(addr_t) ect; | ||
2199 | ccw++; | ||
2200 | } | ||
2201 | if ((intensity & ~0x08) & 0x04) { /* erase track */ | ||
2202 | ect = (struct eckd_count *) data; | ||
2203 | data += sizeof(struct eckd_count); | ||
2204 | ect->cyl = address.cyl; | ||
2205 | ect->head = address.head; | ||
2206 | ect->record = 1; | ||
2207 | ect->kl = 0; | ||
2208 | ect->dl = 0; | ||
2209 | ccw[-1].flags |= CCW_FLAG_CC; | ||
2210 | ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; | ||
2211 | ccw->flags = CCW_FLAG_SLI; | ||
2212 | ccw->count = 8; | ||
2213 | ccw->cda = (__u32)(addr_t) ect; | ||
2214 | } else { /* write remaining records */ | ||
2215 | for (i = 0; i < rpt; i++) { | ||
2216 | ect = (struct eckd_count *) data; | 2194 | ect = (struct eckd_count *) data; |
2217 | data += sizeof(struct eckd_count); | 2195 | data += sizeof(struct eckd_count); |
2218 | ect->cyl = address.cyl; | 2196 | ect->cyl = address.cyl; |
2219 | ect->head = address.head; | 2197 | ect->head = address.head; |
2220 | ect->record = i + 1; | 2198 | ect->record = 0; |
2221 | ect->kl = 0; | 2199 | ect->kl = 0; |
2222 | ect->dl = fdata->blksize; | 2200 | ect->dl = 8; |
2223 | /* Check for special tracks 0-1 when formatting CDL */ | ||
2224 | if ((intensity & 0x08) && | ||
2225 | fdata->start_unit == 0) { | ||
2226 | if (i < 3) { | ||
2227 | ect->kl = 4; | ||
2228 | ect->dl = sizes_trk0[i] - 4; | ||
2229 | } | ||
2230 | } | ||
2231 | if ((intensity & 0x08) && | ||
2232 | fdata->start_unit == 1) { | ||
2233 | ect->kl = 44; | ||
2234 | ect->dl = LABEL_SIZE - 44; | ||
2235 | } | ||
2236 | ccw[-1].flags |= CCW_FLAG_CC; | 2201 | ccw[-1].flags |= CCW_FLAG_CC; |
2237 | ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; | 2202 | ccw->cmd_code = DASD_ECKD_CCW_WRITE_RECORD_ZERO; |
2238 | ccw->flags = CCW_FLAG_SLI; | 2203 | ccw->flags = CCW_FLAG_SLI; |
2239 | ccw->count = 8; | 2204 | ccw->count = 8; |
2240 | ccw->cda = (__u32)(addr_t) ect; | 2205 | ccw->cda = (__u32)(addr_t) ect; |
2241 | ccw++; | 2206 | ccw++; |
2242 | } | 2207 | } |
2208 | if ((intensity & ~0x08) & 0x04) { /* erase track */ | ||
2209 | ect = (struct eckd_count *) data; | ||
2210 | data += sizeof(struct eckd_count); | ||
2211 | ect->cyl = address.cyl; | ||
2212 | ect->head = address.head; | ||
2213 | ect->record = 1; | ||
2214 | ect->kl = 0; | ||
2215 | ect->dl = 0; | ||
2216 | ccw[-1].flags |= CCW_FLAG_CC; | ||
2217 | ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; | ||
2218 | ccw->flags = CCW_FLAG_SLI; | ||
2219 | ccw->count = 8; | ||
2220 | ccw->cda = (__u32)(addr_t) ect; | ||
2221 | } else { /* write remaining records */ | ||
2222 | for (i = 0; i < rpt; i++) { | ||
2223 | ect = (struct eckd_count *) data; | ||
2224 | data += sizeof(struct eckd_count); | ||
2225 | ect->cyl = address.cyl; | ||
2226 | ect->head = address.head; | ||
2227 | ect->record = i + 1; | ||
2228 | ect->kl = 0; | ||
2229 | ect->dl = fdata->blksize; | ||
2230 | /* | ||
2231 | * Check for special tracks 0-1 | ||
2232 | * when formatting CDL | ||
2233 | */ | ||
2234 | if ((intensity & 0x08) && | ||
2235 | fdata->start_unit == 0) { | ||
2236 | if (i < 3) { | ||
2237 | ect->kl = 4; | ||
2238 | ect->dl = sizes_trk0[i] - 4; | ||
2239 | } | ||
2240 | } | ||
2241 | if ((intensity & 0x08) && | ||
2242 | fdata->start_unit == 1) { | ||
2243 | ect->kl = 44; | ||
2244 | ect->dl = LABEL_SIZE - 44; | ||
2245 | } | ||
2246 | ccw[-1].flags |= CCW_FLAG_CC; | ||
2247 | if (i != 0 || j == 0) | ||
2248 | ccw->cmd_code = | ||
2249 | DASD_ECKD_CCW_WRITE_CKD; | ||
2250 | else | ||
2251 | ccw->cmd_code = | ||
2252 | DASD_ECKD_CCW_WRITE_CKD_MT; | ||
2253 | ccw->flags = CCW_FLAG_SLI; | ||
2254 | ccw->count = 8; | ||
2255 | ccw->cda = (__u32)(addr_t) ect; | ||
2256 | ccw++; | ||
2257 | } | ||
2258 | } | ||
2243 | } | 2259 | } |
2244 | fcp->startdev = device; | 2260 | |
2245 | fcp->memdev = device; | 2261 | fcp->startdev = startdev; |
2262 | fcp->memdev = startdev; | ||
2246 | fcp->retries = 256; | 2263 | fcp->retries = 256; |
2264 | fcp->expires = startdev->default_expires * HZ; | ||
2247 | fcp->buildclk = get_tod_clock(); | 2265 | fcp->buildclk = get_tod_clock(); |
2248 | fcp->status = DASD_CQR_FILLED; | 2266 | fcp->status = DASD_CQR_FILLED; |
2267 | |||
2249 | return fcp; | 2268 | return fcp; |
2250 | } | 2269 | } |
2251 | 2270 | ||
2271 | static int | ||
2272 | dasd_eckd_format_device(struct dasd_device *base, | ||
2273 | struct format_data_t *fdata) | ||
2274 | { | ||
2275 | struct dasd_ccw_req *cqr, *n; | ||
2276 | struct dasd_block *block; | ||
2277 | struct dasd_eckd_private *private; | ||
2278 | struct list_head format_queue; | ||
2279 | struct dasd_device *device; | ||
2280 | int old_stop, format_step; | ||
2281 | int step, rc = 0; | ||
2282 | |||
2283 | block = base->block; | ||
2284 | private = (struct dasd_eckd_private *) base->private; | ||
2285 | |||
2286 | /* Sanity checks. */ | ||
2287 | if (fdata->start_unit >= | ||
2288 | (private->real_cyl * private->rdc_data.trk_per_cyl)) { | ||
2289 | dev_warn(&base->cdev->dev, | ||
2290 | "Start track number %u used in formatting is too big\n", | ||
2291 | fdata->start_unit); | ||
2292 | return -EINVAL; | ||
2293 | } | ||
2294 | if (fdata->stop_unit >= | ||
2295 | (private->real_cyl * private->rdc_data.trk_per_cyl)) { | ||
2296 | dev_warn(&base->cdev->dev, | ||
2297 | "Stop track number %u used in formatting is too big\n", | ||
2298 | fdata->stop_unit); | ||
2299 | return -EINVAL; | ||
2300 | } | ||
2301 | if (fdata->start_unit > fdata->stop_unit) { | ||
2302 | dev_warn(&base->cdev->dev, | ||
2303 | "Start track %u used in formatting exceeds end track\n", | ||
2304 | fdata->start_unit); | ||
2305 | return -EINVAL; | ||
2306 | } | ||
2307 | if (dasd_check_blocksize(fdata->blksize) != 0) { | ||
2308 | dev_warn(&base->cdev->dev, | ||
2309 | "The DASD cannot be formatted with block size %u\n", | ||
2310 | fdata->blksize); | ||
2311 | return -EINVAL; | ||
2312 | } | ||
2313 | |||
2314 | INIT_LIST_HEAD(&format_queue); | ||
2315 | old_stop = fdata->stop_unit; | ||
2316 | |||
2317 | while (fdata->start_unit <= 1) { | ||
2318 | fdata->stop_unit = fdata->start_unit; | ||
2319 | cqr = dasd_eckd_build_format(base, fdata); | ||
2320 | list_add(&cqr->blocklist, &format_queue); | ||
2321 | |||
2322 | fdata->stop_unit = old_stop; | ||
2323 | fdata->start_unit++; | ||
2324 | |||
2325 | if (fdata->start_unit > fdata->stop_unit) | ||
2326 | goto sleep; | ||
2327 | } | ||
2328 | |||
2329 | retry: | ||
2330 | format_step = 255 / recs_per_track(&private->rdc_data, 0, | ||
2331 | fdata->blksize); | ||
2332 | while (fdata->start_unit <= old_stop) { | ||
2333 | step = fdata->stop_unit - fdata->start_unit + 1; | ||
2334 | if (step > format_step) | ||
2335 | fdata->stop_unit = fdata->start_unit + format_step - 1; | ||
2336 | |||
2337 | cqr = dasd_eckd_build_format(base, fdata); | ||
2338 | if (IS_ERR(cqr)) { | ||
2339 | if (PTR_ERR(cqr) == -ENOMEM) { | ||
2340 | /* | ||
2341 | * not enough memory available | ||
2342 | * go to out and start requests | ||
2343 | * retry after first requests were finished | ||
2344 | */ | ||
2345 | fdata->stop_unit = old_stop; | ||
2346 | goto sleep; | ||
2347 | } else | ||
2348 | return PTR_ERR(cqr); | ||
2349 | } | ||
2350 | list_add(&cqr->blocklist, &format_queue); | ||
2351 | |||
2352 | fdata->start_unit = fdata->stop_unit + 1; | ||
2353 | fdata->stop_unit = old_stop; | ||
2354 | } | ||
2355 | |||
2356 | sleep: | ||
2357 | dasd_sleep_on_queue(&format_queue); | ||
2358 | |||
2359 | list_for_each_entry_safe(cqr, n, &format_queue, blocklist) { | ||
2360 | device = cqr->startdev; | ||
2361 | private = (struct dasd_eckd_private *) device->private; | ||
2362 | if (cqr->status == DASD_CQR_FAILED) | ||
2363 | rc = -EIO; | ||
2364 | list_del_init(&cqr->blocklist); | ||
2365 | dasd_sfree_request(cqr, device); | ||
2366 | private->count--; | ||
2367 | } | ||
2368 | |||
2369 | /* | ||
2370 | * in case of ENOMEM we need to retry after | ||
2371 | * first requests are finished | ||
2372 | */ | ||
2373 | if (fdata->start_unit <= fdata->stop_unit) | ||
2374 | goto retry; | ||
2375 | |||
2376 | return rc; | ||
2377 | } | ||
2378 | |||
2252 | static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) | 2379 | static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) |
2253 | { | 2380 | { |
2254 | cqr->status = DASD_CQR_FILLED; | 2381 | cqr->status = DASD_CQR_FILLED; |
@@ -4305,8 +4432,9 @@ static struct dasd_discipline dasd_eckd_discipline = { | |||
4305 | .uncheck_device = dasd_eckd_uncheck_device, | 4432 | .uncheck_device = dasd_eckd_uncheck_device, |
4306 | .do_analysis = dasd_eckd_do_analysis, | 4433 | .do_analysis = dasd_eckd_do_analysis, |
4307 | .verify_path = dasd_eckd_verify_path, | 4434 | .verify_path = dasd_eckd_verify_path, |
4308 | .ready_to_online = dasd_eckd_ready_to_online, | 4435 | .basic_to_ready = dasd_eckd_basic_to_ready, |
4309 | .online_to_ready = dasd_eckd_online_to_ready, | 4436 | .online_to_ready = dasd_eckd_online_to_ready, |
4437 | .ready_to_basic = dasd_eckd_ready_to_basic, | ||
4310 | .fill_geometry = dasd_eckd_fill_geometry, | 4438 | .fill_geometry = dasd_eckd_fill_geometry, |
4311 | .start_IO = dasd_start_IO, | 4439 | .start_IO = dasd_start_IO, |
4312 | .term_IO = dasd_term_IO, | 4440 | .term_IO = dasd_term_IO, |
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 899e3f5a56e5..0785bd9bd5b6 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h | |||
@@ -300,10 +300,11 @@ struct dasd_discipline { | |||
300 | * Last things to do when a device is set online, and first things | 300 | * Last things to do when a device is set online, and first things |
301 | * when it is set offline. | 301 | * when it is set offline. |
302 | */ | 302 | */ |
303 | int (*ready_to_online) (struct dasd_device *); | 303 | int (*basic_to_ready) (struct dasd_device *); |
304 | int (*online_to_ready) (struct dasd_device *); | 304 | int (*online_to_ready) (struct dasd_device *); |
305 | int (*ready_to_basic) (struct dasd_device *); | ||
305 | 306 | ||
306 | /* | 307 | /* (struct dasd_device *); |
307 | * Device operation functions. build_cp creates a ccw chain for | 308 | * Device operation functions. build_cp creates a ccw chain for |
308 | * a block device request, start_io starts the request and | 309 | * a block device request, start_io starts the request and |
309 | * term_IO cancels it (e.g. in case of a timeout). format_device | 310 | * term_IO cancels it (e.g. in case of a timeout). format_device |
@@ -317,8 +318,8 @@ struct dasd_discipline { | |||
317 | int (*start_IO) (struct dasd_ccw_req *); | 318 | int (*start_IO) (struct dasd_ccw_req *); |
318 | int (*term_IO) (struct dasd_ccw_req *); | 319 | int (*term_IO) (struct dasd_ccw_req *); |
319 | void (*handle_terminated_request) (struct dasd_ccw_req *); | 320 | void (*handle_terminated_request) (struct dasd_ccw_req *); |
320 | struct dasd_ccw_req *(*format_device) (struct dasd_device *, | 321 | int (*format_device) (struct dasd_device *, |
321 | struct format_data_t *); | 322 | struct format_data_t *); |
322 | int (*free_cp) (struct dasd_ccw_req *, struct request *); | 323 | int (*free_cp) (struct dasd_ccw_req *, struct request *); |
323 | 324 | ||
324 | /* | 325 | /* |
@@ -672,6 +673,7 @@ int dasd_term_IO(struct dasd_ccw_req *); | |||
672 | void dasd_schedule_device_bh(struct dasd_device *); | 673 | void dasd_schedule_device_bh(struct dasd_device *); |
673 | void dasd_schedule_block_bh(struct dasd_block *); | 674 | void dasd_schedule_block_bh(struct dasd_block *); |
674 | int dasd_sleep_on(struct dasd_ccw_req *); | 675 | int dasd_sleep_on(struct dasd_ccw_req *); |
676 | int dasd_sleep_on_queue(struct list_head *); | ||
675 | int dasd_sleep_on_immediatly(struct dasd_ccw_req *); | 677 | int dasd_sleep_on_immediatly(struct dasd_ccw_req *); |
676 | int dasd_sleep_on_interruptible(struct dasd_ccw_req *); | 678 | int dasd_sleep_on_interruptible(struct dasd_ccw_req *); |
677 | void dasd_device_set_timer(struct dasd_device *, int); | 679 | void dasd_device_set_timer(struct dasd_device *, int); |
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 03c0e0444553..8be1b51e9311 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c | |||
@@ -143,12 +143,12 @@ static int dasd_ioctl_resume(struct dasd_block *block) | |||
143 | /* | 143 | /* |
144 | * performs formatting of _device_ according to _fdata_ | 144 | * performs formatting of _device_ according to _fdata_ |
145 | * Note: The discipline's format_function is assumed to deliver formatting | 145 | * Note: The discipline's format_function is assumed to deliver formatting |
146 | * commands to format a single unit of the device. In terms of the ECKD | 146 | * commands to format multiple units of the device. In terms of the ECKD |
147 | * devices this means CCWs are generated to format a single track. | 147 | * devices this means CCWs are generated to format multiple tracks. |
148 | */ | 148 | */ |
149 | static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) | 149 | static int |
150 | dasd_format(struct dasd_block *block, struct format_data_t *fdata) | ||
150 | { | 151 | { |
151 | struct dasd_ccw_req *cqr; | ||
152 | struct dasd_device *base; | 152 | struct dasd_device *base; |
153 | int rc; | 153 | int rc; |
154 | 154 | ||
@@ -157,8 +157,8 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) | |||
157 | return -EPERM; | 157 | return -EPERM; |
158 | 158 | ||
159 | if (base->state != DASD_STATE_BASIC) { | 159 | if (base->state != DASD_STATE_BASIC) { |
160 | pr_warning("%s: The DASD cannot be formatted while it is " | 160 | pr_warn("%s: The DASD cannot be formatted while it is enabled\n", |
161 | "enabled\n", dev_name(&base->cdev->dev)); | 161 | dev_name(&base->cdev->dev)); |
162 | return -EBUSY; | 162 | return -EBUSY; |
163 | } | 163 | } |
164 | 164 | ||
@@ -178,21 +178,10 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) | |||
178 | bdput(bdev); | 178 | bdput(bdev); |
179 | } | 179 | } |
180 | 180 | ||
181 | while (fdata->start_unit <= fdata->stop_unit) { | 181 | rc = base->discipline->format_device(base, fdata); |
182 | cqr = base->discipline->format_device(base, fdata); | 182 | if (rc) |
183 | if (IS_ERR(cqr)) | 183 | return rc; |
184 | return PTR_ERR(cqr); | 184 | |
185 | rc = dasd_sleep_on_interruptible(cqr); | ||
186 | dasd_sfree_request(cqr, cqr->memdev); | ||
187 | if (rc) { | ||
188 | if (rc != -ERESTARTSYS) | ||
189 | pr_err("%s: Formatting unit %d failed with " | ||
190 | "rc=%d\n", dev_name(&base->cdev->dev), | ||
191 | fdata->start_unit, rc); | ||
192 | return rc; | ||
193 | } | ||
194 | fdata->start_unit++; | ||
195 | } | ||
196 | return 0; | 185 | return 0; |
197 | } | 186 | } |
198 | 187 | ||