Loading drivers/s390/block/dasd.c +99 −10 Original line number Diff line number Diff line Loading @@ -246,7 +246,7 @@ static struct dentry *dasd_debugfs_setup(const char *name, static int dasd_state_known_to_basic(struct dasd_device *device) { struct dasd_block *block = device->block; int rc; int rc = 0; /* Allocate and register gendisk structure. */ if (block) { Loading @@ -273,7 +273,8 @@ static int dasd_state_known_to_basic(struct dasd_device *device) DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created"); device->state = DASD_STATE_BASIC; return 0; return rc; } /* Loading @@ -282,6 +283,7 @@ static int dasd_state_known_to_basic(struct dasd_device *device) static int dasd_state_basic_to_known(struct dasd_device *device) { int rc; if (device->block) { dasd_profile_exit(&device->block->profile); if (device->block->debugfs_dentry) Loading Loading @@ -332,8 +334,10 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) if (block->base->discipline->do_analysis != NULL) rc = block->base->discipline->do_analysis(block); if (rc) { if (rc != -EAGAIN) if (rc != -EAGAIN) { device->state = DASD_STATE_UNFMT; goto out; } return rc; } dasd_setup_queue(block); Loading @@ -341,11 +345,16 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) block->blocks << block->s2b_shift); device->state = DASD_STATE_READY; rc = dasd_scan_partitions(block); if (rc) if (rc) { device->state = DASD_STATE_BASIC; return rc; } } else { device->state = DASD_STATE_READY; } out: if (device->discipline->basic_to_ready) rc = device->discipline->basic_to_ready(device); return rc; } Loading @@ -368,6 +377,11 @@ static int dasd_state_ready_to_basic(struct dasd_device *device) { int rc; if (device->discipline->ready_to_basic) { rc = device->discipline->ready_to_basic(device); if (rc) return rc; } device->state = DASD_STATE_BASIC; if (device->block) { struct dasd_block *block = device->block; Loading Loading @@ -402,16 +416,10 @@ static int dasd_state_unfmt_to_basic(struct dasd_device *device) static int dasd_state_ready_to_online(struct dasd_device * device) { int rc; struct gendisk *disk; struct disk_part_iter piter; struct hd_struct *part; if (device->discipline->ready_to_online) { rc = device->discipline->ready_to_online(device); if (rc) return rc; } device->state = DASD_STATE_ONLINE; if (device->block) { dasd_schedule_block_bh(device->block); Loading Loading @@ -444,6 +452,7 @@ static int dasd_state_online_to_ready(struct dasd_device *device) if (rc) return rc; } device->state = DASD_STATE_READY; if (device->block && !(device->features & DASD_FEATURE_USERAW)) { disk = device->block->bdev->bd_disk; Loading Loading @@ -2223,6 +2232,77 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) return rc; } static inline int _wait_for_wakeup_queue(struct list_head *ccw_queue) { struct dasd_ccw_req *cqr; list_for_each_entry(cqr, ccw_queue, blocklist) { if (cqr->callback_data != DASD_SLEEPON_END_TAG) return 0; } return 1; } static int _dasd_sleep_on_queue(struct list_head *ccw_queue, int interruptible) { struct dasd_device *device; int rc; struct dasd_ccw_req *cqr, *n; retry: list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { device = cqr->startdev; if (cqr->status != DASD_CQR_FILLED) /*could be failed*/ continue; if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags) && !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { cqr->status = DASD_CQR_FAILED; cqr->intrc = -EPERM; continue; } /*Non-temporary stop condition will trigger fail fast*/ if (device->stopped & ~DASD_STOPPED_PENDING && test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && !dasd_eer_enabled(device)) { cqr->status = DASD_CQR_FAILED; cqr->intrc = -EAGAIN; continue; } /*Don't try to start requests if device is stopped*/ if (interruptible) { rc = wait_event_interruptible( generic_waitq, !device->stopped); if (rc == -ERESTARTSYS) { cqr->status = DASD_CQR_FAILED; cqr->intrc = rc; continue; } } else wait_event(generic_waitq, !(device->stopped)); if (!cqr->callback) cqr->callback = dasd_wakeup_cb; cqr->callback_data = DASD_SLEEPON_START_TAG; dasd_add_request_tail(cqr); } wait_event(generic_waitq, _wait_for_wakeup_queue(ccw_queue)); rc = 0; list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { if (__dasd_sleep_on_erp(cqr)) rc = 1; } if (rc) goto retry; return 0; } /* * Queue a request to the tail of the device ccw_queue and wait for * it's completion. Loading @@ -2232,6 +2312,15 @@ int dasd_sleep_on(struct dasd_ccw_req *cqr) return _dasd_sleep_on(cqr, 0); } /* * Start requests from a ccw_queue and wait for their completion. */ int dasd_sleep_on_queue(struct list_head *ccw_queue) { return _dasd_sleep_on_queue(ccw_queue, 0); } EXPORT_SYMBOL(dasd_sleep_on_queue); /* * Queue a request to the tail of the device ccw_queue and wait * interruptible for it's completion. Loading drivers/s390/block/dasd_eckd.c +236 −108 Original line number Diff line number Diff line Loading @@ -2022,7 +2022,7 @@ static int dasd_eckd_do_analysis(struct dasd_block *block) return dasd_eckd_end_analysis(block); } static int dasd_eckd_ready_to_online(struct dasd_device *device) static int dasd_eckd_basic_to_ready(struct dasd_device *device) { return dasd_alias_add_device(device); }; Loading @@ -2031,6 +2031,11 @@ static int dasd_eckd_online_to_ready(struct dasd_device *device) { cancel_work_sync(&device->reload_device); cancel_work_sync(&device->kick_validate); return 0; }; static int dasd_eckd_ready_to_basic(struct dasd_device *device) { return dasd_alias_remove_device(device); }; Loading @@ -2050,45 +2055,34 @@ dasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) } static struct dasd_ccw_req * dasd_eckd_format_device(struct dasd_device * device, dasd_eckd_build_format(struct dasd_device *base, struct format_data_t *fdata) { struct dasd_eckd_private *private; struct dasd_eckd_private *base_priv; struct dasd_eckd_private *start_priv; struct dasd_device *startdev; struct dasd_ccw_req *fcp; struct eckd_count *ect; struct ch_t address; struct ccw1 *ccw; void *data; int rpt; struct ch_t address; int cplength, datasize; int i; int i, j; int intensity = 0; int r0_perm; int nr_tracks; private = (struct dasd_eckd_private *) device->private; rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize); set_ch_t(&address, fdata->start_unit / private->rdc_data.trk_per_cyl, fdata->start_unit % private->rdc_data.trk_per_cyl); startdev = dasd_alias_get_start_dev(base); if (!startdev) startdev = base; /* Sanity checks. */ if (fdata->start_unit >= (private->real_cyl * private->rdc_data.trk_per_cyl)) { dev_warn(&device->cdev->dev, "Start track number %d used in " "formatting is too big\n", fdata->start_unit); return ERR_PTR(-EINVAL); } if (fdata->start_unit > fdata->stop_unit) { dev_warn(&device->cdev->dev, "Start track %d used in " "formatting exceeds end track\n", fdata->start_unit); return ERR_PTR(-EINVAL); } if (dasd_check_blocksize(fdata->blksize) != 0) { dev_warn(&device->cdev->dev, "The DASD cannot be formatted with block size %d\n", fdata->blksize); return ERR_PTR(-EINVAL); } start_priv = (struct dasd_eckd_private *) startdev->private; base_priv = (struct dasd_eckd_private *) base->private; rpt = recs_per_track(&base_priv->rdc_data, 0, fdata->blksize); nr_tracks = fdata->stop_unit - fdata->start_unit + 1; /* * fdata->intensity is a bit string that tells us what to do: Loading @@ -2106,83 +2100,96 @@ dasd_eckd_format_device(struct dasd_device * device, r0_perm = 1; intensity = fdata->intensity; } switch (intensity) { case 0x00: /* Normal format */ case 0x08: /* Normal format, use cdl. */ cplength = 2 + rpt; datasize = sizeof(struct DE_eckd_data) + cplength = 2 + (rpt*nr_tracks); datasize = sizeof(struct PFX_eckd_data) + sizeof(struct LO_eckd_data) + rpt * sizeof(struct eckd_count); rpt * nr_tracks * sizeof(struct eckd_count); break; case 0x01: /* Write record zero and format track. */ case 0x09: /* Write record zero and format track, use cdl. */ cplength = 3 + rpt; datasize = sizeof(struct DE_eckd_data) + cplength = 2 + rpt * nr_tracks; datasize = sizeof(struct PFX_eckd_data) + sizeof(struct LO_eckd_data) + sizeof(struct eckd_count) + rpt * sizeof(struct eckd_count); rpt * nr_tracks * sizeof(struct eckd_count); break; case 0x04: /* Invalidate track. */ case 0x0c: /* Invalidate track, use cdl. */ cplength = 3; datasize = sizeof(struct DE_eckd_data) + datasize = sizeof(struct PFX_eckd_data) + sizeof(struct LO_eckd_data) + sizeof(struct eckd_count); break; default: dev_warn(&device->cdev->dev, "An I/O control call used " "incorrect flags 0x%x\n", fdata->intensity); dev_warn(&startdev->cdev->dev, "An I/O control call used incorrect flags 0x%x\n", fdata->intensity); return ERR_PTR(-EINVAL); } /* Allocate the format ccw request. */ fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device); fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev); if (IS_ERR(fcp)) return fcp; start_priv->count++; data = fcp->data; ccw = fcp->cpaddr; switch (intensity & ~0x08) { case 0x00: /* Normal format. */ define_extent(ccw++, (struct DE_eckd_data *) data, fdata->start_unit, fdata->start_unit, DASD_ECKD_CCW_WRITE_CKD, device); prefix(ccw++, (struct PFX_eckd_data *) data, fdata->start_unit, fdata->stop_unit, DASD_ECKD_CCW_WRITE_CKD, base, startdev); /* grant subsystem permission to format R0 */ if (r0_perm) ((struct DE_eckd_data *)data)->ga_extended |= 0x04; data += sizeof(struct DE_eckd_data); ((struct PFX_eckd_data *)data) ->define_extent.ga_extended |= 0x04; data += sizeof(struct PFX_eckd_data); ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, (struct LO_eckd_data *) data, fdata->start_unit, 0, rpt, DASD_ECKD_CCW_WRITE_CKD, device, fdata->start_unit, 0, rpt*nr_tracks, DASD_ECKD_CCW_WRITE_CKD, base, fdata->blksize); data += sizeof(struct LO_eckd_data); break; case 0x01: /* Write record zero + format track. */ define_extent(ccw++, (struct DE_eckd_data *) data, fdata->start_unit, fdata->start_unit, prefix(ccw++, (struct PFX_eckd_data *) data, fdata->start_unit, fdata->stop_unit, DASD_ECKD_CCW_WRITE_RECORD_ZERO, device); data += sizeof(struct DE_eckd_data); base, startdev); data += sizeof(struct PFX_eckd_data); ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, (struct LO_eckd_data *) data, fdata->start_unit, 0, rpt + 1, DASD_ECKD_CCW_WRITE_RECORD_ZERO, device, device->block->bp_block); fdata->start_unit, 0, rpt * nr_tracks + 1, DASD_ECKD_CCW_WRITE_RECORD_ZERO, base, base->block->bp_block); data += sizeof(struct LO_eckd_data); break; case 0x04: /* Invalidate track. */ define_extent(ccw++, (struct DE_eckd_data *) data, fdata->start_unit, fdata->start_unit, DASD_ECKD_CCW_WRITE_CKD, device); data += sizeof(struct DE_eckd_data); prefix(ccw++, (struct PFX_eckd_data *) data, fdata->start_unit, fdata->stop_unit, DASD_ECKD_CCW_WRITE_CKD, base, startdev); data += sizeof(struct PFX_eckd_data); ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, (struct LO_eckd_data *) data, fdata->start_unit, 0, 1, DASD_ECKD_CCW_WRITE_CKD, device, 8); DASD_ECKD_CCW_WRITE_CKD, base, 8); data += sizeof(struct LO_eckd_data); break; } for (j = 0; j < nr_tracks; j++) { /* calculate cylinder and head for the current track */ set_ch_t(&address, (fdata->start_unit + j) / base_priv->rdc_data.trk_per_cyl, (fdata->start_unit + j) % base_priv->rdc_data.trk_per_cyl); if (intensity & 0x01) { /* write record zero */ ect = (struct eckd_count *) data; data += sizeof(struct eckd_count); Loading Loading @@ -2220,7 +2227,10 @@ dasd_eckd_format_device(struct dasd_device * device, ect->record = i + 1; ect->kl = 0; ect->dl = fdata->blksize; /* Check for special tracks 0-1 when formatting CDL */ /* * Check for special tracks 0-1 * when formatting CDL */ if ((intensity & 0x08) && fdata->start_unit == 0) { if (i < 3) { Loading @@ -2234,21 +2244,138 @@ dasd_eckd_format_device(struct dasd_device * device, ect->dl = LABEL_SIZE - 44; } ccw[-1].flags |= CCW_FLAG_CC; ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; if (i != 0 || j == 0) ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; else ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD_MT; ccw->flags = CCW_FLAG_SLI; ccw->count = 8; ccw->cda = (__u32)(addr_t) ect; ccw++; } } fcp->startdev = device; fcp->memdev = device; } fcp->startdev = startdev; fcp->memdev = startdev; fcp->retries = 256; fcp->expires = startdev->default_expires * HZ; fcp->buildclk = get_tod_clock(); fcp->status = DASD_CQR_FILLED; return fcp; } static int dasd_eckd_format_device(struct dasd_device *base, struct format_data_t *fdata) { struct dasd_ccw_req *cqr, *n; struct dasd_block *block; struct dasd_eckd_private *private; struct list_head format_queue; struct dasd_device *device; int old_stop, format_step; int step, rc = 0; block = base->block; private = (struct dasd_eckd_private *) base->private; /* Sanity checks. */ if (fdata->start_unit >= (private->real_cyl * private->rdc_data.trk_per_cyl)) { dev_warn(&base->cdev->dev, "Start track number %u used in formatting is too big\n", fdata->start_unit); return -EINVAL; } if (fdata->stop_unit >= (private->real_cyl * private->rdc_data.trk_per_cyl)) { dev_warn(&base->cdev->dev, "Stop track number %u used in formatting is too big\n", fdata->stop_unit); return -EINVAL; } if (fdata->start_unit > fdata->stop_unit) { dev_warn(&base->cdev->dev, "Start track %u used in formatting exceeds end track\n", fdata->start_unit); return -EINVAL; } if (dasd_check_blocksize(fdata->blksize) != 0) { dev_warn(&base->cdev->dev, "The DASD cannot be formatted with block size %u\n", fdata->blksize); return -EINVAL; } INIT_LIST_HEAD(&format_queue); old_stop = fdata->stop_unit; while (fdata->start_unit <= 1) { fdata->stop_unit = fdata->start_unit; cqr = dasd_eckd_build_format(base, fdata); list_add(&cqr->blocklist, &format_queue); fdata->stop_unit = old_stop; fdata->start_unit++; if (fdata->start_unit > fdata->stop_unit) goto sleep; } retry: format_step = 255 / recs_per_track(&private->rdc_data, 0, fdata->blksize); while (fdata->start_unit <= old_stop) { step = fdata->stop_unit - fdata->start_unit + 1; if (step > format_step) fdata->stop_unit = fdata->start_unit + format_step - 1; cqr = dasd_eckd_build_format(base, fdata); if (IS_ERR(cqr)) { if (PTR_ERR(cqr) == -ENOMEM) { /* * not enough memory available * go to out and start requests * retry after first requests were finished */ fdata->stop_unit = old_stop; goto sleep; } else return PTR_ERR(cqr); } list_add(&cqr->blocklist, &format_queue); fdata->start_unit = fdata->stop_unit + 1; fdata->stop_unit = old_stop; } sleep: dasd_sleep_on_queue(&format_queue); list_for_each_entry_safe(cqr, n, &format_queue, blocklist) { device = cqr->startdev; private = (struct dasd_eckd_private *) device->private; if (cqr->status == DASD_CQR_FAILED) rc = -EIO; list_del_init(&cqr->blocklist); dasd_sfree_request(cqr, device); private->count--; } /* * in case of ENOMEM we need to retry after * first requests are finished */ if (fdata->start_unit <= fdata->stop_unit) goto retry; return rc; } static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) { cqr->status = DASD_CQR_FILLED; Loading Loading @@ -4305,8 +4432,9 @@ static struct dasd_discipline dasd_eckd_discipline = { .uncheck_device = dasd_eckd_uncheck_device, .do_analysis = dasd_eckd_do_analysis, .verify_path = dasd_eckd_verify_path, .ready_to_online = dasd_eckd_ready_to_online, .basic_to_ready = dasd_eckd_basic_to_ready, .online_to_ready = dasd_eckd_online_to_ready, .ready_to_basic = dasd_eckd_ready_to_basic, .fill_geometry = dasd_eckd_fill_geometry, .start_IO = dasd_start_IO, .term_IO = dasd_term_IO, Loading drivers/s390/block/dasd_int.h +6 −4 Original line number Diff line number Diff line Loading @@ -300,10 +300,11 @@ struct dasd_discipline { * Last things to do when a device is set online, and first things * when it is set offline. */ int (*ready_to_online) (struct dasd_device *); int (*basic_to_ready) (struct dasd_device *); int (*online_to_ready) (struct dasd_device *); int (*ready_to_basic) (struct dasd_device *); /* /* (struct dasd_device *); * Device operation functions. build_cp creates a ccw chain for * a block device request, start_io starts the request and * term_IO cancels it (e.g. in case of a timeout). format_device Loading @@ -317,7 +318,7 @@ struct dasd_discipline { int (*start_IO) (struct dasd_ccw_req *); int (*term_IO) (struct dasd_ccw_req *); void (*handle_terminated_request) (struct dasd_ccw_req *); struct dasd_ccw_req *(*format_device) (struct dasd_device *, int (*format_device) (struct dasd_device *, struct format_data_t *); int (*free_cp) (struct dasd_ccw_req *, struct request *); Loading Loading @@ -672,6 +673,7 @@ int dasd_term_IO(struct dasd_ccw_req *); void dasd_schedule_device_bh(struct dasd_device *); void dasd_schedule_block_bh(struct dasd_block *); int dasd_sleep_on(struct dasd_ccw_req *); int dasd_sleep_on_queue(struct list_head *); int dasd_sleep_on_immediatly(struct dasd_ccw_req *); int dasd_sleep_on_interruptible(struct dasd_ccw_req *); void dasd_device_set_timer(struct dasd_device *, int); Loading drivers/s390/block/dasd_ioctl.c +10 −21 Original line number Diff line number Diff line Loading @@ -143,12 +143,12 @@ static int dasd_ioctl_resume(struct dasd_block *block) /* * performs formatting of _device_ according to _fdata_ * Note: The discipline's format_function is assumed to deliver formatting * commands to format a single unit of the device. In terms of the ECKD * devices this means CCWs are generated to format a single track. * commands to format multiple units of the device. In terms of the ECKD * devices this means CCWs are generated to format multiple tracks. */ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) { struct dasd_ccw_req *cqr; struct dasd_device *base; int rc; Loading @@ -157,8 +157,8 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) return -EPERM; if (base->state != DASD_STATE_BASIC) { pr_warning("%s: The DASD cannot be formatted while it is " "enabled\n", dev_name(&base->cdev->dev)); pr_warn("%s: The DASD cannot be formatted while it is enabled\n", dev_name(&base->cdev->dev)); return -EBUSY; } Loading @@ -178,21 +178,10 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) bdput(bdev); } while (fdata->start_unit <= fdata->stop_unit) { cqr = base->discipline->format_device(base, fdata); if (IS_ERR(cqr)) return PTR_ERR(cqr); rc = dasd_sleep_on_interruptible(cqr); dasd_sfree_request(cqr, cqr->memdev); if (rc) { if (rc != -ERESTARTSYS) pr_err("%s: Formatting unit %d failed with " "rc=%d\n", dev_name(&base->cdev->dev), fdata->start_unit, rc); rc = base->discipline->format_device(base, fdata); if (rc) return rc; } fdata->start_unit++; } return 0; } Loading Loading
drivers/s390/block/dasd.c +99 −10 Original line number Diff line number Diff line Loading @@ -246,7 +246,7 @@ static struct dentry *dasd_debugfs_setup(const char *name, static int dasd_state_known_to_basic(struct dasd_device *device) { struct dasd_block *block = device->block; int rc; int rc = 0; /* Allocate and register gendisk structure. */ if (block) { Loading @@ -273,7 +273,8 @@ static int dasd_state_known_to_basic(struct dasd_device *device) DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created"); device->state = DASD_STATE_BASIC; return 0; return rc; } /* Loading @@ -282,6 +283,7 @@ static int dasd_state_known_to_basic(struct dasd_device *device) static int dasd_state_basic_to_known(struct dasd_device *device) { int rc; if (device->block) { dasd_profile_exit(&device->block->profile); if (device->block->debugfs_dentry) Loading Loading @@ -332,8 +334,10 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) if (block->base->discipline->do_analysis != NULL) rc = block->base->discipline->do_analysis(block); if (rc) { if (rc != -EAGAIN) if (rc != -EAGAIN) { device->state = DASD_STATE_UNFMT; goto out; } return rc; } dasd_setup_queue(block); Loading @@ -341,11 +345,16 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) block->blocks << block->s2b_shift); device->state = DASD_STATE_READY; rc = dasd_scan_partitions(block); if (rc) if (rc) { device->state = DASD_STATE_BASIC; return rc; } } else { device->state = DASD_STATE_READY; } out: if (device->discipline->basic_to_ready) rc = device->discipline->basic_to_ready(device); return rc; } Loading @@ -368,6 +377,11 @@ static int dasd_state_ready_to_basic(struct dasd_device *device) { int rc; if (device->discipline->ready_to_basic) { rc = device->discipline->ready_to_basic(device); if (rc) return rc; } device->state = DASD_STATE_BASIC; if (device->block) { struct dasd_block *block = device->block; Loading Loading @@ -402,16 +416,10 @@ static int dasd_state_unfmt_to_basic(struct dasd_device *device) static int dasd_state_ready_to_online(struct dasd_device * device) { int rc; struct gendisk *disk; struct disk_part_iter piter; struct hd_struct *part; if (device->discipline->ready_to_online) { rc = device->discipline->ready_to_online(device); if (rc) return rc; } device->state = DASD_STATE_ONLINE; if (device->block) { dasd_schedule_block_bh(device->block); Loading Loading @@ -444,6 +452,7 @@ static int dasd_state_online_to_ready(struct dasd_device *device) if (rc) return rc; } device->state = DASD_STATE_READY; if (device->block && !(device->features & DASD_FEATURE_USERAW)) { disk = device->block->bdev->bd_disk; Loading Loading @@ -2223,6 +2232,77 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) return rc; } static inline int _wait_for_wakeup_queue(struct list_head *ccw_queue) { struct dasd_ccw_req *cqr; list_for_each_entry(cqr, ccw_queue, blocklist) { if (cqr->callback_data != DASD_SLEEPON_END_TAG) return 0; } return 1; } static int _dasd_sleep_on_queue(struct list_head *ccw_queue, int interruptible) { struct dasd_device *device; int rc; struct dasd_ccw_req *cqr, *n; retry: list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { device = cqr->startdev; if (cqr->status != DASD_CQR_FILLED) /*could be failed*/ continue; if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags) && !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { cqr->status = DASD_CQR_FAILED; cqr->intrc = -EPERM; continue; } /*Non-temporary stop condition will trigger fail fast*/ if (device->stopped & ~DASD_STOPPED_PENDING && test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && !dasd_eer_enabled(device)) { cqr->status = DASD_CQR_FAILED; cqr->intrc = -EAGAIN; continue; } /*Don't try to start requests if device is stopped*/ if (interruptible) { rc = wait_event_interruptible( generic_waitq, !device->stopped); if (rc == -ERESTARTSYS) { cqr->status = DASD_CQR_FAILED; cqr->intrc = rc; continue; } } else wait_event(generic_waitq, !(device->stopped)); if (!cqr->callback) cqr->callback = dasd_wakeup_cb; cqr->callback_data = DASD_SLEEPON_START_TAG; dasd_add_request_tail(cqr); } wait_event(generic_waitq, _wait_for_wakeup_queue(ccw_queue)); rc = 0; list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { if (__dasd_sleep_on_erp(cqr)) rc = 1; } if (rc) goto retry; return 0; } /* * Queue a request to the tail of the device ccw_queue and wait for * it's completion. Loading @@ -2232,6 +2312,15 @@ int dasd_sleep_on(struct dasd_ccw_req *cqr) return _dasd_sleep_on(cqr, 0); } /* * Start requests from a ccw_queue and wait for their completion. */ int dasd_sleep_on_queue(struct list_head *ccw_queue) { return _dasd_sleep_on_queue(ccw_queue, 0); } EXPORT_SYMBOL(dasd_sleep_on_queue); /* * Queue a request to the tail of the device ccw_queue and wait * interruptible for it's completion. Loading
drivers/s390/block/dasd_eckd.c +236 −108 Original line number Diff line number Diff line Loading @@ -2022,7 +2022,7 @@ static int dasd_eckd_do_analysis(struct dasd_block *block) return dasd_eckd_end_analysis(block); } static int dasd_eckd_ready_to_online(struct dasd_device *device) static int dasd_eckd_basic_to_ready(struct dasd_device *device) { return dasd_alias_add_device(device); }; Loading @@ -2031,6 +2031,11 @@ static int dasd_eckd_online_to_ready(struct dasd_device *device) { cancel_work_sync(&device->reload_device); cancel_work_sync(&device->kick_validate); return 0; }; static int dasd_eckd_ready_to_basic(struct dasd_device *device) { return dasd_alias_remove_device(device); }; Loading @@ -2050,45 +2055,34 @@ dasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) } static struct dasd_ccw_req * dasd_eckd_format_device(struct dasd_device * device, dasd_eckd_build_format(struct dasd_device *base, struct format_data_t *fdata) { struct dasd_eckd_private *private; struct dasd_eckd_private *base_priv; struct dasd_eckd_private *start_priv; struct dasd_device *startdev; struct dasd_ccw_req *fcp; struct eckd_count *ect; struct ch_t address; struct ccw1 *ccw; void *data; int rpt; struct ch_t address; int cplength, datasize; int i; int i, j; int intensity = 0; int r0_perm; int nr_tracks; private = (struct dasd_eckd_private *) device->private; rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize); set_ch_t(&address, fdata->start_unit / private->rdc_data.trk_per_cyl, fdata->start_unit % private->rdc_data.trk_per_cyl); startdev = dasd_alias_get_start_dev(base); if (!startdev) startdev = base; /* Sanity checks. */ if (fdata->start_unit >= (private->real_cyl * private->rdc_data.trk_per_cyl)) { dev_warn(&device->cdev->dev, "Start track number %d used in " "formatting is too big\n", fdata->start_unit); return ERR_PTR(-EINVAL); } if (fdata->start_unit > fdata->stop_unit) { dev_warn(&device->cdev->dev, "Start track %d used in " "formatting exceeds end track\n", fdata->start_unit); return ERR_PTR(-EINVAL); } if (dasd_check_blocksize(fdata->blksize) != 0) { dev_warn(&device->cdev->dev, "The DASD cannot be formatted with block size %d\n", fdata->blksize); return ERR_PTR(-EINVAL); } start_priv = (struct dasd_eckd_private *) startdev->private; base_priv = (struct dasd_eckd_private *) base->private; rpt = recs_per_track(&base_priv->rdc_data, 0, fdata->blksize); nr_tracks = fdata->stop_unit - fdata->start_unit + 1; /* * fdata->intensity is a bit string that tells us what to do: Loading @@ -2106,83 +2100,96 @@ dasd_eckd_format_device(struct dasd_device * device, r0_perm = 1; intensity = fdata->intensity; } switch (intensity) { case 0x00: /* Normal format */ case 0x08: /* Normal format, use cdl. */ cplength = 2 + rpt; datasize = sizeof(struct DE_eckd_data) + cplength = 2 + (rpt*nr_tracks); datasize = sizeof(struct PFX_eckd_data) + sizeof(struct LO_eckd_data) + rpt * sizeof(struct eckd_count); rpt * nr_tracks * sizeof(struct eckd_count); break; case 0x01: /* Write record zero and format track. */ case 0x09: /* Write record zero and format track, use cdl. */ cplength = 3 + rpt; datasize = sizeof(struct DE_eckd_data) + cplength = 2 + rpt * nr_tracks; datasize = sizeof(struct PFX_eckd_data) + sizeof(struct LO_eckd_data) + sizeof(struct eckd_count) + rpt * sizeof(struct eckd_count); rpt * nr_tracks * sizeof(struct eckd_count); break; case 0x04: /* Invalidate track. */ case 0x0c: /* Invalidate track, use cdl. */ cplength = 3; datasize = sizeof(struct DE_eckd_data) + datasize = sizeof(struct PFX_eckd_data) + sizeof(struct LO_eckd_data) + sizeof(struct eckd_count); break; default: dev_warn(&device->cdev->dev, "An I/O control call used " "incorrect flags 0x%x\n", fdata->intensity); dev_warn(&startdev->cdev->dev, "An I/O control call used incorrect flags 0x%x\n", fdata->intensity); return ERR_PTR(-EINVAL); } /* Allocate the format ccw request. */ fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device); fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev); if (IS_ERR(fcp)) return fcp; start_priv->count++; data = fcp->data; ccw = fcp->cpaddr; switch (intensity & ~0x08) { case 0x00: /* Normal format. */ define_extent(ccw++, (struct DE_eckd_data *) data, fdata->start_unit, fdata->start_unit, DASD_ECKD_CCW_WRITE_CKD, device); prefix(ccw++, (struct PFX_eckd_data *) data, fdata->start_unit, fdata->stop_unit, DASD_ECKD_CCW_WRITE_CKD, base, startdev); /* grant subsystem permission to format R0 */ if (r0_perm) ((struct DE_eckd_data *)data)->ga_extended |= 0x04; data += sizeof(struct DE_eckd_data); ((struct PFX_eckd_data *)data) ->define_extent.ga_extended |= 0x04; data += sizeof(struct PFX_eckd_data); ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, (struct LO_eckd_data *) data, fdata->start_unit, 0, rpt, DASD_ECKD_CCW_WRITE_CKD, device, fdata->start_unit, 0, rpt*nr_tracks, DASD_ECKD_CCW_WRITE_CKD, base, fdata->blksize); data += sizeof(struct LO_eckd_data); break; case 0x01: /* Write record zero + format track. */ define_extent(ccw++, (struct DE_eckd_data *) data, fdata->start_unit, fdata->start_unit, prefix(ccw++, (struct PFX_eckd_data *) data, fdata->start_unit, fdata->stop_unit, DASD_ECKD_CCW_WRITE_RECORD_ZERO, device); data += sizeof(struct DE_eckd_data); base, startdev); data += sizeof(struct PFX_eckd_data); ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, (struct LO_eckd_data *) data, fdata->start_unit, 0, rpt + 1, DASD_ECKD_CCW_WRITE_RECORD_ZERO, device, device->block->bp_block); fdata->start_unit, 0, rpt * nr_tracks + 1, DASD_ECKD_CCW_WRITE_RECORD_ZERO, base, base->block->bp_block); data += sizeof(struct LO_eckd_data); break; case 0x04: /* Invalidate track. */ define_extent(ccw++, (struct DE_eckd_data *) data, fdata->start_unit, fdata->start_unit, DASD_ECKD_CCW_WRITE_CKD, device); data += sizeof(struct DE_eckd_data); prefix(ccw++, (struct PFX_eckd_data *) data, fdata->start_unit, fdata->stop_unit, DASD_ECKD_CCW_WRITE_CKD, base, startdev); data += sizeof(struct PFX_eckd_data); ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, (struct LO_eckd_data *) data, fdata->start_unit, 0, 1, DASD_ECKD_CCW_WRITE_CKD, device, 8); DASD_ECKD_CCW_WRITE_CKD, base, 8); data += sizeof(struct LO_eckd_data); break; } for (j = 0; j < nr_tracks; j++) { /* calculate cylinder and head for the current track */ set_ch_t(&address, (fdata->start_unit + j) / base_priv->rdc_data.trk_per_cyl, (fdata->start_unit + j) % base_priv->rdc_data.trk_per_cyl); if (intensity & 0x01) { /* write record zero */ ect = (struct eckd_count *) data; data += sizeof(struct eckd_count); Loading Loading @@ -2220,7 +2227,10 @@ dasd_eckd_format_device(struct dasd_device * device, ect->record = i + 1; ect->kl = 0; ect->dl = fdata->blksize; /* Check for special tracks 0-1 when formatting CDL */ /* * Check for special tracks 0-1 * when formatting CDL */ if ((intensity & 0x08) && fdata->start_unit == 0) { if (i < 3) { Loading @@ -2234,21 +2244,138 @@ dasd_eckd_format_device(struct dasd_device * device, ect->dl = LABEL_SIZE - 44; } ccw[-1].flags |= CCW_FLAG_CC; ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; if (i != 0 || j == 0) ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD; else ccw->cmd_code = DASD_ECKD_CCW_WRITE_CKD_MT; ccw->flags = CCW_FLAG_SLI; ccw->count = 8; ccw->cda = (__u32)(addr_t) ect; ccw++; } } fcp->startdev = device; fcp->memdev = device; } fcp->startdev = startdev; fcp->memdev = startdev; fcp->retries = 256; fcp->expires = startdev->default_expires * HZ; fcp->buildclk = get_tod_clock(); fcp->status = DASD_CQR_FILLED; return fcp; } static int dasd_eckd_format_device(struct dasd_device *base, struct format_data_t *fdata) { struct dasd_ccw_req *cqr, *n; struct dasd_block *block; struct dasd_eckd_private *private; struct list_head format_queue; struct dasd_device *device; int old_stop, format_step; int step, rc = 0; block = base->block; private = (struct dasd_eckd_private *) base->private; /* Sanity checks. */ if (fdata->start_unit >= (private->real_cyl * private->rdc_data.trk_per_cyl)) { dev_warn(&base->cdev->dev, "Start track number %u used in formatting is too big\n", fdata->start_unit); return -EINVAL; } if (fdata->stop_unit >= (private->real_cyl * private->rdc_data.trk_per_cyl)) { dev_warn(&base->cdev->dev, "Stop track number %u used in formatting is too big\n", fdata->stop_unit); return -EINVAL; } if (fdata->start_unit > fdata->stop_unit) { dev_warn(&base->cdev->dev, "Start track %u used in formatting exceeds end track\n", fdata->start_unit); return -EINVAL; } if (dasd_check_blocksize(fdata->blksize) != 0) { dev_warn(&base->cdev->dev, "The DASD cannot be formatted with block size %u\n", fdata->blksize); return -EINVAL; } INIT_LIST_HEAD(&format_queue); old_stop = fdata->stop_unit; while (fdata->start_unit <= 1) { fdata->stop_unit = fdata->start_unit; cqr = dasd_eckd_build_format(base, fdata); list_add(&cqr->blocklist, &format_queue); fdata->stop_unit = old_stop; fdata->start_unit++; if (fdata->start_unit > fdata->stop_unit) goto sleep; } retry: format_step = 255 / recs_per_track(&private->rdc_data, 0, fdata->blksize); while (fdata->start_unit <= old_stop) { step = fdata->stop_unit - fdata->start_unit + 1; if (step > format_step) fdata->stop_unit = fdata->start_unit + format_step - 1; cqr = dasd_eckd_build_format(base, fdata); if (IS_ERR(cqr)) { if (PTR_ERR(cqr) == -ENOMEM) { /* * not enough memory available * go to out and start requests * retry after first requests were finished */ fdata->stop_unit = old_stop; goto sleep; } else return PTR_ERR(cqr); } list_add(&cqr->blocklist, &format_queue); fdata->start_unit = fdata->stop_unit + 1; fdata->stop_unit = old_stop; } sleep: dasd_sleep_on_queue(&format_queue); list_for_each_entry_safe(cqr, n, &format_queue, blocklist) { device = cqr->startdev; private = (struct dasd_eckd_private *) device->private; if (cqr->status == DASD_CQR_FAILED) rc = -EIO; list_del_init(&cqr->blocklist); dasd_sfree_request(cqr, device); private->count--; } /* * in case of ENOMEM we need to retry after * first requests are finished */ if (fdata->start_unit <= fdata->stop_unit) goto retry; return rc; } static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) { cqr->status = DASD_CQR_FILLED; Loading Loading @@ -4305,8 +4432,9 @@ static struct dasd_discipline dasd_eckd_discipline = { .uncheck_device = dasd_eckd_uncheck_device, .do_analysis = dasd_eckd_do_analysis, .verify_path = dasd_eckd_verify_path, .ready_to_online = dasd_eckd_ready_to_online, .basic_to_ready = dasd_eckd_basic_to_ready, .online_to_ready = dasd_eckd_online_to_ready, .ready_to_basic = dasd_eckd_ready_to_basic, .fill_geometry = dasd_eckd_fill_geometry, .start_IO = dasd_start_IO, .term_IO = dasd_term_IO, Loading
drivers/s390/block/dasd_int.h +6 −4 Original line number Diff line number Diff line Loading @@ -300,10 +300,11 @@ struct dasd_discipline { * Last things to do when a device is set online, and first things * when it is set offline. */ int (*ready_to_online) (struct dasd_device *); int (*basic_to_ready) (struct dasd_device *); int (*online_to_ready) (struct dasd_device *); int (*ready_to_basic) (struct dasd_device *); /* /* (struct dasd_device *); * Device operation functions. build_cp creates a ccw chain for * a block device request, start_io starts the request and * term_IO cancels it (e.g. in case of a timeout). format_device Loading @@ -317,7 +318,7 @@ struct dasd_discipline { int (*start_IO) (struct dasd_ccw_req *); int (*term_IO) (struct dasd_ccw_req *); void (*handle_terminated_request) (struct dasd_ccw_req *); struct dasd_ccw_req *(*format_device) (struct dasd_device *, int (*format_device) (struct dasd_device *, struct format_data_t *); int (*free_cp) (struct dasd_ccw_req *, struct request *); Loading Loading @@ -672,6 +673,7 @@ int dasd_term_IO(struct dasd_ccw_req *); void dasd_schedule_device_bh(struct dasd_device *); void dasd_schedule_block_bh(struct dasd_block *); int dasd_sleep_on(struct dasd_ccw_req *); int dasd_sleep_on_queue(struct list_head *); int dasd_sleep_on_immediatly(struct dasd_ccw_req *); int dasd_sleep_on_interruptible(struct dasd_ccw_req *); void dasd_device_set_timer(struct dasd_device *, int); Loading
drivers/s390/block/dasd_ioctl.c +10 −21 Original line number Diff line number Diff line Loading @@ -143,12 +143,12 @@ static int dasd_ioctl_resume(struct dasd_block *block) /* * performs formatting of _device_ according to _fdata_ * Note: The discipline's format_function is assumed to deliver formatting * commands to format a single unit of the device. In terms of the ECKD * devices this means CCWs are generated to format a single track. * commands to format multiple units of the device. In terms of the ECKD * devices this means CCWs are generated to format multiple tracks. */ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) { struct dasd_ccw_req *cqr; struct dasd_device *base; int rc; Loading @@ -157,8 +157,8 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) return -EPERM; if (base->state != DASD_STATE_BASIC) { pr_warning("%s: The DASD cannot be formatted while it is " "enabled\n", dev_name(&base->cdev->dev)); pr_warn("%s: The DASD cannot be formatted while it is enabled\n", dev_name(&base->cdev->dev)); return -EBUSY; } Loading @@ -178,21 +178,10 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) bdput(bdev); } while (fdata->start_unit <= fdata->stop_unit) { cqr = base->discipline->format_device(base, fdata); if (IS_ERR(cqr)) return PTR_ERR(cqr); rc = dasd_sleep_on_interruptible(cqr); dasd_sfree_request(cqr, cqr->memdev); if (rc) { if (rc != -ERESTARTSYS) pr_err("%s: Formatting unit %d failed with " "rc=%d\n", dev_name(&base->cdev->dev), fdata->start_unit, rc); rc = base->discipline->format_device(base, fdata); if (rc) return rc; } fdata->start_unit++; } return 0; } Loading