diff options
Diffstat (limited to 'drivers/s390')
28 files changed, 1293 insertions, 239 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index c78db05e7..8973d34ce 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -75,6 +75,8 @@ static void dasd_block_timeout(unsigned long); static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *); static void dasd_profile_init(struct dasd_profile *, struct dentry *); static void dasd_profile_exit(struct dasd_profile *); +static void dasd_hosts_init(struct dentry *, struct dasd_device *); +static void dasd_hosts_exit(struct dasd_device *); /* * SECTION: Operations on the device structure. @@ -267,6 +269,7 @@ static int dasd_state_known_to_basic(struct dasd_device *device) dasd_debugfs_setup(dev_name(&device->cdev->dev), dasd_debugfs_root_entry); dasd_profile_init(&device->profile, device->debugfs_dentry); + dasd_hosts_init(device->debugfs_dentry, device); /* register 'device' debug area, used for all DBF_DEV_XXX calls */ device->debug_area = debug_register(dev_name(&device->cdev->dev), 4, 1, @@ -304,6 +307,7 @@ static int dasd_state_basic_to_known(struct dasd_device *device) return rc; dasd_device_clear_timer(device); dasd_profile_exit(&device->profile); + dasd_hosts_exit(device); debugfs_remove(device->debugfs_dentry); DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device); if (device->debug_area != NULL) { @@ -1150,6 +1154,58 @@ int dasd_profile_on(struct dasd_profile *profile) #endif /* CONFIG_DASD_PROFILE */ +static int dasd_hosts_show(struct seq_file *m, void *v) +{ + struct dasd_device *device; + int rc = -EOPNOTSUPP; + + device = m->private; + dasd_get_device(device); + + if (device->discipline->hosts_print) + rc = device->discipline->hosts_print(device, m); + + dasd_put_device(device); + return rc; +} + +static int dasd_hosts_open(struct inode *inode, struct file *file) +{ + struct dasd_device *device = inode->i_private; + + return single_open(file, dasd_hosts_show, device); +} + +static const struct file_operations dasd_hosts_fops = { + .owner = THIS_MODULE, + .open = dasd_hosts_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void dasd_hosts_exit(struct dasd_device *device) +{ + debugfs_remove(device->hosts_dentry); + device->hosts_dentry = NULL; +} + +static void dasd_hosts_init(struct dentry *base_dentry, + struct dasd_device *device) +{ + struct dentry *pde; + umode_t mode; + + if (!base_dentry) + return; + + mode = S_IRUSR | S_IFREG; + pde = debugfs_create_file("host_access_list", mode, base_dentry, + device, &dasd_hosts_fops); + if (pde && !IS_ERR(pde)) + device->hosts_dentry = pde; +} + /* * Allocate memory for a channel program with 'cplength' channel * command words and 'datasize' additional space. There are two @@ -1582,6 +1638,9 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, struct dasd_ccw_req *cqr, *next; struct dasd_device *device; unsigned long long now; + int nrf_suppressed = 0; + int fp_suppressed = 0; + u8 *sense = NULL; int expires; if (IS_ERR(irb)) { @@ -1617,7 +1676,23 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, dasd_put_device(device); return; } - device->discipline->dump_sense_dbf(device, irb, "int"); + + /* + * In some cases 'File Protected' or 'No Record Found' errors + * might be expected and debug log messages for the + * corresponding interrupts shouldn't be written then. + * Check if either of the according suppress bits is set. + */ + sense = dasd_get_sense(irb); + if (sense) { + fp_suppressed = (sense[1] & SNS1_FILE_PROTECTED) && + test_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags); + nrf_suppressed = (sense[1] & SNS1_NO_REC_FOUND) && + test_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); + } + if (!(fp_suppressed || nrf_suppressed)) + device->discipline->dump_sense_dbf(device, irb, "int"); + if (device->features & DASD_FEATURE_ERPLOG) device->discipline->dump_sense(device, cqr, irb); device->discipline->check_for_device_change(device, cqr, irb); @@ -2256,6 +2331,7 @@ static int _dasd_sleep_on_queue(struct list_head *ccw_queue, int interruptible) { struct dasd_device *device; struct dasd_ccw_req *cqr, *n; + u8 *sense = NULL; int rc; retry: @@ -2302,6 +2378,20 @@ retry: rc = 0; list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { /* + * In some cases the 'File Protected' or 'Incorrect Length' + * error might be expected and error recovery would be + * unnecessary in these cases. Check if the according suppress + * bit is set. + */ + sense = dasd_get_sense(&cqr->irb); + if (sense && sense[1] & SNS1_FILE_PROTECTED && + test_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags)) + continue; + if (scsw_cstat(&cqr->irb.scsw) == 0x40 && + test_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags)) + continue; + + /* * for alias devices simplify error recovery and * return to upper layer * do not skip ERP requests diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index d26134713..8305ab688 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -1367,8 +1367,14 @@ dasd_3990_erp_no_rec(struct dasd_ccw_req * default_erp, char *sense) struct dasd_device *device = default_erp->startdev; - dev_err(&device->cdev->dev, - "The specified record was not found\n"); + /* + * In some cases the 'No Record Found' error might be expected and + * log messages shouldn't be written then. + * Check if the according suppress bit is set. + */ + if (!test_bit(DASD_CQR_SUPPRESS_NRF, &default_erp->flags)) + dev_err(&device->cdev->dev, + "The specified record was not found\n"); return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED); @@ -1393,8 +1399,14 @@ dasd_3990_erp_file_prot(struct dasd_ccw_req * erp) struct dasd_device *device = erp->startdev; - dev_err(&device->cdev->dev, "Accessing the DASD failed because of " - "a hardware error\n"); + /* + * In some cases the 'File Protected' error might be expected and + * log messages shouldn't be written then. + * Check if the according suppress bit is set. + */ + if (!test_bit(DASD_CQR_SUPPRESS_FP, &erp->flags)) + dev_err(&device->cdev->dev, + "Accessing the DASD failed because of a hardware error\n"); return dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 2f18f6109..3cdbce45e 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -982,6 +982,32 @@ out: static DEVICE_ATTR(safe_offline, 0200, NULL, dasd_safe_offline_store); static ssize_t +dasd_access_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct dasd_device *device; + int count; + + device = dasd_device_from_cdev(cdev); + if (IS_ERR(device)) + return PTR_ERR(device); + + if (device->discipline->host_access_count) + count = device->discipline->host_access_count(device); + else + count = -EOPNOTSUPP; + + dasd_put_device(device); + if (count < 0) + return count; + + return sprintf(buf, "%d\n", count); +} + +static DEVICE_ATTR(host_access_count, 0444, dasd_access_show, NULL); + +static ssize_t dasd_discipline_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1471,6 +1497,7 @@ static struct attribute * dasd_attrs[] = { &dev_attr_reservation_policy.attr, &dev_attr_last_known_reservation_state.attr, &dev_attr_safe_offline.attr, + &dev_attr_host_access_count.attr, &dev_attr_path_masks.attr, NULL, }; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index c1b4ae55e..42b34cd1f 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/compat.h> #include <linux/init.h> +#include <linux/seq_file.h> #include <asm/css_chars.h> #include <asm/debug.h> @@ -120,6 +121,11 @@ struct check_attention_work_data { __u8 lpum; }; +static int prepare_itcw(struct itcw *, unsigned int, unsigned int, int, + struct dasd_device *, struct dasd_device *, + unsigned int, int, unsigned int, unsigned int, + unsigned int, unsigned int); + /* initial attempt at a probe function. this can be simplified once * the other detection code is gone */ static int @@ -256,10 +262,13 @@ define_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk, case DASD_ECKD_CCW_READ_CKD_MT: case DASD_ECKD_CCW_READ_KD: case DASD_ECKD_CCW_READ_KD_MT: - case DASD_ECKD_CCW_READ_COUNT: data->mask.perm = 0x1; data->attributes.operation = private->attrib.operation; break; + case DASD_ECKD_CCW_READ_COUNT: + data->mask.perm = 0x1; + data->attributes.operation = DASD_BYPASS_CACHE; + break; case DASD_ECKD_CCW_WRITE: case DASD_ECKD_CCW_WRITE_MT: case DASD_ECKD_CCW_WRITE_KD: @@ -528,10 +537,13 @@ static int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, case DASD_ECKD_CCW_READ_CKD_MT: case DASD_ECKD_CCW_READ_KD: case DASD_ECKD_CCW_READ_KD_MT: - case DASD_ECKD_CCW_READ_COUNT: dedata->mask.perm = 0x1; dedata->attributes.operation = basepriv->attrib.operation; break; + case DASD_ECKD_CCW_READ_COUNT: + dedata->mask.perm = 0x1; + dedata->attributes.operation = DASD_BYPASS_CACHE; + break; case DASD_ECKD_CCW_READ_TRACK: case DASD_ECKD_CCW_READ_TRACK_DATA: dedata->mask.perm = 0x1; @@ -2095,6 +2107,180 @@ dasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) return 0; } +/* + * Build the TCW request for the format check + */ +static struct dasd_ccw_req * +dasd_eckd_build_check_tcw(struct dasd_device *base, struct format_data_t *fdata, + int enable_pav, struct eckd_count *fmt_buffer, + int rpt) +{ + struct dasd_eckd_private *start_priv; + struct dasd_device *startdev = NULL; + struct tidaw *last_tidaw = NULL; + struct dasd_ccw_req *cqr; + struct itcw *itcw; + int itcw_size; + int count; + int rc; + int i; + + if (enable_pav) + startdev = dasd_alias_get_start_dev(base); + + if (!startdev) + startdev = base; + + start_priv = startdev->private; + + count = rpt * (fdata->stop_unit - fdata->start_unit + 1); + + /* + * we're adding 'count' amount of tidaw to the itcw. + * calculate the corresponding itcw_size + */ + itcw_size = itcw_calc_size(0, count, 0); + + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev); + if (IS_ERR(cqr)) + return cqr; + + start_priv->count++; + + itcw = itcw_init(cqr->data, itcw_size, ITCW_OP_READ, 0, count, 0); + if (IS_ERR(itcw)) { + rc = -EINVAL; + goto out_err; + } + + cqr->cpaddr = itcw_get_tcw(itcw); + rc = prepare_itcw(itcw, fdata->start_unit, fdata->stop_unit, + DASD_ECKD_CCW_READ_COUNT_MT, base, startdev, 0, count, + sizeof(struct eckd_count), + count * sizeof(struct eckd_count), 0, rpt); + if (rc) + goto out_err; + + for (i = 0; i < count; i++) { + last_tidaw = itcw_add_tidaw(itcw, 0, fmt_buffer++, + sizeof(struct eckd_count)); + if (IS_ERR(last_tidaw)) { + rc = -EINVAL; + goto out_err; + } + } + + last_tidaw->flags |= TIDAW_FLAGS_LAST; + itcw_finalize(itcw); + + cqr->cpmode = 1; + cqr->startdev = startdev; + cqr->memdev = startdev; + cqr->basedev = base; + cqr->retries = startdev->default_retries; + cqr->expires = startdev->default_expires * HZ; + cqr->buildclk = get_tod_clock(); + cqr->status = DASD_CQR_FILLED; + /* Set flags to suppress output for expected errors */ + set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags); + set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags); + + return cqr; + +out_err: + dasd_sfree_request(cqr, startdev); + + return ERR_PTR(rc); +} + +/* + * Build the CCW request for the format check + */ +static struct dasd_ccw_req * +dasd_eckd_build_check(struct dasd_device *base, struct format_data_t *fdata, + int enable_pav, struct eckd_count *fmt_buffer, int rpt) +{ + struct dasd_eckd_private *start_priv; + struct dasd_eckd_private *base_priv; + struct dasd_device *startdev = NULL; + struct dasd_ccw_req *cqr; + struct ccw1 *ccw; + void *data; + int cplength, datasize; + int use_prefix; + int count; + int i; + + if (enable_pav) + startdev = dasd_alias_get_start_dev(base); + + if (!startdev) + startdev = base; + + start_priv = startdev->private; + base_priv = base->private; + + count = rpt * (fdata->stop_unit - fdata->start_unit + 1); + + use_prefix = base_priv->features.feature[8] & 0x01; + + if (use_prefix) { + cplength = 1; + datasize = sizeof(struct PFX_eckd_data); + } else { + cplength = 2; + datasize = sizeof(struct DE_eckd_data) + + sizeof(struct LO_eckd_data); + } + cplength += count; + + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, + startdev); + if (IS_ERR(cqr)) + return cqr; + + start_priv->count++; + data = cqr->data; + ccw = cqr->cpaddr; + + if (use_prefix) { + prefix_LRE(ccw++, data, fdata->start_unit, fdata->stop_unit, + DASD_ECKD_CCW_READ_COUNT, base, startdev, 1, 0, + count, 0, 0); + } else { + define_extent(ccw++, data, fdata->start_unit, fdata->stop_unit, + DASD_ECKD_CCW_READ_COUNT, startdev); + + data += sizeof(struct DE_eckd_data); + ccw[-1].flags |= CCW_FLAG_CC; + + locate_record(ccw++, data, fdata->start_unit, 0, count, + DASD_ECKD_CCW_READ_COUNT, base, 0); + } + + for (i = 0; i < count; i++) { + ccw[-1].flags |= CCW_FLAG_CC; + ccw->cmd_code = DASD_ECKD_CCW_READ_COUNT; + ccw->flags = CCW_FLAG_SLI; + ccw->count = 8; + ccw->cda = (__u32)(addr_t) fmt_buffer; + ccw++; + fmt_buffer++; + } + + cqr->startdev = startdev; + cqr->memdev = startdev; + cqr->basedev = base; + cqr->retries = DASD_RETRIES; + cqr->expires = startdev->default_expires * HZ; + cqr->buildclk = get_tod_clock(); + cqr->status = DASD_CQR_FILLED; + /* Set flags to suppress output for expected errors */ + set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags); + + return cqr; +} + static struct dasd_ccw_req * dasd_eckd_build_format(struct dasd_device *base, struct format_data_t *fdata, @@ -2362,9 +2548,24 @@ dasd_eckd_build_format(struct dasd_device *base, */ static struct dasd_ccw_req * dasd_eckd_format_build_ccw_req(struct dasd_device *base, - struct format_data_t *fdata, int enable_pav) + struct format_data_t *fdata, int enable_pav, + int tpm, struct eckd_count *fmt_buffer, int rpt) { - return dasd_eckd_build_format(base, fdata, enable_pav); + struct dasd_ccw_req *ccw_req; + + if (!fmt_buffer) { + ccw_req = dasd_eckd_build_format(base, fdata, enable_pav); + } else { + if (tpm) + ccw_req = dasd_eckd_build_check_tcw(base, fdata, + enable_pav, + fmt_buffer, rpt); + else + ccw_req = dasd_eckd_build_check(base, fdata, enable_pav, + fmt_buffer, rpt); + } + + return ccw_req; } /* @@ -2409,12 +2610,15 @@ static int dasd_eckd_format_sanity_checks(struct dasd_device *base, */ static int dasd_eckd_format_process_data(struct dasd_device *base, struct format_data_t *fdata, - int enable_pav) + int enable_pav, int tpm, + struct eckd_count *fmt_buffer, int rpt, + struct irb *irb) { struct dasd_eckd_private *private = base->private; struct dasd_ccw_req *cqr, *n; struct list_head format_queue; struct dasd_device *device; + char *sense = NULL; int old_start, old_stop, format_step; int step, retry; int rc; @@ -2428,8 +2632,18 @@ static int dasd_eckd_format_process_data(struct dasd_device *base, old_start = fdata->start_unit; old_stop = fdata->stop_unit; - format_step = DASD_CQR_MAX_CCW / recs_per_track(&private->rdc_data, 0, - fdata->blksize); + if (!tpm && fmt_buffer != NULL) { + /* Command Mode / Format Check */ + format_step = 1; + } else if (tpm && fmt_buffer != NULL) { + /* Transport Mode / Format Check */ + format_step = DASD_CQR_MAX_CCW / rpt; + } else { + /* Normal Formatting */ + format_step = DASD_CQR_MAX_CCW / + recs_per_track(&private->rdc_data, 0, fdata->blksize); + } + do { retry = 0; while (fdata->start_unit <= old_stop) { @@ -2440,7 +2654,8 @@ static int dasd_eckd_format_process_data(struct dasd_device *base, } cqr = dasd_eckd_format_build_ccw_req(base, fdata, - enable_pav); + enable_pav, tpm, + fmt_buffer, rpt); if (IS_ERR(cqr)) { rc = PTR_ERR(cqr); if (rc == -ENOMEM) { @@ -2458,6 +2673,10 @@ static int dasd_eckd_format_process_data(struct dasd_device *base, } list_add_tail(&cqr->blocklist, &format_queue); + if (fmt_buffer) { + step = fdata->stop_unit - fdata->start_unit + 1; + fmt_buffer += rpt * step; + } fdata->start_unit = fdata->stop_unit + 1; fdata->stop_unit = old_stop; } @@ -2468,15 +2687,41 @@ out_err: list_for_each_entry_safe(cqr, n, &format_queue, blocklist) { device = cqr->startdev; private = device->private; - if (cqr->status == DASD_CQR_FAILED) + + if (cqr->status == DASD_CQR_FAILED) { + /* + * Only get sense data if called by format + * check + */ + if (fmt_buffer && irb) { + sense = dasd_get_sense(&cqr->irb); + memcpy(irb, &cqr->irb, sizeof(*irb)); + } rc = -EIO; + } list_del_init(&cqr->blocklist); dasd_sfree_request(cqr, device); private->count--; } - if (rc) + if (rc && rc != -EIO) goto out; + if (rc == -EIO) { + /* + * In case fewer than the expected records are on the + * track, we will most likely get a 'No Record Found' + * error (in command mode) or a 'File Protected' error + * (in transport mode). Those particular cases shouldn't + * pass the -EIO to the IOCTL, therefore reset the rc + * and continue. + */ + if (sense && + (sense[1] & SNS1_NO_REC_FOUND || + sense[1] & SNS1_FILE_PROTECTED)) + retry = 1; + else + goto out; + } } while (retry); @@ -2490,7 +2735,225 @@ out: static int dasd_eckd_format_device(struct dasd_device *base, struct format_data_t *fdata, int enable_pav) { - return dasd_eckd_format_process_data(base, fdata, enable_pav); + return dasd_eckd_format_process_data(base, fdata, enable_pav, 0, NULL, + 0, NULL); +} + +/* + * Helper function to count consecutive records of a single track. + */ +static int dasd_eckd_count_records(struct eckd_count *fmt_buffer, int start, + int max) +{ + int head; + int i; + + head = fmt_buffer[start].head; + + /* + * There are 3 conditions where we stop counting: + * - if data reoccurs (same head and record may reoccur), which may + * happen due to the way DASD_ECKD_CCW_READ_COUNT works + * - when the head changes, because we're iterating over several tracks + * then (DASD_ECKD_CCW_READ_COUNT_MT) + * - when we've reached the end of sensible data in the buffer (the + * record will be 0 then) + */ + for (i = start; i < max; i++) { + if (i > start) { + if ((fmt_buffer[i].head == head && + fmt_buffer[i].record == 1) || + fmt_buffer[i].head != head || + fmt_buffer[i].record == 0) + break; + } + } + + return i - start; +} + +/* + * Evaluate a given range of tracks. Data like number of records, blocksize, + * record ids, and key length are compared with expected data. + * + * If a mismatch occurs, the corresponding error bit is set, as well as + * additional information, depending on the error. + */ +static void dasd_eckd_format_evaluate_tracks(struct eckd_count *fmt_buffer, + struct format_check_t *cdata, + int rpt_max, int rpt_exp, + int trk_per_cyl, int tpm) +{ + struct ch_t geo; + int max_entries; + int count = 0; + int trkcount; + int blksize; + int pos = 0; + int i, j; + int kl; + + trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1; + max_entries = trkcount * rpt_max; + + for (i = cdata->expect.start_unit; i <= cdata->expect.stop_unit; i++) { + /* Calculate the correct next starting position in the buffer */ + if (tpm) { + while (fmt_buffer[pos].record == 0 && + fmt_buffer[pos].dl == 0) { + if (pos++ > max_entries) + break; + } + } else { + if (i != cdata->expect.start_unit) + pos += rpt_max - count; + } + + /* Calculate the expected geo values for the current track */ + set_ch_t(&geo, i / trk_per_cyl, i % trk_per_cyl); + + /* Count and check number of records */ + count = dasd_eckd_count_records(fmt_buffer, pos, pos + rpt_max); + + if (count < rpt_exp) { + cdata->result = DASD_FMT_ERR_TOO_FEW_RECORDS; + break; + } + if (count > rpt_exp) { + cdata->result = DASD_FMT_ERR_TOO_MANY_RECORDS; + break; + } + + for (j = 0; j < count; j++, pos++) { + blksize = cdata->expect.blksize; + kl = 0; + + /* + * Set special values when checking CDL formatted + * devices. + */ + if ((cdata->expect.intensity & 0x08) && + geo.cyl == 0 && geo.head == 0) { + if (j < 3) { + blksize = sizes_trk0[j] - 4; + kl = 4; + } + } + if ((cdata->expect.intensity & 0x08) && + geo.cyl == 0 && geo.head == 1) { + blksize = LABEL_SIZE - 44; + kl = 44; + } + + /* Check blocksize */ + if (fmt_buffer[pos].dl != blksize) { + cdata->result = DASD_FMT_ERR_BLKSIZE; + goto out; + } + /* Check if key length is 0 */ + if (fmt_buffer[pos].kl != kl) { + cdata->result = DASD_FMT_ERR_KEY_LENGTH; + goto out; + } + /* Check if record_id is correct */ + if (fmt_buffer[pos].cyl != geo.cyl || + fmt_buffer[pos].head != geo.head || + fmt_buffer[pos].record != (j + 1)) { + cdata->result = DASD_FMT_ERR_RECORD_ID; + goto out; + } + } + } + +out: + /* + * In case of no errors, we need to decrease by one + * to get the correct positions. + */ + if (!cdata->result) { + i--; + pos--; + } + + cdata->unit = i; + cdata->num_records = count; + cdata->rec = fmt_buffer[pos].record; + cdata->blksize = fmt_buffer[pos].dl; + cdata->key_length = fmt_buffer[pos].kl; +} + +/* + * Check the format of a range of tracks of a DASD. + */ +static int dasd_eckd_check_device_format(struct dasd_device *base, + struct format_check_t *cdata, + int enable_pav) +{ + struct dasd_eckd_private *private = base->private; + struct eckd_count *fmt_buffer; + struct irb irb; + int rpt_max, rpt_exp; + int fmt_buffer_size; + int trk_per_cyl; + int trkcount; + int tpm = 0; + int rc; + + trk_per_cyl = private->rdc_data.trk_per_cyl; + + /* Get maximum and expected amount of records per track */ + rpt_max = recs_per_track(&private->rdc_data, 0, 512) + 1; + rpt_exp = recs_per_track(&private->rdc_data, 0, cdata->expect.blksize); + + trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1; + fmt_buffer_size = trkcount * rpt_max * sizeof(struct eckd_count); + + fmt_buffer = kzalloc(fmt_buffer_size, GFP_KERNEL | GFP_DMA); + if (!fmt_buffer) + return -ENOMEM; + + /* + * A certain FICON feature subset is needed to operate in transport + * mode. Additionally, the support for transport mode is implicitly + * checked by comparing the buffer size with fcx_max_data. As long as + * the buffer size is smaller we can operate in transport mode and + * process multiple tracks. If not, only one track at once is being + * processed using command mode. + */ + if ((private->features.feature[40] & 0x04) && + fmt_buffer_size <= private->fcx_max_data) + tpm = 1; + + rc = dasd_eckd_format_process_data(base, &cdata->expect, enable_pav, + tpm, fmt_buffer, rpt_max, &irb); + if (rc && rc != -EIO) + goto out; + if (rc == -EIO) { + /* + * If our first attempt with transport mode enabled comes back + * with an incorrect length error, we're going to retry the + * check with command mode. + */ + if (tpm && scsw_cstat(&irb.scsw) == 0x40) { + tpm = 0; + rc = dasd_eckd_format_process_data(base, &cdata->expect, + enable_pav, tpm, + fmt_buffer, rpt_max, + &irb); + if (rc) + goto out; + } else { + goto out; + } + } + + dasd_eckd_format_evaluate_tracks(fmt_buffer, cdata, rpt_max, rpt_exp, + trk_per_cyl, tpm); + +out: + kfree(fmt_buffer); + + return rc; } static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) @@ -3037,6 +3500,16 @@ static int prepare_itcw(struct itcw *itcw, lredata->auxiliary.check_bytes = 0x2; pfx_cmd = DASD_ECKD_CCW_PFX; break; + case DASD_ECKD_CCW_READ_COUNT_MT: + dedata->mask.perm = 0x1; + dedata->attributes.operation = DASD_BYPASS_CACHE; + dedata->ga_extended |= 0x42; + dedata->blk_size = blksize; + lredata->operation.orientation = 0x2; + lredata->operation.operation = 0x16; + lredata->auxiliary.check_bytes = 0x01; + pfx_cmd = DASD_ECKD_CCW_PFX_READ; + break; default: DBF_DEV_EVENT(DBF_ERR, basedev, "prepare itcw, unknown opcode 0x%x", cmd); @@ -3084,13 +3557,19 @@ static int prepare_itcw(struct itcw *itcw, } } - lredata->auxiliary.length_valid = 1; - lredata->auxiliary.length_scope = 1; + if (cmd == DASD_ECKD_CCW_READ_COUNT_MT) { + lredata->auxiliary.length_valid = 0; + lredata->auxiliary.length_scope = 0; + lredata->sector = 0xff; + } else { + lredata->auxiliary.length_valid = 1; + lredata->auxiliary.length_scope = 1; + lredata->sector = sector; + } lredata->auxiliary.imbedded_ccw_valid = 1; lredata->length = tlf; lredata->imbedded_ccw = cmd; lredata->count = count; - lredata->sector = sector; set_ch_t(&lredata->seek_addr, begcyl, beghead); lredata->search_arg.cyl = lredata->seek_addr.cyl; lredata->search_arg.head = lredata->seek_addr.head; @@ -4412,10 +4891,34 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, static void dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req *req, struct irb *irb) { - if (scsw_is_tm(&irb->scsw)) + u8 *sense = dasd_get_sense(irb); + + if (scsw_is_tm(&irb->scsw)) { + /* + * In some cases the 'File Protected' or 'Incorrect Length' + * error might be expected and log messages shouldn't be written + * then. Check if the according suppress bit is set. + */ + if (sense && (sense[1] & SNS1_FILE_PROTECTED) && + test_bit(DASD_CQR_SUPPRESS_FP, &req->flags)) + return; + if (scsw_cstat(&irb->scsw) == 0x40 && + test_bit(DASD_CQR_SUPPRESS_IL, &req->flags)) + return; + dasd_eckd_dump_sense_tcw(device, req, irb); - else + } else { + /* + * In some cases the 'No Record Found' error might be expected + * and log messages shouldn't be written then. Check if the + * according suppress bit is set. + */ + if (sense && sense[1] & SNS1_NO_REC_FOUND && + test_bit(DASD_CQR_SUPPRESS_NRF, &req->flags)) + return; + dasd_eckd_dump_sense_ccw(device, req, irb); + } } static int dasd_eckd_pm_freeze(struct dasd_device *device) @@ -4627,6 +5130,167 @@ static int dasd_eckd_read_message_buffer(struct dasd_device *device, return rc; } +static int dasd_eckd_query_host_access(struct dasd_device *device, + struct dasd_psf_query_host_access *data) +{ + struct dasd_eckd_private *private = device->private; + struct dasd_psf_query_host_access *host_access; + struct dasd_psf_prssd_data *prssdp; + struct dasd_ccw_req *cqr; + struct ccw1 *ccw; + int rc; + + /* not available for HYPER PAV alias devices */ + if (!device->block && private->lcu->pav == HYPER_PAV) + return -EOPNOTSUPP; + + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, + sizeof(struct dasd_psf_prssd_data) + 1, + device); + if (IS_ERR(cqr)) { + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "Could not allocate read message buffer request"); + return PTR_ERR(cqr); + } + host_access = kzalloc(sizeof(*host_access), GFP_KERNEL | GFP_DMA); + if (!host_access) { + dasd_sfree_request(cqr, device); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "Could not allocate host_access buffer"); + return -ENOMEM; + } + cqr->startdev = device; + cqr->memdev = device; + cqr->block = NULL; + cqr->retries = 256; + cqr->expires = 10 * HZ; + + /* 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 = PSF_SUBORDER_QHA; /* query host access */ + /* LSS and Volume that will be queried */ + prssdp->lss = private->ned->ID; + prssdp->volume = private->ned->unit_addr; + /* all other bytes of prssdp must be zero */ + + ccw = cqr->cpaddr; + ccw->cmd_code = DASD_ECKD_CCW_PSF; + ccw->count = sizeof(struct dasd_psf_prssd_data); + ccw->flags |= CCW_FLAG_CC; + ccw->flags |= CCW_FLAG_SLI; + ccw->cda = (__u32)(addr_t) prssdp; + + /* Read Subsystem Data - query host access */ + ccw++; + ccw->cmd_code = DASD_ECKD_CCW_RSSD; + ccw->count = sizeof(struct dasd_psf_query_host_access); + ccw->flags |= CCW_FLAG_SLI; + ccw->cda = (__u32)(addr_t) host_access; + + cqr->buildclk = get_tod_clock(); + cqr->status = DASD_CQR_FILLED; + rc = dasd_sleep_on(cqr); + if (rc == 0) { + *data = *host_access; + } else { + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, + "Reading host access data failed with rc=%d\n", + rc); + rc = -EOPNOTSUPP; + } + + dasd_sfree_request(cqr, cqr->memdev); + kfree(host_access); + return rc; +} +/* + * return number of grouped devices + */ +static int dasd_eckd_host_access_count(struct dasd_device *device) +{ + struct dasd_psf_query_host_access *access; + struct dasd_ckd_path_group_entry *entry; + struct dasd_ckd_host_information *info; + int count = 0; + int rc, i; + + access = kzalloc(sizeof(*access), GFP_NOIO); + if (!access) { + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "Could not allocate access buffer"); + return -ENOMEM; + } + rc = dasd_eckd_query_host_access(device, access); + if (rc) { + kfree(access); + return rc; + } + + info = (struct dasd_ckd_host_information *) + access->host_access_information; + for (i = 0; i < info->entry_count; i++) { + entry = (struct dasd_ckd_path_group_entry *) + (info->entry + i * info->entry_size); + if (entry->status_flags & DASD_ECKD_PG_GROUPED) + count++; + } + + kfree(access); + return count; +} + +/* + * write host access information to a sequential file + */ +static int dasd_hosts_print(struct dasd_device *device, struct seq_file *m) +{ + struct dasd_psf_query_host_access *access; + struct dasd_ckd_path_group_entry *entry; + struct dasd_ckd_host_information *info; + char sysplex[9] = ""; + int rc, i, j; + + access = kzalloc(sizeof(*access), GFP_NOIO); + if (!access) { + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "Could not allocate access buffer"); + return -ENOMEM; + } + rc = dasd_eckd_query_host_access(device, access); + if (rc) { + kfree(access); + return rc; + } + + info = (struct dasd_ckd_host_information *) + access->host_access_information; + for (i = 0; i < info->entry_count; i++) { + entry = (struct dasd_ckd_path_group_entry *) + (info->entry + i * info->entry_size); + /* PGID */ + seq_puts(m, "pgid "); + for (j = 0; j < 11; j++) + seq_printf(m, "%02x", entry->pgid[j]); + seq_putc(m, '\n'); + /* FLAGS */ + seq_printf(m, "status_flags %02x\n", entry->status_flags); + /* SYSPLEX NAME */ + memcpy(&sysplex, &entry->sysplex_name, sizeof(sysplex) - 1); + EBCASC(sysplex, sizeof(sysplex)); + seq_printf(m, "sysplex_name %8s\n", sysplex); + /* SUPPORTED CYLINDER */ + seq_printf(m, "supported_cylinder %d\n", entry->cylinder); + /* TIMESTAMP */ + seq_printf(m, "timestamp %lu\n", (unsigned long) + entry->timestamp); + } + kfree(access); + + return 0; +} + /* * Perform Subsystem Function - CUIR response */ @@ -5084,6 +5748,7 @@ static struct dasd_discipline dasd_eckd_discipline = { .term_IO = dasd_term_IO, .handle_terminated_request = dasd_eckd_handle_terminated_request, .format_device = dasd_eckd_format_device, + .check_device_format = dasd_eckd_check_device_format, .erp_action = dasd_eckd_erp_action, .erp_postaction = dasd_eckd_erp_postaction, .check_for_device_change = dasd_eckd_check_for_device_change, @@ -5099,6 +5764,8 @@ static struct dasd_discipline dasd_eckd_discipline = { .get_uid = dasd_eckd_get_uid, .kick_validate = dasd_eckd_kick_validate_server, .check_attention = dasd_eckd_check_attention, + .host_access_count = dasd_eckd_host_access_count, + .hosts_print = dasd_hosts_print, }; static int __init diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index 6d9a6d351..59803626e 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -35,6 +35,7 @@ #define DASD_ECKD_CCW_READ_MT 0x86 #define DASD_ECKD_CCW_WRITE_KD_MT 0x8d #define DASD_ECKD_CCW_READ_KD_MT 0x8e +#define DASD_ECKD_CCW_READ_COUNT_MT 0x92 #define DASD_ECKD_CCW_RELEASE 0x94 #define DASD_ECKD_CCW_WRITE_FULL_TRACK 0x95 #define DASD_ECKD_CCW_READ_CKD_MT 0x9e @@ -53,6 +54,7 @@ */ #define PSF_ORDER_PRSSD 0x18 #define PSF_ORDER_CUIR_RESPONSE 0x1A +#define PSF_SUBORDER_QHA 0x1C #define PSF_ORDER_SSC 0x1D /* @@ -81,6 +83,8 @@ #define ATTENTION_LENGTH_CUIR 0x0e #define ATTENTION_FORMAT_CUIR 0x01 +#define DASD_ECKD_PG_GROUPED 0x10 + /* * Size that is reportet for large volumes in the old 16-bit no_cyl field */ @@ -403,13 +407,41 @@ struct dasd_psf_cuir_response { __u8 ssid; } __packed; +struct dasd_ckd_path_group_entry { + __u8 status_flags; + __u8 pgid[11]; + __u8 sysplex_name[8]; + __u32 timestamp; + __u32 cylinder; + __u8 reserved[4]; +} __packed; + +struct dasd_ckd_host_information { + __u8 access_flags; + __u8 entry_size; + __u16 entry_count; + __u8 entry[16390]; +} __packed; + +struct dasd_psf_query_host_access { + __u8 access_flag; + __u8 version; + __u16 CKD_length; + __u16 SCSI_length; + __u8 unused[10]; + __u8 host_access_information[16394]; +} __packed; + /* * Perform Subsystem Function - Prepare for Read Subsystem Data */ struct dasd_psf_prssd_data { unsigned char order; unsigned char flags; - unsigned char reserved[4]; + unsigned char reserved1; + unsigned char reserved2; + unsigned char lss; + unsigned char volume; unsigned char suborder; unsigned char varies[5]; } __attribute__ ((packed)); diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 0f0add932..ac7027e6d 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -236,6 +236,13 @@ struct dasd_ccw_req { * stolen. Should not be combined with * DASD_CQR_FLAGS_USE_ERP */ +/* + * The following flags are used to suppress output of certain errors. + * These flags should only be used for format checks! + */ +#define DASD_CQR_SUPPRESS_NRF 4 /* Suppress 'No Record Found' error */ +#define DASD_CQR_SUPPRESS_FP 5 /* Suppress 'File Protected' error*/ +#define DASD_CQR_SUPPRESS_IL 6 /* Suppress 'Incorrect Length' error */ /* Signature for error recovery functions. */ typedef struct dasd_ccw_req *(*dasd_erp_fn_t) (struct dasd_ccw_req *); @@ -318,7 +325,8 @@ struct dasd_discipline { * 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 - * returns a ccw chain to be used to format the device. + * formats the device and check_device_format compares the format of + * a device with the expected format_data. * handle_terminated_request allows to examine a cqr and prepare * it for retry. */ @@ -329,7 +337,9 @@ struct dasd_discipline { int (*term_IO) (struct dasd_ccw_req *); void (*handle_terminated_request) (struct dasd_ccw_req *); int (*format_device) (struct dasd_device *, - struct format_data_t *, int enable_pav); + struct format_data_t *, int); + int (*check_device_format)(struct dasd_device *, + struct format_check_t *, int); int (*free_cp) (struct dasd_ccw_req *, struct request *); /* @@ -365,6 +375,8 @@ struct dasd_discipline { int (*get_uid) (struct dasd_device *, struct dasd_uid *); void (*kick_validate) (struct dasd_device *); int (*check_attention)(struct dasd_device *, __u8); + int (*host_access_count)(struct dasd_device *); + int (*hosts_print)(struct dasd_device *, struct seq_file *); }; extern struct dasd_discipline *dasd_diag_discipline_pointer; @@ -487,6 +499,7 @@ struct dasd_device { unsigned long blk_timeout; struct dentry *debugfs_dentry; + struct dentry *hosts_dentry; struct dasd_profile profile; }; diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 90f30cc31..9dfbd972f 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -238,6 +238,23 @@ dasd_format(struct dasd_block *block, struct format_data_t *fdata) return rc; } +static int dasd_check_format(struct dasd_block *block, + struct format_check_t *cdata) +{ + struct dasd_device *base; + int rc; + + base = block->base; + if (!base->discipline->check_device_format) + return -ENOTTY; + + rc = base->discipline->check_device_format(base, cdata, 1); + if (rc == -EAGAIN) + rc = base->discipline->check_device_format(base, cdata, 0); + + return rc; +} + /* * Format device. */ @@ -272,6 +289,47 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp) } rc = dasd_format(base->block, &fdata); dasd_put_device(base); + + return rc; +} + +/* + * Check device format + */ +static int dasd_ioctl_check_format(struct block_device *bdev, void __user *argp) +{ + struct format_check_t cdata; + struct dasd_device *base; + int rc = 0; + + if (!argp) + return -EINVAL; + + base = dasd_device_from_gendisk(bdev->bd_disk); + if (!base) + return -ENODEV; + if (bdev != bdev->bd_contains) { + pr_warn("%s: The specified DASD is a partition and cannot be checked\n", + dev_name(&base->cdev->dev)); + rc = -EINVAL; + goto out_err; + } + + if (copy_from_user(&cdata, argp, sizeof(cdata))) { + rc = -EFAULT; + goto out_err; + } + + rc = dasd_check_format(base->block, &cdata); + if (rc) + goto out_err; + + if (copy_to_user(argp, &cdata, sizeof(cdata))) + rc = -EFAULT; + +out_err: + dasd_put_device(base); + return rc; } @@ -519,6 +577,9 @@ int dasd_ioctl(struct block_device *bdev, fmode_t mode, case BIODASDFMT: rc = dasd_ioctl_format(bdev, argp); break; + case BIODASDCHECKFMT: + rc = dasd_ioctl_check_format(bdev, argp); + break; case BIODASDINFO: rc = dasd_ioctl_information(block, cmd, argp); break; diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index b83908670..bed53c46d 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -31,7 +31,7 @@ static void dcssblk_release(struct gendisk *disk, fmode_t mode); static blk_qc_t dcssblk_make_request(struct request_queue *q, struct bio *bio); static long dcssblk_direct_access(struct block_device *bdev, sector_t secnum, - void __pmem **kaddr, pfn_t *pfn); + void __pmem **kaddr, pfn_t *pfn, long size); static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; @@ -884,7 +884,7 @@ fail: static long dcssblk_direct_access (struct block_device *bdev, sector_t secnum, - void __pmem **kaddr, pfn_t *pfn) + void __pmem **kaddr, pfn_t *pfn, long size) { struct dcssblk_dev_info *dev_info; unsigned long offset, dev_sz; diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index dd2f7c832..41e28b23b 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -18,6 +18,8 @@ obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o obj-$(CONFIG_SCLP_ASYNC) += sclp_async.o +obj-$(CONFIG_PCI) += sclp_pci.o + obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o obj-$(CONFIG_VMCP) += vmcp.o diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index e7e078b3c..931d10e86 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -289,7 +289,7 @@ static void raw3215_timeout(unsigned long __data) spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw->flags &= ~RAW3215_TIMER_RUNS; - if (!(raw->port.flags & ASYNC_SUSPENDED)) { + if (!tty_port_suspended(&raw->port)) { raw3215_mk_write_req(raw); raw3215_start_io(raw); if ((raw->queued_read || raw->queued_write) && @@ -311,8 +311,7 @@ static void raw3215_timeout(unsigned long __data) */ static inline void raw3215_try_io(struct raw3215_info *raw) { - if (!(raw->port.flags & ASYNC_INITIALIZED) || - (raw->port.flags & ASYNC_SUSPENDED)) + if (!tty_port_initialized(&raw->port) || tty_port_suspended(&raw->port)) return; if (raw->queued_read != NULL) raw3215_start_io(raw); @@ -494,7 +493,7 @@ static void raw3215_make_room(struct raw3215_info *raw, unsigned int length) /* While console is frozen for suspend we have no other * choice but to drop message from the buffer to make * room for even more messages. */ - if (raw->port.flags & ASYNC_SUSPENDED) { + if (tty_port_suspended(&raw->port)) { raw3215_drop_line(raw); continue; } @@ -616,10 +615,10 @@ static int raw3215_startup(struct raw3215_info *raw) { unsigned long flags; - if (raw->port.flags & ASYNC_INITIALIZED) + if (tty_port_initialized(&raw->port)) return 0; raw->line_pos = 0; - raw->port.flags |= ASYNC_INITIALIZED; + tty_port_set_initialized(&raw->port, 1); spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw3215_try_io(raw); spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); @@ -635,8 +634,7 @@ static void raw3215_shutdown(struct raw3215_info *raw) DECLARE_WAITQUEUE(wait, current); unsigned long flags; - if (!(raw->port.flags & ASYNC_INITIALIZED) || - (raw->flags & RAW3215_FIXED)) + if (!tty_port_initialized(&raw->port) || (raw->flags & RAW3215_FIXED)) return; /* Wait for outstanding requests, then free irq */ spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); @@ -650,7 +648,7 @@ static void raw3215_shutdown(struct raw3215_info *raw) spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); remove_wait_queue(&raw->empty_wait, &wait); set_current_state(TASK_RUNNING); - raw->port.flags &= ~ASYNC_INITIALIZED; + tty_port_set_initialized(&raw->port, 1); } spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); } @@ -773,7 +771,7 @@ static int raw3215_pm_stop(struct ccw_device *cdev) raw = dev_get_drvdata(&cdev->dev); spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw3215_make_room(raw, RAW3215_BUFFER_SIZE); - raw->port.flags |= ASYNC_SUSPENDED; + tty_port_set_suspended(&raw->port, 1); spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); return 0; } @@ -786,7 +784,7 @@ static int raw3215_pm_start(struct ccw_device *cdev) /* Allow I/O again and flush output buffer. */ raw = dev_get_drvdata(&cdev->dev); spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); - raw->port.flags &= ~ASYNC_SUSPENDED; + tty_port_set_suspended(&raw->port, 0); raw->flags |= RAW3215_FLUSHING; raw3215_try_io(raw); raw->flags &= ~RAW3215_FLUSHING; @@ -859,7 +857,7 @@ static void con3215_flush(void) unsigned long flags; raw = raw3215[0]; /* console 3215 is the first one */ - if (raw->port.flags & ASYNC_SUSPENDED) + if (tty_port_suspended(&raw->port)) /* The console is still frozen for suspend. */ if (ccw_device_force_console(raw->cdev)) /* Forcing didn't work, no panic message .. */ diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 4d7a9badf..6b1577c73 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -400,7 +400,7 @@ con3270_deactivate(struct raw3270_view *view) del_timer(&cp->timer); } -static int +static void con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) { /* Handle ATTN. Schedule tasklet to read aid. */ @@ -418,7 +418,6 @@ con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) cp->update_flags = CON_UPDATE_ALL; con3270_set_timer(cp, 1); } - return RAW3270_IO_DONE; } /* Console view to a 3270 device. */ diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 71e974738..85eca1cef 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -217,7 +217,7 @@ fs3270_deactivate(struct raw3270_view *view) fp->init->callback(fp->init, NULL); } -static int +static void fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) { /* Handle ATTN. Set indication and wake waiters for attention. */ @@ -233,7 +233,6 @@ fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) /* Normal end. Copy residual count. */ rq->rescnt = irb->scsw.cmd.count; } - return RAW3270_IO_DONE; } /* diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 220acb4cb..a2da898ce 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -90,6 +90,8 @@ module_param(tubxcorrect, bool, 0); */ DECLARE_WAIT_QUEUE_HEAD(raw3270_wait_queue); +static void __raw3270_disconnect(struct raw3270 *rp); + /* * Encode array for 12 bit 3270 addresses. */ @@ -229,29 +231,6 @@ raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) } /* - * Stop running ccw. - */ -static int -__raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq) -{ - int retries; - int rc; - - if (raw3270_request_final(rq)) - return 0; - /* Check if interrupt has already been processed */ - for (retries = 0; retries < 5; retries++) { - if (retries < 2) - rc = ccw_device_halt(rp->cdev, (long) rq); - else - rc = ccw_device_clear(rp->cdev, (long) rq); - if (rc == 0) - break; /* termination successful */ - } - return rc; -} - -/* * Add the request to the request queue, try to start it if the * 3270 device is idle. Return without waiting for end of i/o. */ @@ -342,7 +321,6 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) struct raw3270 *rp; struct raw3270_view *view; struct raw3270_request *rq; - int rc; rp = dev_get_drvdata(&cdev->dev); if (!rp) @@ -350,57 +328,31 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) rq = (struct raw3270_request *) intparm; view = rq ? rq->view : rp->view; - if (IS_ERR(irb)) - rc = RAW3270_IO_RETRY; - else if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) { - rq->rc = -EIO; - rc = RAW3270_IO_DONE; - } else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END | - DEV_STAT_UNIT_EXCEP)) { + if (!IS_ERR(irb)) { /* Handle CE-DE-UE and subsequent UDE */ - set_bit(RAW3270_FLAGS_BUSY, &rp->flags); - rc = RAW3270_IO_BUSY; - } else if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) { - /* Wait for UDE if busy flag is set. */ - if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { + if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) clear_bit(RAW3270_FLAGS_BUSY, &rp->flags); - /* Got it, now retry. */ - rc = RAW3270_IO_RETRY; - } else - rc = RAW3270_IO_BUSY; - } else if (view) - rc = view->fn->intv(view, rq, irb); - else - rc = RAW3270_IO_DONE; + if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | + DEV_STAT_DEV_END | + DEV_STAT_UNIT_EXCEP)) + set_bit(RAW3270_FLAGS_BUSY, &rp->flags); + /* Handle disconnected devices */ + if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) && + (irb->ecw[0] & SNS0_INTERVENTION_REQ)) { + set_bit(RAW3270_FLAGS_BUSY, &rp->flags); + if (rp->state > RAW3270_STATE_RESET) + __raw3270_disconnect(rp); + } + /* Call interrupt handler of the view */ + if (view) + view->fn->intv(view, rq, irb); + } - switch (rc) { - case RAW3270_IO_DONE: - break; - case RAW3270_IO_BUSY: - /* - * Intervention required by the operator. We have to wait - * for unsolicited device end. - */ + if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) + /* Device busy, do not start I/O */ return; - case RAW3270_IO_RETRY: - if (!rq) - break; - rq->rc = ccw_device_start(rp->cdev, &rq->ccw, - (unsigned long) rq, 0, 0); - if (rq->rc == 0) - return; /* Successfully restarted. */ - break; - case RAW3270_IO_STOP: - if (!rq) - break; - __raw3270_halt_io(rp, rq); - rq->rc = -EIO; - break; - default: - BUG(); - } - if (rq) { - BUG_ON(list_empty(&rq->list)); + + if (rq && !list_empty(&rq->list)) { /* The request completed, remove from queue and do callback. */ list_del_init(&rq->list); if (rq->callback) @@ -408,6 +360,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) /* Do put_device for get_device in raw3270_start. */ raw3270_put_view(view); } + /* * Try to start each request on request queue until one is * started successful. @@ -685,23 +638,34 @@ raw3270_reset(struct raw3270_view *view) return rc; } -static int +static void +__raw3270_disconnect(struct raw3270 *rp) +{ + struct raw3270_request *rq; + struct raw3270_view *view; + + rp->state = RAW3270_STATE_INIT; + rp->view = &rp->init_view; + /* Cancel all queued requests */ + while (!list_empty(&rp->req_queue)) { + rq = list_entry(rp->req_queue.next,struct raw3270_request,list); + view = rq->view; + rq->rc = -EACCES; + list_del_init(&rq->list); + if (rq->callback) + rq->callback(rq, rq->callback_data); + raw3270_put_view(view); + } + /* Start from scratch */ + __raw3270_reset_device(rp); +} + +static void raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, struct irb *irb) { struct raw3270 *rp; - /* - * Unit-Check Processing: - * Expect Command Reject or Intervention Required. - */ - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { - /* Request finished abnormally. */ - if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { - set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); - return RAW3270_IO_BUSY; - } - } if (rq) { if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { if (irb->ecw[0] & SNS0_CMD_REJECT) @@ -715,7 +679,6 @@ raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, rp = view->dev; raw3270_read_modified(rp); } - return RAW3270_IO_DONE; } static struct raw3270_fn raw3270_init_fn = { diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index e1e41c286..56519cbb1 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -125,19 +125,13 @@ raw3270_request_final(struct raw3270_request *rq) void raw3270_buffer_address(struct raw3270 *, char *, unsigned short); -/* Return value of *intv (see raw3270_fn below) can be one of the following: */ -#define RAW3270_IO_DONE 0 /* request finished */ -#define RAW3270_IO_BUSY 1 /* request still active */ -#define RAW3270_IO_RETRY 2 /* retry current request */ -#define RAW3270_IO_STOP 3 /* kill current request */ - /* * Functions of a 3270 view. */ struct raw3270_fn { int (*activate)(struct raw3270_view *); void (*deactivate)(struct raw3270_view *); - int (*intv)(struct raw3270_view *, + void (*intv)(struct raw3270_view *, struct raw3270_request *, struct irb *); void (*release)(struct raw3270_view *); void (*free)(struct raw3270_view *); diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 026e38990..7a10c5633 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -17,33 +17,35 @@ #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) #define SCLP_CONSOLE_PAGES 6 +#define SCLP_EVTYP_MASK(T) (1U << (32 - (T))) + #define EVTYP_OPCMD 0x01 #define EVTYP_MSG 0x02 +#define EVTYP_CONFMGMDATA 0x04 #define EVTYP_DIAG_TEST 0x07 #define EVTYP_STATECHANGE 0x08 #define EVTYP_PMSGCMD 0x09 -#define EVTYP_CNTLPROGOPCMD 0x20 -#define EVTYP_CNTLPROGIDENT 0x0B -#define EVTYP_SIGQUIESCE 0x1D +#define EVTYP_ASYNC 0x0A +#define EVTYP_CTLPROGIDENT 0x0B +#define EVTYP_ERRNOTIFY 0x18 #define EVTYP_VT220MSG 0x1A -#define EVTYP_CONFMGMDATA 0x04 #define EVTYP_SDIAS 0x1C -#define EVTYP_ASYNC 0x0A +#define EVTYP_SIGQUIESCE 0x1D #define EVTYP_OCF 0x1E -#define EVTYP_OPCMD_MASK 0x80000000 -#define EVTYP_MSG_MASK 0x40000000 -#define EVTYP_DIAG_TEST_MASK 0x02000000 -#define EVTYP_STATECHANGE_MASK 0x01000000 -#define EVTYP_PMSGCMD_MASK 0x00800000 -#define EVTYP_CTLPROGOPCMD_MASK 0x00000001 -#define EVTYP_CTLPROGIDENT_MASK 0x00200000 -#define EVTYP_SIGQUIESCE_MASK 0x00000008 -#define EVTYP_VT220MSG_MASK 0x00000040 -#define EVTYP_CONFMGMDATA_MASK 0x10000000 -#define EVTYP_SDIAS_MASK 0x00000010 -#define EVTYP_ASYNC_MASK 0x00400000 -#define EVTYP_OCF_MASK 0x00000004 +#define EVTYP_OPCMD_MASK SCLP_EVTYP_MASK(EVTYP_OPCMD) +#define EVTYP_MSG_MASK SCLP_EVTYP_MASK(EVTYP_MSG) +#define EVTYP_CONFMGMDATA_MASK SCLP_EVTYP_MASK(EVTYP_CONFMGMDATA) +#define EVTYP_DIAG_TEST_MASK SCLP_EVTYP_MASK(EVTYP_DIAG_TEST) +#define EVTYP_STATECHANGE_MASK SCLP_EVTYP_MASK(EVTYP_STATECHANGE) +#define EVTYP_PMSGCMD_MASK SCLP_EVTYP_MASK(EVTYP_PMSGCMD) +#define EVTYP_ASYNC_MASK SCLP_EVTYP_MASK(EVTYP_ASYNC) +#define EVTYP_CTLPROGIDENT_MASK SCLP_EVTYP_MASK(EVTYP_CTLPROGIDENT) +#define EVTYP_ERRNOTIFY_MASK SCLP_EVTYP_MASK(EVTYP_ERRNOTIFY) +#define EVTYP_VT220MSG_MASK SCLP_EVTYP_MASK(EVTYP_VT220MSG) +#define EVTYP_SDIAS_MASK SCLP_EVTYP_MASK(EVTYP_SDIAS) +#define EVTYP_SIGQUIESCE_MASK SCLP_EVTYP_MASK(EVTYP_SIGQUIESCE) +#define EVTYP_OCF_MASK SCLP_EVTYP_MASK(EVTYP_OCF) #define GNRLMSGFLGS_DOM 0x8000 #define GNRLMSGFLGS_SNDALRM 0x4000 diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index d3947ea3e..e3fc75391 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -576,67 +576,6 @@ __initcall(sclp_detect_standby_memory); #endif /* CONFIG_MEMORY_HOTPLUG */ /* - * PCI I/O adapter configuration related functions. - */ -#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001 -#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001 - -#define SCLP_RECONFIG_PCI_ATPYE 2 - -struct pci_cfg_sccb { - struct sccb_header header; - u8 atype; /* adapter type */ - u8 reserved1; - u16 reserved2; - u32 aid; /* adapter identifier */ -} __packed; - -static int do_pci_configure(sclp_cmdw_t cmd, u32 fid) -{ - struct pci_cfg_sccb *sccb; - int rc; - - if (!SCLP_HAS_PCI_RECONFIG) - return -EOPNOTSUPP; - - sccb = (struct pci_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!sccb) - return -ENOMEM; - - sccb->header.length = PAGE_SIZE; - sccb->atype = SCLP_RECONFIG_PCI_ATPYE; - sccb->aid = fid; - rc = sclp_sync_request(cmd, sccb); - if (rc) - goto out; - switch (sccb->header.response_code) { - case 0x0020: - case 0x0120: - break; - default: - pr_warn("configure PCI I/O adapter failed: cmd=0x%08x response=0x%04x\n", - cmd, sccb->header.response_code); - rc = -EIO; - break; - } -out: - free_page((unsigned long) sccb); - return rc; -} - -int sclp_pci_configure(u32 fid) -{ - return do_pci_configure(SCLP_CMDW_CONFIGURE_PCI, fid); -} -EXPORT_SYMBOL(sclp_pci_configure); - -int sclp_pci_deconfigure(u32 fid) -{ - return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid); -} -EXPORT_SYMBOL(sclp_pci_deconfigure); - -/* * Channel path configuration related functions. */ diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c index f344e5bd2..90d92fbe7 100644 --- a/drivers/s390/char/sclp_cpi_sys.c +++ b/drivers/s390/char/sclp_cpi_sys.c @@ -93,7 +93,7 @@ static struct sclp_req *cpi_prepare_req(void) /* setup SCCB for Control-Program Identification */ sccb->header.length = sizeof(struct cpi_sccb); sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); - sccb->cpi_evbuf.header.type = 0x0b; + sccb->cpi_evbuf.header.type = EVTYP_CTLPROGIDENT; evb = &sccb->cpi_evbuf; /* set system type */ diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index 6804354c4..0ac520dd1 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c @@ -49,7 +49,9 @@ struct read_info_sccb { u8 _pad_117[119 - 117]; /* 117-118 */ u8 fac119; /* 119 */ u16 hcpua; /* 120-121 */ - u8 _pad_122[4096 - 122]; /* 122-4095 */ + u8 _pad_122[124 - 122]; /* 122-123 */ + u32 hmfai; /* 124-127 */ + u8 _pad_128[4096 - 128]; /* 128-4095 */ } __packed __aligned(PAGE_SIZE); static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata; @@ -155,6 +157,8 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb) sclp.mtid = (sccb->fac42 & 0x80) ? (sccb->fac42 & 31) : 0; sclp.mtid_cp = (sccb->fac42 & 0x80) ? (sccb->fac43 & 31) : 0; sclp.mtid_prev = (sccb->fac42 & 0x80) ? (sccb->fac66 & 31) : 0; + + sclp.hmfai = sccb->hmfai; } /* diff --git a/drivers/s390/char/sclp_pci.c b/drivers/s390/char/sclp_pci.c new file mode 100644 index 000000000..4dbb3dfd4 --- /dev/null +++ b/drivers/s390/char/sclp_pci.c @@ -0,0 +1,193 @@ +/* + * PCI I/O adapter configuration related functions. + * + * Copyright IBM Corp. 2016 + */ +#define KMSG_COMPONENT "sclp_cmd" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/completion.h> +#include <linux/export.h> +#include <linux/mutex.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/err.h> + +#include <asm/sclp.h> + +#include "sclp.h" + +#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001 +#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001 + +#define SCLP_ATYPE_PCI 2 + +#define SCLP_ERRNOTIFY_AQ_REPAIR 1 +#define SCLP_ERRNOTIFY_AQ_INFO_LOG 2 + +static DEFINE_MUTEX(sclp_pci_mutex); +static struct sclp_register sclp_pci_event = { + .send_mask = EVTYP_ERRNOTIFY_MASK, +}; + +struct err_notify_evbuf { + struct evbuf_header header; + u8 action; + u8 atype; + u32 fh; + u32 fid; + u8 data[0]; +} __packed; + +struct err_notify_sccb { + struct sccb_header header; + struct err_notify_evbuf evbuf; +} __packed; + +struct pci_cfg_sccb { + struct sccb_header header; + u8 atype; /* adapter type */ + u8 reserved1; + u16 reserved2; + u32 aid; /* adapter identifier */ +} __packed; + +static int do_pci_configure(sclp_cmdw_t cmd, u32 fid) +{ + struct pci_cfg_sccb *sccb; + int rc; + + if (!SCLP_HAS_PCI_RECONFIG) + return -EOPNOTSUPP; + + sccb = (struct pci_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + + sccb->header.length = PAGE_SIZE; + sccb->atype = SCLP_ATYPE_PCI; + sccb->aid = fid; + rc = sclp_sync_request(cmd, sccb); + if (rc) + goto out; + switch (sccb->header.response_code) { + case 0x0020: + case 0x0120: + break; + default: + pr_warn("configure PCI I/O adapter failed: cmd=0x%08x response=0x%04x\n", + cmd, sccb->header.response_code); + rc = -EIO; + break; + } +out: + free_page((unsigned long) sccb); + return rc; +} + +int sclp_pci_configure(u32 fid) +{ + return do_pci_configure(SCLP_CMDW_CONFIGURE_PCI, fid); +} +EXPORT_SYMBOL(sclp_pci_configure); + +int sclp_pci_deconfigure(u32 fid) +{ + return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid); +} +EXPORT_SYMBOL(sclp_pci_deconfigure); + +static void sclp_pci_callback(struct sclp_req *req, void *data) +{ + struct completion *completion = data; + + complete(completion); +} + +static int sclp_pci_check_report(struct zpci_report_error_header *report) +{ + if (report->version != 1) + return -EINVAL; + + if (report->action != SCLP_ERRNOTIFY_AQ_REPAIR && + report->action != SCLP_ERRNOTIFY_AQ_INFO_LOG) + return -EINVAL; + + if (report->length > (PAGE_SIZE - sizeof(struct err_notify_sccb))) + return -EINVAL; + + return 0; +} + +int sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid) +{ + DECLARE_COMPLETION_ONSTACK(completion); + struct err_notify_sccb *sccb; + struct sclp_req req; + int ret; + + ret = sclp_pci_check_report(report); + if (ret) + return ret; + + mutex_lock(&sclp_pci_mutex); + ret = sclp_register(&sclp_pci_event); + if (ret) + goto out_unlock; + + if (!(sclp_pci_event.sclp_receive_mask & EVTYP_ERRNOTIFY_MASK)) { + ret = -EOPNOTSUPP; + goto out_unregister; + } + + sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) { + ret = -ENOMEM; + goto out_unregister; + } + + memset(&req, 0, sizeof(req)); + req.callback_data = &completion; + req.callback = sclp_pci_callback; + req.command = SCLP_CMDW_WRITE_EVENT_DATA; + req.status = SCLP_REQ_FILLED; + req.sccb = sccb; + + sccb->evbuf.header.length = sizeof(sccb->evbuf) + report->length; + sccb->evbuf.header.type = EVTYP_ERRNOTIFY; + sccb->header.length = sizeof(sccb->header) + sccb->evbuf.header.length; + + sccb->evbuf.action = report->action; + sccb->evbuf.atype = SCLP_ATYPE_PCI; + sccb->evbuf.fh = fh; + sccb->evbuf.fid = fid; + + memcpy(sccb->evbuf.data, report->data, report->length); + + ret = sclp_add_request(&req); + if (ret) + goto out_free_req; + + wait_for_completion(&completion); + if (req.status != SCLP_REQ_DONE) { + pr_warn("request failed (status=0x%02x)\n", + req.status); + ret = -EIO; + goto out_free_req; + } + + if (sccb->header.response_code != 0x0020) { + pr_warn("request failed with response code 0x%x\n", + sccb->header.response_code); + ret = -EIO; + } + +out_free_req: + free_page((unsigned long) sccb); +out_unregister: + sclp_unregister(&sclp_pci_event); +out_unlock: + mutex_unlock(&sclp_pci_mutex); + return ret; +} diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index e96fc7fd9..272cb6cd1 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -92,6 +92,7 @@ struct tty3270 { unsigned char inattr; /* Visible/invisible input. */ int throttle, attn; /* tty throttle/unthrottle. */ struct tasklet_struct readlet; /* Tasklet to issue read request. */ + struct tasklet_struct hanglet; /* Tasklet to hang up the tty. */ struct kbd_data *kbd; /* key_maps stuff. */ /* Escape sequence parsing. */ @@ -319,6 +320,27 @@ tty3270_blank_line(struct tty3270 *tp) } /* + * Create a blank screen and remove all lines from the history. + */ +static void +tty3270_blank_screen(struct tty3270 *tp) +{ + struct string *s, *n; + int i; + + for (i = 0; i < tp->view.rows - 2; i++) + tp->screen[i].len = 0; + tp->nr_up = 0; + list_for_each_entry_safe(s, n, &tp->lines, list) { + list_del(&s->list); + if (!list_empty(&s->update)) + list_del(&s->update); + tp->nr_lines--; + free_string(&tp->freemem, s); + } +} + +/* * Write request completion callback. */ static void @@ -405,7 +427,10 @@ tty3270_update(struct tty3270 *tp) if (raw3270_request_add_data(wrq, str, len) != 0) break; list_del_init(&s->update); - sba = s->string + s->len - 3; + if (s->string[s->len - 4] == TO_RA) + sba = s->string + s->len - 3; + else + sba = invalid_sba; } if (list_empty(&tp->update)) updated |= TTY_UPDATE_LIST; @@ -622,6 +647,16 @@ tty3270_issue_read(struct tty3270 *tp, int lock) } /* + * Hang up the tty + */ +static void +tty3270_hangup_tasklet(struct tty3270 *tp) +{ + tty_port_tty_hangup(&tp->port, true); + raw3270_put_view(&tp->view); +} + +/* * Switch to the tty view. */ static int @@ -642,7 +677,7 @@ tty3270_deactivate(struct raw3270_view *view) del_timer(&tp->timer); } -static int +static void tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) { /* Handle ATTN. Schedule tasklet to read aid. */ @@ -654,17 +689,19 @@ tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) } if (rq) { - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { rq->rc = -EIO; - else + raw3270_get_view(&tp->view); + tasklet_schedule(&tp->hanglet); + } else { /* Normal end. Copy residual count. */ rq->rescnt = irb->scsw.cmd.count; + } } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { /* Interrupt without an outstanding request -> update all */ tp->update_flags = TTY_UPDATE_ALL; tty3270_set_timer(tp, 1); } - return RAW3270_IO_DONE; } /* @@ -716,6 +753,9 @@ tty3270_alloc_view(void) tasklet_init(&tp->readlet, (void (*)(unsigned long)) tty3270_read_tasklet, (unsigned long) tp->read); + tasklet_init(&tp->hanglet, + (void (*)(unsigned long)) tty3270_hangup_tasklet, + (unsigned long) tp); INIT_WORK(&tp->resize_work, tty3270_resize_work); return tp; @@ -814,6 +854,7 @@ static void tty3270_resize_work(struct work_struct *work) return; /* Switch to new output size */ spin_lock_bh(&tp->view.lock); + tty3270_blank_screen(tp); oscreen = tp->screen; orows = tp->view.rows; tp->view.model = tp->n_model; @@ -824,7 +865,6 @@ static void tty3270_resize_work(struct work_struct *work) free_string(&tp->freemem, tp->status); tty3270_create_prompt(tp); tty3270_create_status(tp); - tp->nr_up = 0; while (tp->nr_lines < tp->view.rows - 2) tty3270_blank_line(tp); tp->update_flags = TTY_UPDATE_ALL; @@ -838,6 +878,7 @@ static void tty3270_resize_work(struct work_struct *work) ws.ws_row = tp->view.rows - 2; ws.ws_col = tp->view.cols; tty_do_resize(tty, &ws); + tty_kref_put(tty); } static void @@ -845,6 +886,8 @@ tty3270_resize(struct raw3270_view *view, int model, int rows, int cols) { struct tty3270 *tp = container_of(view, struct tty3270, view); + if (tp->n_model == model && tp->n_rows == rows && tp->n_cols == cols) + return; tp->n_model = model; tp->n_rows = rows; tp->n_cols = cols; @@ -923,10 +966,8 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) tty->winsize.ws_row = tp->view.rows - 2; tty->winsize.ws_col = tp->view.cols; tp->port.low_latency = 0; - /* why to reassign? */ - tty_port_tty_set(&tp->port, tty); tp->inattr = TF_INPUT; - return tty_port_install(&tp->port, driver, tty); + goto port_install; } if (tty3270_max_index < tty->index + 1) tty3270_max_index = tty->index + 1; @@ -952,7 +993,6 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) return rc; } - tty_port_tty_set(&tp->port, tty); tp->port.low_latency = 0; tty->winsize.ws_row = tp->view.rows - 2; tty->winsize.ws_col = tp->view.cols; @@ -974,6 +1014,7 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) raw3270_activate_view(&tp->view); +port_install: rc = tty_port_install(&tp->port, driver, tty); if (rc) { raw3270_put_view(&tp->view); @@ -1010,18 +1051,18 @@ tty3270_close(struct tty_struct *tty, struct file * filp) if (tty->count > 1) return; - if (tp) { - tty->driver_data = NULL; + if (tp) tty_port_tty_set(&tp->port, NULL); - } } static void tty3270_cleanup(struct tty_struct *tty) { struct tty3270 *tp = tty->driver_data; - if (tp) + if (tp) { + tty->driver_data = NULL; raw3270_put_view(&tp->view); + } } /* @@ -1788,7 +1829,22 @@ tty3270_unthrottle(struct tty_struct * tty) static void tty3270_hangup(struct tty_struct *tty) { - // FIXME: implement + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return; + spin_lock_bh(&tp->view.lock); + tp->cx = tp->saved_cx = 0; + tp->cy = tp->saved_cy = 0; + tp->highlight = tp->saved_highlight = TAX_RESET; + tp->f_color = tp->saved_f_color = TAC_RESET; + tty3270_blank_screen(tp); + while (tp->nr_lines < tp->view.rows - 2) + tty3270_blank_line(tp); + tp->update_flags = TTY_UPDATE_ALL; + spin_unlock_bh(&tp->view.lock); + tty3270_set_timer(tp, 1); } static void @@ -1804,7 +1860,7 @@ static int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd, tp = tty->driver_data; if (!tp) return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) + if (tty_io_error(tty)) return -EIO; return kbd_ioctl(tp->kbd, cmd, arg); } @@ -1818,7 +1874,7 @@ static long tty3270_compat_ioctl(struct tty_struct *tty, tp = tty->driver_data; if (!tp) return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) + if (tty_io_error(tty)) return -EIO; return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg)); } diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 24ec282e1..327255da1 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -787,7 +787,7 @@ static enum ap_wait ap_sm_setirq_wait(struct ap_device *ap_dev) /* * AP state machine jump table */ -ap_func_t *ap_jumptable[NR_AP_STATES][NR_AP_EVENTS] = { +static ap_func_t *ap_jumptable[NR_AP_STATES][NR_AP_EVENTS] = { [AP_STATE_RESET_START] = { [AP_EVENT_POLL] = ap_sm_reset, [AP_EVENT_TIMEOUT] = ap_sm_nop, diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index c3e22523f..ad17fc588 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -642,7 +642,7 @@ static void ctcmpc_send_sweep_req(struct channel *rch) kfree(header); - dev->trans_start = jiffies; + netif_trans_update(dev); skb_queue_tail(&ch->sweep_queue, sweep_skb); fsm_addtimer(&ch->sweep_timer, 100, CTC_EVENT_RSWEEP_TIMER, ch); @@ -911,7 +911,7 @@ static int ctcm_tx(struct sk_buff *skb, struct net_device *dev) if (ctcm_test_and_set_busy(dev)) return NETDEV_TX_BUSY; - dev->trans_start = jiffies; + netif_trans_update(dev); if (ctcm_transmit_skb(priv->channel[CTCM_WRITE], skb) != 0) return NETDEV_TX_BUSY; return NETDEV_TX_OK; @@ -994,7 +994,7 @@ static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev) goto done; } - dev->trans_start = jiffies; + netif_trans_update(dev); if (ctcmpc_transmit_skb(priv->channel[CTCM_WRITE], skb) != 0) { CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, "%s(%s): device error - dropped", diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c index edf16bfba..c103fc7ef 100644 --- a/drivers/s390/net/ctcm_mpc.c +++ b/drivers/s390/net/ctcm_mpc.c @@ -671,7 +671,7 @@ static void ctcmpc_send_sweep_resp(struct channel *rch) kfree(header); - dev->trans_start = jiffies; + netif_trans_update(dev); skb_queue_tail(&ch->sweep_queue, sweep_skb); fsm_addtimer(&ch->sweep_timer, 100, CTC_EVENT_RSWEEP_TIMER, ch); diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 0ba3a2f81..b0e8ffdf8 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -1407,7 +1407,7 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev) IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n"); return NETDEV_TX_BUSY; } - dev->trans_start = jiffies; + netif_trans_update(dev); rc = netiucv_transmit_skb(privptr->conn, skb); netiucv_clear_busy(dev); return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 787153764..b7b74776e 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3481,7 +3481,7 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, } } - queue->card->dev->trans_start = jiffies; + netif_trans_update(queue->card->dev); if (queue->card->options.performance_stats) { queue->card->perf_stats.outbound_do_qdio_cnt++; queue->card->perf_stats.outbound_do_qdio_start_time = diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 80b1979e8..df036b872 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1051,6 +1051,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) qeth_l2_set_offline(cgdev); if (card->dev) { + netif_napi_del(&card->napi); unregister_netdev(card->dev); card->dev = NULL; } diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index ac544330d..709b52339 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -3226,6 +3226,7 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev) qeth_l3_set_offline(cgdev); if (card->dev) { + netif_napi_del(&card->napi); unregister_netdev(card->dev); card->dev = NULL; } diff --git a/drivers/s390/scsi/zfcp_unit.c b/drivers/s390/scsi/zfcp_unit.c index 157d3d203..9310a547b 100644 --- a/drivers/s390/scsi/zfcp_unit.c +++ b/drivers/s390/scsi/zfcp_unit.c @@ -26,7 +26,8 @@ void zfcp_unit_scsi_scan(struct zfcp_unit *unit) lun = scsilun_to_int((struct scsi_lun *) &unit->fcp_lun); if (rport && rport->port_state == FC_PORTSTATE_ONLINE) - scsi_scan_target(&rport->dev, 0, rport->scsi_target_id, lun, 1); + scsi_scan_target(&rport->dev, 0, rport->scsi_target_id, lun, + SCSI_SCAN_MANUAL); } static void zfcp_unit_scsi_scan_work(struct work_struct *work) |