/* * raw track access always map to 64k in memory * so it maps to 16 blocks of 4k per track
*/ #define DASD_RAW_BLOCK_PER_TRACK 16 #define DASD_RAW_BLOCKSIZE 4096 /* 64k are 128 x 512 byte sectors */ #define DASD_RAW_SECTORS_PER_TRACK 128
/* initial attempt at a probe function. this can be simplified once
* the other detection code is gone */ staticint
dasd_eckd_probe (struct ccw_device *cdev)
{ int ret;
/* set ECKD specific ccw-device options */
ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE |
CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH); if (ret) {
DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "dasd_eckd_probe: could not set " "ccw-device options"); return ret;
}
ret = dasd_generic_probe(cdev); return ret;
}
/* * calculate failing track from sense data depending if * it is an EAV device or not
*/ staticint dasd_eckd_track_from_irb(struct irb *irb, struct dasd_device *device,
sector_t *track)
{ struct dasd_eckd_private *private = device->private;
u8 *sense = NULL;
u32 cyl;
u8 head;
sense = dasd_get_sense(irb); if (!sense) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s", "ESE error no sense data\n"); return -EINVAL;
} if (!(sense[27] & DASD_SENSE_BIT_2)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s", "ESE error no valid track data\n"); return -EINVAL;
}
rc = get_phys_clock(&data->ep_sys_time); /* * Ignore return code if XRC is not supported or * sync clock is switched off
*/ if ((rc && !private->rdc_data.facilities.XRC_supported) ||
rc == -EOPNOTSUPP || rc == -EACCES) return 0;
/* switch on System Time Stamp - needed for XRC Support */
data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */
data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
/* * For some commands the System Time Stamp is set in the define extent * data when XRC is supported. The validity of the time stamp must be * reflected in the prefix data as well.
*/ if (dedata->ga_extended & 0x08 && dedata->ga_extended & 0x02)
pfxdata->validity.time_stamp = 1; /* 'Time Stamp Valid' */
memset(data, 0, sizeof(struct LO_eckd_data));
sector = 0; if (rec_on_trk) { switch (private->rdc_data.dev_type) { case 0x3390:
dn = ceil_quot(reclen + 6, 232);
d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34);
sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8; break; case 0x3380:
d = 7 + ceil_quot(reclen + 12, 32);
sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7; break;
}
}
data->sector = sector;
data->count = no_rec; switch (cmd) { case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
data->operation.orientation = 0x3;
data->operation.operation = 0x03; break; case DASD_ECKD_CCW_READ_HOME_ADDRESS:
data->operation.orientation = 0x3;
data->operation.operation = 0x16; break; case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
data->operation.orientation = 0x1;
data->operation.operation = 0x03;
data->count++; break; case DASD_ECKD_CCW_READ_RECORD_ZERO:
data->operation.orientation = 0x3;
data->operation.operation = 0x16;
data->count++; break; case DASD_ECKD_CCW_WRITE: case DASD_ECKD_CCW_WRITE_MT: case DASD_ECKD_CCW_WRITE_KD: case DASD_ECKD_CCW_WRITE_KD_MT:
data->auxiliary.last_bytes_used = 0x1;
data->length = reclen;
data->operation.operation = 0x01; break; case DASD_ECKD_CCW_WRITE_CKD: case DASD_ECKD_CCW_WRITE_CKD_MT:
data->auxiliary.last_bytes_used = 0x1;
data->length = reclen;
data->operation.operation = 0x03; break; case DASD_ECKD_CCW_READ: case DASD_ECKD_CCW_READ_MT: case DASD_ECKD_CCW_READ_KD: case DASD_ECKD_CCW_READ_KD_MT:
data->auxiliary.last_bytes_used = 0x1;
data->length = reclen;
data->operation.operation = 0x06; break; case DASD_ECKD_CCW_READ_CKD: case DASD_ECKD_CCW_READ_CKD_MT:
data->auxiliary.last_bytes_used = 0x1;
data->length = reclen;
data->operation.operation = 0x16; break; case DASD_ECKD_CCW_READ_COUNT:
data->operation.operation = 0x06; break; case DASD_ECKD_CCW_ERASE:
data->length = reclen;
data->auxiliary.last_bytes_used = 0x1;
data->operation.operation = 0x0b; break; default:
DBF_DEV_EVENT(DBF_ERR, device, "unknown locate record " "opcode 0x%x", cmd);
}
set_ch_t(&data->seek_addr,
trk / private->rdc_data.trk_per_cyl,
trk % private->rdc_data.trk_per_cyl);
data->search_arg.cyl = data->seek_addr.cyl;
data->search_arg.head = data->seek_addr.head;
data->search_arg.record = rec_on_trk;
}
/* * Returns 1 if the block is one of the special blocks that needs * to get read/written with the KD variant of the command. * That is DASD_ECKD_READ_KD_MT instead of DASD_ECKD_READ_MT and * DASD_ECKD_WRITE_KD_MT instead of DASD_ECKD_WRITE_MT. * Luckily the KD variants differ only by one bit (0x08) from the * normal variant. So don't wonder about code like: * if (dasd_eckd_cdl_special(blk_per_trk, recid)) * ccw->cmd_code |= 0x8;
*/ staticinlineint
dasd_eckd_cdl_special(int blk_per_trk, int recid)
{ if (recid < 3) return 1; if (recid < blk_per_trk) return 0; if (recid < 2 * blk_per_trk) return 1; return 0;
}
/* * Returns the record size for the special blocks of the cdl format. * Only returns something useful if dasd_eckd_cdl_special is true * for the recid.
*/ staticinlineint
dasd_eckd_cdl_reclen(int recid)
{ if (recid < 3) return sizes_trk0[recid]; return LABEL_SIZE;
} /* create unique id from private structure. */ staticvoid create_uid(struct dasd_conf *conf, struct dasd_uid *uid)
{ int count;
/* * compare device UID with data of a given dasd_eckd_private structure * return 0 for match
*/ staticint dasd_eckd_compare_path_uid(struct dasd_device *device, struct dasd_conf *path_conf)
{ struct dasd_uid device_uid; struct dasd_uid path_uid;
/* * Wakeup helper for read_conf * if the cqr is not done and needs some error recovery * the buffer has to be re-initialized with the EBCDIC "V1.0" * to show support for virtual device SNEQ
*/ staticvoid read_conf_cb(struct dasd_ccw_req *cqr, void *data)
{ struct ccw1 *ccw;
__u8 *rcd_buffer;
/* * sanity check: scan for RCD command in extended SenseID data * some devices do not support RCD
*/
ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD); if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) {
ret = -EOPNOTSUPP; goto out_error;
}
rcd_buf = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL | GFP_DMA); if (!rcd_buf) {
ret = -ENOMEM; goto out_error;
}
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */,
0, /* use rcd_buf as data ara */
device, NULL); if (IS_ERR(cqr)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s", "Could not allocate RCD request");
ret = -ENOMEM; goto out_error;
}
dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buf, lpm);
cqr->callback = read_conf_cb;
ret = dasd_sleep_on(cqr); /* * on success we update the user input parms
*/
dasd_sfree_request(cqr, cqr->memdev); if (ret) goto out_error;
/* * path handling and read_conf allocate data * free it before replacing the pointer * also replace the old private->conf_data pointer * with the new one if this points to the same data
*/
cdp = device->path[chp].conf_data; if (private->conf.data == cdp) {
private->conf.data = (void *)conf_data;
dasd_eckd_identify_conf_parts(&private->conf);
}
ccw_device_get_schid(device->cdev, &sch_id);
device->path[chp].conf_data = conf_data;
device->path[chp].cssid = sch_id.cssid;
device->path[chp].ssid = sch_id.ssid;
chp_desc = ccw_device_get_chp_desc(device->cdev, chp); if (chp_desc)
device->path[chp].chpid = chp_desc->chpid;
kfree(chp_desc);
kfree(cdp);
}
path_conf.data = conf_data;
path_conf.len = DASD_ECKD_RCD_DATA_SIZE; if (dasd_eckd_identify_conf_parts(&path_conf)) return 1;
if (dasd_eckd_compare_path_uid(device, &path_conf)) {
dasd_eckd_get_uid_string(&path_conf, print_path_uid);
dasd_eckd_get_uid_string(&private->conf, print_device_uid);
dev_err(&device->cdev->dev, "Not all channel paths lead to the same device, path %02X leads to device %s instead of %s\n",
lpm, print_path_uid, print_device_uid); return 1;
}
return 0;
}
staticint dasd_eckd_read_conf(struct dasd_device *device)
{ void *conf_data; int conf_len, conf_data_saved; int rc, path_err, pos;
__u8 lpm, opm; struct dasd_eckd_private *private;
private = device->private;
opm = ccw_device_get_path_mask(device->cdev);
conf_data_saved = 0;
path_err = 0; /* get configuration data per operational path */ for (lpm = 0x80; lpm; lpm>>= 1) { if (!(lpm & opm)) continue;
rc = dasd_eckd_read_conf_lpm(device, &conf_data,
&conf_len, lpm); if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "Read configuration data returned " "error %d", rc); return rc;
} if (conf_data == NULL) {
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "No configuration data " "retrieved"); /* no further analysis possible */
dasd_path_add_opm(device, opm); continue; /* no error */
} /* save first valid configuration data */ if (!conf_data_saved) { /* initially clear previously stored conf_data */
dasd_eckd_clear_conf_data(device);
private->conf.data = conf_data;
private->conf.len = conf_len; if (dasd_eckd_identify_conf_parts(&private->conf)) {
private->conf.data = NULL;
private->conf.len = 0;
kfree(conf_data); continue;
} /* * build device UID that other path data * can be compared to it
*/
dasd_eckd_generate_uid(device);
conf_data_saved++;
} elseif (dasd_eckd_check_cabling(device, conf_data, lpm)) {
dasd_path_add_cablepm(device, lpm);
path_err = -EINVAL;
kfree(conf_data); continue;
}
if (private->fcx_max_data) {
mdc = ccw_device_get_mdc(device->cdev, lpm); if (mdc == 0) {
dev_warn(&device->cdev->dev, "Detecting the maximum data size for zHPF " "requests failed (rc=%d) for a new path %x\n",
mdc, lpm); return mdc;
}
fcx_max_data = (u32)mdc * FCX_MAX_DATA_FACTOR; if (fcx_max_data < private->fcx_max_data) {
dev_warn(&device->cdev->dev, "The maximum data size for zHPF requests %u " "on a new path %x is below the active maximum " "%u\n", fcx_max_data, lpm,
private->fcx_max_data); return -EACCES;
}
} return 0;
}
/* * save conf_data for comparison after * rebuild_device_uid may have changed * the original data
*/
memcpy(&path_rcd_buf, data->rcd_buffer,
DASD_ECKD_RCD_DATA_SIZE);
path_conf.data = (void *)&path_rcd_buf;
path_conf.len = DASD_ECKD_RCD_DATA_SIZE; if (dasd_eckd_identify_conf_parts(&path_conf)) {
path_conf.data = NULL;
path_conf.len = 0; continue;
}
/* * compare path UID with device UID only if at least * one valid path is left * in other case the device UID may have changed and * the first working path UID will be used as device UID
*/ if (dasd_path_get_opm(device) &&
dasd_eckd_compare_path_uid(device, &path_conf)) { /* * the comparison was not successful * rebuild the device UID with at least one * known path in case a z/VM hyperswap command * has changed the device * * after this compare again * * if either the rebuild or the recompare fails * the path can not be used
*/ if (rebuild_device_uid(device, data) ||
dasd_eckd_compare_path_uid(
device, &path_conf)) {
dasd_eckd_get_uid_string(&path_conf, print_uid);
dev_err(&device->cdev->dev, "The newly added channel path %02X " "will not be used because it leads " "to a different device %s\n",
lpm, print_uid);
opm &= ~lpm;
npm &= ~lpm;
ppm &= ~lpm;
cablepm |= lpm; continue;
}
}
conf_data = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL); if (conf_data) {
memcpy(conf_data, data->rcd_buffer,
DASD_ECKD_RCD_DATA_SIZE);
} else { /* * path is operational but path config data could not * be stored due to low mem condition * add it to the error path mask and schedule a path * verification later that this could be added again
*/
epm |= lpm;
}
pos = pathmask_to_pos(lpm);
dasd_eckd_store_conf_data(device, conf_data, pos);
/* * There is a small chance that a path is lost again between * above path verification and the following modification of * the device opm mask. We could avoid that race here by using * yet another path mask, but we rather deal with this unlikely * situation in dasd_start_IO.
*/
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); if (!dasd_path_get_opm(device) && opm) {
dasd_path_set_opm(device, opm);
dasd_generic_path_operational(device);
} else {
dasd_path_add_opm(device, opm);
}
dasd_path_add_nppm(device, npm);
dasd_path_add_ppm(device, ppm); if (epm) {
dasd_path_add_tbvpm(device, epm);
dasd_device_set_timer(device, 50);
}
dasd_path_add_cablepm(device, cablepm);
dasd_path_add_nohpfpm(device, hpfpm);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
data = container_of(work, struct pe_handler_work_data, worker);
device = data->device;
/* delay path verification until device was resumed */ if (test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
schedule_work(work); return;
} /* check if path verification already running and delay if so */ if (test_and_set_bit(DASD_FLAG_PATH_VERIFY, &device->flags)) {
schedule_work(work); return;
}
if (data->tbvpm)
dasd_eckd_path_available_action(device, data); if (data->fcsecpm)
dasd_eckd_read_fc_security(device);
clear_bit(DASD_FLAG_PATH_VERIFY, &device->flags);
dasd_put_device(device); if (data->isglobal)
mutex_unlock(&dasd_pe_handler_mutex); else
kfree(data);
}
/* Prepare for Read Subsystem Data */
prssdp = (struct dasd_psf_prssd_data *) cqr->data;
memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
prssdp->order = PSF_ORDER_PRSSD;
prssdp->suborder = 0x41; /* Read Feature Codes */ /* all other bytes of prssdp must be zero */
/* This command cannot be executed on an alias device */ if (private->uid.type == UA_BASE_PAV_ALIAS ||
private->uid.type == UA_HYPER_PAV_ALIAS) return 0;
/* * This value represents the total amount of available space. As more space is * allocated by ESE volumes, this value will decrease. * The data for this value is therefore updated on any call.
*/ staticint dasd_eckd_space_configured(struct dasd_device *device)
{ struct dasd_eckd_private *private = device->private; int rc;
rc = dasd_eckd_read_vol_info(device);
return rc ? : private->vsq.space_configured;
}
/* * The value of space allocated by an ESE volume may have changed and is * therefore updated on any call.
*/ staticint dasd_eckd_space_allocated(struct dasd_device *device)
{ struct dasd_eckd_private *private = device->private; int rc;
data = container_of(work, struct ext_pool_exhaust_work_data, worker);
device = data->device;
base = data->base;
if (!base)
base = device; if (dasd_eckd_space_configured(base) != 0) {
dasd_generic_space_avail(device);
} else {
dev_warn(&device->cdev->dev, "No space left in the extent pool\n");
DBF_DEV_EVENT(DBF_WARNING, device, "%s", "out of space");
}
/* This command cannot be executed on an alias device */ if (private->uid.type == UA_BASE_PAV_ALIAS ||
private->uid.type == UA_HYPER_PAV_ALIAS) return 0;
cqr->buildclk = get_tod_clock();
cqr->status = DASD_CQR_FILLED;
cqr->startdev = device;
cqr->memdev = device;
cqr->block = NULL;
cqr->retries = 256;
cqr->expires = device->default_expires * HZ; /* The command might not be supported. Suppress the error output */
__set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags);
rc = dasd_sleep_on_interruptible(cqr); if (rc == 0) {
dasd_eckd_cpy_ext_pool_data(device, lcq);
} else {
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "Reading the logical configuration failed with rc=%d", rc);
}
dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
/* * Depending on the device type, the extent size is specified either as * cylinders per extent (CKD) or size per extent (FBA) * A 1GB size corresponds to 1113cyl, and 16MB to 21cyl.
*/ staticint dasd_eckd_ext_size(struct dasd_device *device)
{ struct dasd_eckd_private *private = device->private; struct dasd_ext_pool_sum eps = private->eps;
if (!eps.flags.extent_size_valid) return 0; if (eps.extent_size.size_1G) return 1113; if (eps.extent_size.size_16M) return 21;
/* * Perform Subsystem Function. * It is necessary to trigger CIO for channel revalidation since this * call might change behaviour of DASD devices.
*/ staticint
dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav, unsignedlong flags)
{ struct dasd_ccw_req *cqr; int rc;
cqr = dasd_eckd_build_psf_ssc(device, enable_pav); if (IS_ERR(cqr)) return PTR_ERR(cqr);
/* * set flags e.g. turn on failfast, to prevent blocking * the calling function should handle failed requests
*/
cqr->flags |= flags;
rc = dasd_sleep_on(cqr); if (!rc) /* trigger CIO to reprobe devices */
css_schedule_reprobe(); elseif (cqr->intrc == -EAGAIN)
rc = -EAGAIN;
/* may be requested feature is not available on server,
* therefore just report error and go ahead */
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x " "returned rc=%d", private->uid.ssid, rc); return rc;
}
/* * worker to do a validate server in case of a lost pathgroup
*/ staticvoid dasd_eckd_do_validate_server(struct work_struct *work)
{ struct dasd_device *device = container_of(work, struct dasd_device,
kick_validate); unsignedlong flags = 0;
set_bit(DASD_CQR_FLAGS_FAILFAST, &flags); if (dasd_eckd_validate_server(device, flags)
== -EAGAIN) { /* schedule worker again if failed */
schedule_work(&device->kick_validate); return;
}
dasd_put_device(device);
}
staticvoid dasd_eckd_kick_validate_server(struct dasd_device *device)
{
dasd_get_device(device); /* exit if device not online or in offline processing */ if (test_bit(DASD_FLAG_OFFLINE, &device->flags) ||
device->state < DASD_STATE_ONLINE) {
dasd_put_device(device); return;
} /* queue call to do_validate_server to the kernel event daemon. */ if (!schedule_work(&device->kick_validate))
dasd_put_device(device);
}
/* * return if the device is the copy relation primary if a copy relation is active
*/ staticint dasd_device_is_primary(struct dasd_device *device)
{ if (!device->copy) return 1;
if (device->copy->active->device == device) return 1;
/* * Check device characteristics. * If the device is accessible using ECKD discipline, the device is enabled.
*/ staticint
dasd_eckd_check_characteristics(struct dasd_device *device)
{ struct dasd_eckd_private *private = device->private; int rc, i; int readonly; unsignedlong value;
/* setup work queue for validate server*/
INIT_WORK(&device->kick_validate, dasd_eckd_do_validate_server); /* setup work queue for summary unit check */
INIT_WORK(&device->suc_work, dasd_alias_handle_summary_unit_check);
if (!ccw_device_is_pathgroup(device->cdev)) {
dev_warn(&device->cdev->dev, "A channel path group could not be established\n"); return -EIO;
} if (!ccw_device_is_multipath(device->cdev)) {
dev_info(&device->cdev->dev, "The DASD is not operating in multipath mode\n");
} if (!private) { private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); if (!private) {
dev_warn(&device->cdev->dev, "Allocating memory for private DASD data " "failed\n"); return -ENOMEM;
}
device->private = private;
} else {
memset(private, 0, sizeof(*private));
} /* Invalidate status of initial analysis. */
private->init_cqr_status = -1; /* Set default cache operations. */
private->attrib.operation = DASD_NORMAL_CACHE;
private->attrib.nr_cyl = 0;
/* Read Configuration Data */
rc = dasd_eckd_read_conf(device); if (rc) goto out_err1;
if (private->conf.gneq) {
value = 1; for (i = 0; i < private->conf.gneq->timeout.value; i++)
value = 10 * value;
value = value * private->conf.gneq->timeout.number; /* do not accept useless values */ if (value != 0 && value <= DASD_EXPIRES_MAX)
device->default_expires = value;
}
/* Read Volume Information */
dasd_eckd_read_vol_info(device);
/* Read Extent Pool Information */
dasd_eckd_read_ext_pool_info(device);
if ((device->features & DASD_FEATURE_USERAW) &&
!(private->rdc_data.facilities.RT_in_LR)) {
dev_err(&device->cdev->dev, "The storage server does not " "support raw-track access\n");
rc = -EINVAL; goto out_err3;
}
/* find the valid cylinder size */ if (private->rdc_data.no_cyl == LV_COMPAT_CYL &&
private->rdc_data.long_no_cyl)
private->real_cyl = private->rdc_data.long_no_cyl; else
private->real_cyl = private->rdc_data.no_cyl;
private->fcx_max_data = get_fcx_max_data(device);
readonly = dasd_device_is_ro(device); if (readonly)
set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
/* differentiate between 'no record found' and any other error */ staticint dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr)
{ char *sense; if (init_cqr->status == DASD_CQR_DONE) return INIT_CQR_OK; elseif (init_cqr->status == DASD_CQR_NEED_ERP ||
init_cqr->status == DASD_CQR_FAILED) {
sense = dasd_get_sense(&init_cqr->irb); if (sense && (sense[1] & SNS1_NO_REC_FOUND)) return INIT_CQR_UNFORMATTED; else return INIT_CQR_ERROR;
} else return INIT_CQR_ERROR;
}
/* * This is the callback function for the init_analysis cqr. It saves * the status of the initial analysis ccw before it frees it and kicks * the device to continue the startup sequence. This will call * dasd_eckd_do_analysis again (if the devices has not been marked * for deletion in the meantime).
*/ staticvoid dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data)
{ struct dasd_device *device = init_cqr->startdev; struct dasd_eckd_private *private = device->private;
init_cqr = dasd_eckd_analysis_ccw(block->base); if (IS_ERR(init_cqr)) return PTR_ERR(init_cqr);
init_cqr->callback = dasd_eckd_analysis_callback;
init_cqr->callback_data = NULL;
init_cqr->expires = 5*HZ; /* first try without ERP, so we can later handle unformatted * devices as special case
*/
clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags);
init_cqr->retries = 0;
dasd_add_request_head(init_cqr); return -EAGAIN;
}
status = private->init_cqr_status;
private->init_cqr_status = -1; if (status == INIT_CQR_ERROR) { /* try again, this time with full ERP */
init_cqr = dasd_eckd_analysis_ccw(device);
dasd_sleep_on(init_cqr);
status = dasd_eckd_analysis_evaluation(init_cqr);
dasd_sfree_request(init_cqr, device);
}
if (status == INIT_CQR_UNFORMATTED) {
dev_warn(&device->cdev->dev, "The DASD is not formatted\n"); return -EMEDIUMTYPE;
} elseif (status == INIT_CQR_ERROR) {
dev_err(&device->cdev->dev, "Detecting the DASD disk layout failed because " "of an I/O error\n"); return -EIO;
}
private->uses_cdl = 1; /* Check Track 0 for Compatible Disk Layout */
count_area = NULL; for (i = 0; i < 3; i++) { if (private->count_area[i].kl != 4 ||
private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4 ||
private->count_area[i].cyl != 0 ||
private->count_area[i].head != count_area_head[i] ||
private->count_area[i].record != count_area_rec[i]) {
private->uses_cdl = 0; break;
}
} if (i == 3)
count_area = &private->count_area[3];
if (private->uses_cdl == 0) { for (i = 0; i < 5; i++) { if ((private->count_area[i].kl != 0) ||
(private->count_area[i].dl !=
private->count_area[0].dl) ||
private->count_area[i].cyl != 0 ||
private->count_area[i].head != count_area_head[i] ||
private->count_area[i].record != count_area_rec[i]) break;
} if (i == 5)
count_area = &private->count_area[0];
} else { if (private->count_area[3].record == 1)
dev_warn(&device->cdev->dev, "Track 0 has no records following the VTOC\n");
}
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.27 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.