diff options
Diffstat (limited to 'drivers/s390')
67 files changed, 2654 insertions, 2016 deletions
diff --git a/drivers/s390/block/Kconfig b/drivers/s390/block/Kconfig index 07883197f47..8e477bb1f3f 100644 --- a/drivers/s390/block/Kconfig +++ b/drivers/s390/block/Kconfig @@ -2,7 +2,8 @@ comment "S/390 block device drivers" depends on S390 && BLOCK config BLK_DEV_XPRAM - tristate "XPRAM disk support" + def_tristate m + prompt "XPRAM disk support" depends on S390 && BLOCK help Select this option if you want to use your expanded storage on S/390 @@ -12,13 +13,15 @@ config BLK_DEV_XPRAM xpram. If unsure, say "N". config DCSSBLK - tristate "DCSSBLK support" + def_tristate m + prompt "DCSSBLK support" depends on S390 && BLOCK help Support for dcss block device config DASD - tristate "Support for DASD devices" + def_tristate y + prompt "Support for DASD devices" depends on CCW && BLOCK select IOSCHED_DEADLINE help @@ -27,28 +30,32 @@ config DASD natively on a single image or an LPAR. config DASD_PROFILE - bool "Profiling support for dasd devices" + def_bool y + prompt "Profiling support for dasd devices" depends on DASD help Enable this option if you want to see profiling information in /proc/dasd/statistics. config DASD_ECKD - tristate "Support for ECKD Disks" + def_tristate y + prompt "Support for ECKD Disks" depends on DASD help ECKD devices are the most commonly used devices. You should enable this option unless you are very sure to have no ECKD device. config DASD_FBA - tristate "Support for FBA Disks" + def_tristate y + prompt "Support for FBA Disks" depends on DASD help Select this option to be able to access FBA devices. It is safe to say "Y". config DASD_DIAG - tristate "Support for DIAG access to Disks" + def_tristate y + prompt "Support for DIAG access to Disks" depends on DASD help Select this option if you want to use Diagnose250 command to access @@ -56,7 +63,8 @@ config DASD_DIAG say "N". config DASD_EER - bool "Extended error reporting (EER)" + def_bool y + prompt "Extended error reporting (EER)" depends on DASD help This driver provides a character device interface to the diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index fb613d70c2c..794bfd96226 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -11,6 +11,7 @@ #define KMSG_COMPONENT "dasd" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/kernel_stat.h> #include <linux/kmod.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -368,6 +369,11 @@ dasd_state_ready_to_online(struct dasd_device * device) device->state = DASD_STATE_ONLINE; if (device->block) { dasd_schedule_block_bh(device->block); + if ((device->features & DASD_FEATURE_USERAW)) { + disk = device->block->gdp; + kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE); + return 0; + } disk = device->block->bdev->bd_disk; disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); while ((part = disk_part_iter_next(&piter))) @@ -393,7 +399,7 @@ static int dasd_state_online_to_ready(struct dasd_device *device) return rc; } device->state = DASD_STATE_READY; - if (device->block) { + if (device->block && !(device->features & DASD_FEATURE_USERAW)) { disk = device->block->bdev->bd_disk; disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); while ((part = disk_part_iter_next(&piter))) @@ -744,10 +750,6 @@ struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength, char *data; int size; - /* Sanity checks */ - BUG_ON(datasize > PAGE_SIZE || - (cplength*sizeof(struct ccw1)) > PAGE_SIZE); - size = (sizeof(struct dasd_ccw_req) + 7L) & -8L; if (cplength > 0) size += cplength * sizeof(struct ccw1); @@ -853,7 +855,6 @@ int dasd_term_IO(struct dasd_ccw_req *cqr) rc = ccw_device_clear(device->cdev, (long) cqr); switch (rc) { case 0: /* termination successful */ - cqr->retries--; cqr->status = DASD_CQR_CLEAR_PENDING; cqr->stopclk = get_clock(); cqr->starttime = 0; @@ -905,6 +906,16 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) return rc; } device = (struct dasd_device *) cqr->startdev; + if (((cqr->block && + test_bit(DASD_FLAG_LOCK_STOLEN, &cqr->block->base->flags)) || + test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags)) && + !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { + DBF_DEV_EVENT(DBF_DEBUG, device, "start_IO: return request %p " + "because of stolen lock", cqr); + cqr->status = DASD_CQR_ERROR; + cqr->intrc = -EPERM; + return -EPERM; + } if (cqr->retries < 0) { /* internal error 14 - start_IO run out of retries */ sprintf(errorstring, "14 %p", cqr); @@ -916,6 +927,11 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) cqr->startclk = get_clock(); cqr->starttime = jiffies; cqr->retries--; + if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) { + cqr->lpm &= device->path_data.opm; + if (!cqr->lpm) + cqr->lpm = device->path_data.opm; + } if (cqr->cpmode == 1) { rc = ccw_device_tm_start(device->cdev, cqr->cpaddr, (long) cqr, cqr->lpm); @@ -928,35 +944,53 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) cqr->status = DASD_CQR_IN_IO; break; case -EBUSY: - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "start_IO: device busy, retry later"); break; case -ETIMEDOUT: - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "start_IO: request timeout, retry later"); break; case -EACCES: - /* -EACCES indicates that the request used only a - * subset of the available pathes and all these - * pathes are gone. - * Do a retry with all available pathes. + /* -EACCES indicates that the request used only a subset of the + * available paths and all these paths are gone. If the lpm of + * this request was only a subset of the opm (e.g. the ppm) then + * we just do a retry with all available paths. + * If we already use the full opm, something is amiss, and we + * need a full path verification. */ - cqr->lpm = LPM_ANYPATH; - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", - "start_IO: selected pathes gone," - " retry on all pathes"); + if (test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) { + DBF_DEV_EVENT(DBF_WARNING, device, + "start_IO: selected paths gone (%x)", + cqr->lpm); + } else if (cqr->lpm != device->path_data.opm) { + cqr->lpm = device->path_data.opm; + DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + "start_IO: selected paths gone," + " retry on all paths"); + } else { + DBF_DEV_EVENT(DBF_WARNING, device, "%s", + "start_IO: all paths in opm gone," + " do path verification"); + dasd_generic_last_path_gone(device); + device->path_data.opm = 0; + device->path_data.ppm = 0; + device->path_data.npm = 0; + device->path_data.tbvpm = + ccw_device_get_path_mask(device->cdev); + } break; case -ENODEV: - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "start_IO: -ENODEV device gone, retry"); break; case -EIO: - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "start_IO: -EIO device gone, retry"); break; case -EINVAL: /* most likely caused in power management context */ - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "start_IO: -EINVAL device currently " "not accessible"); break; @@ -1076,6 +1110,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, unsigned long long now; int expires; + kstat_cpu(smp_processor_id()).irqs[IOINT_DAS]++; if (IS_ERR(irb)) { switch (PTR_ERR(irb)) { case -EIO: @@ -1094,16 +1129,11 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, } now = get_clock(); - - /* check for unsolicited interrupts */ cqr = (struct dasd_ccw_req *) intparm; - if (!cqr || ((scsw_cc(&irb->scsw) == 1) && - (scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) && - ((scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND) || - (scsw_stctl(&irb->scsw) == (SCSW_STCTL_STATUS_PEND | - SCSW_STCTL_ALERT_STATUS))))) { - if (cqr && cqr->status == DASD_CQR_IN_IO) - cqr->status = DASD_CQR_QUEUED; + /* check for conditions that should be handled immediately */ + if (!cqr || + !(scsw_dstat(&irb->scsw) == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && + scsw_cstat(&irb->scsw) == 0)) { if (cqr) memcpy(&cqr->irb, irb, sizeof(*irb)); device = dasd_device_from_cdev_locked(cdev); @@ -1114,17 +1144,14 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, dasd_put_device(device); return; } - device->discipline->dump_sense_dbf(device, irb, - "unsolicited"); - if ((device->features & DASD_FEATURE_ERPLOG)) - device->discipline->dump_sense(device, cqr, - irb); - dasd_device_clear_timer(device); - device->discipline->handle_unsolicited_interrupt(device, - irb); + 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); dasd_put_device(device); - return; } + if (!cqr) + return; device = (struct dasd_device *) cqr->startdev; if (!device || @@ -1164,25 +1191,19 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, struct dasd_ccw_req, devlist); } } else { /* error */ - memcpy(&cqr->irb, irb, sizeof(struct irb)); - /* log sense for every failed I/O to s390 debugfeature */ - dasd_log_sense_dbf(cqr, irb); - if (device->features & DASD_FEATURE_ERPLOG) { - dasd_log_sense(cqr, irb); - } - /* * If we don't want complex ERP for this request, then just * reset this and retry it in the fastpath */ if (!test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags) && cqr->retries > 0) { - if (cqr->lpm == LPM_ANYPATH) + if (cqr->lpm == device->path_data.opm) DBF_DEV_EVENT(DBF_DEBUG, device, "default ERP in fastpath " "(%i retries left)", cqr->retries); - cqr->lpm = LPM_ANYPATH; + if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) + cqr->lpm = device->path_data.opm; cqr->status = DASD_CQR_QUEUED; next = cqr; } else @@ -1210,13 +1231,13 @@ enum uc_todo dasd_generic_uc_handler(struct ccw_device *cdev, struct irb *irb) goto out; if (test_bit(DASD_FLAG_OFFLINE, &device->flags) || device->state != device->target || - !device->discipline->handle_unsolicited_interrupt){ + !device->discipline->check_for_device_change){ dasd_put_device(device); goto out; } - - dasd_device_clear_timer(device); - device->discipline->handle_unsolicited_interrupt(device, irb); + if (device->discipline->dump_sense_dbf) + device->discipline->dump_sense_dbf(device, irb, "uc"); + device->discipline->check_for_device_change(device, NULL, irb); dasd_put_device(device); out: return UC_TODO_RETRY; @@ -1366,8 +1387,14 @@ static void __dasd_device_start_head(struct dasd_device *device) cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); if (cqr->status != DASD_CQR_QUEUED) return; - /* when device is stopped, return request to previous layer */ - if (device->stopped) { + /* when device is stopped, return request to previous layer + * exception: only the disconnect or unresumed bits are set and the + * cqr is a path verification request + */ + if (device->stopped && + !(!(device->stopped & ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM)) + && test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))) { + cqr->intrc = -EAGAIN; cqr->status = DASD_CQR_CLEARED; dasd_schedule_device_bh(device); return; @@ -1383,6 +1410,23 @@ static void __dasd_device_start_head(struct dasd_device *device) dasd_device_set_timer(device, 50); } +static void __dasd_device_check_path_events(struct dasd_device *device) +{ + int rc; + + if (device->path_data.tbvpm) { + if (device->stopped & ~(DASD_STOPPED_DC_WAIT | + DASD_UNRESUMED_PM)) + return; + rc = device->discipline->verify_path( + device, device->path_data.tbvpm); + if (rc) + dasd_device_set_timer(device, 50); + else + device->path_data.tbvpm = 0; + } +}; + /* * Go through all request on the dasd_device request queue, * terminate them on the cdev if necessary, and return them to the @@ -1457,6 +1501,7 @@ static void dasd_device_tasklet(struct dasd_device *device) __dasd_device_check_expire(device); /* find final requests on ccw queue */ __dasd_device_process_ccw_queue(device, &final_queue); + __dasd_device_check_path_events(device); spin_unlock_irq(get_ccwdev_lock(device->cdev)); /* Now call the callback function of requests with final status */ __dasd_device_process_final_queue(device, &final_queue); @@ -1613,7 +1658,12 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) continue; if (cqr->status != DASD_CQR_FILLED) /* could be failed */ continue; - + if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags) && + !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = -EPERM; + continue; + } /* Non-temporary stop condition will trigger fail fast */ if (device->stopped & ~DASD_STOPPED_PENDING && test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && @@ -1621,7 +1671,6 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) cqr->status = DASD_CQR_FAILED; continue; } - /* Don't try to start requests if device is stopped */ if (interruptible) { rc = wait_event_interruptible( @@ -1706,13 +1755,18 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr) int rc; device = cqr->startdev; + if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags) && + !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = -EPERM; + return -EIO; + } spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = _dasd_term_running_cqr(device); if (rc) { spin_unlock_irq(get_ccwdev_lock(device->cdev)); return rc; } - cqr->callback = dasd_wakeup_cb; cqr->callback_data = DASD_SLEEPON_START_TAG; cqr->status = DASD_CQR_QUEUED; @@ -2016,6 +2070,13 @@ static void __dasd_block_start_head(struct dasd_block *block) list_for_each_entry(cqr, &block->ccw_queue, blocklist) { if (cqr->status != DASD_CQR_FILLED) continue; + if (test_bit(DASD_FLAG_LOCK_STOLEN, &block->base->flags) && + !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = -EPERM; + dasd_schedule_block_bh(block); + continue; + } /* Non-temporary stop condition will trigger fail fast */ if (block->base->stopped & ~DASD_STOPPED_PENDING && test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && @@ -2201,8 +2262,20 @@ static void dasd_setup_queue(struct dasd_block *block) { int max; - blk_queue_logical_block_size(block->request_queue, block->bp_block); - max = block->base->discipline->max_blocks << block->s2b_shift; + if (block->base->features & DASD_FEATURE_USERAW) { + /* + * the max_blocks value for raw_track access is 256 + * it is higher than the native ECKD value because we + * only need one ccw per track + * so the max_hw_sectors are + * 2048 x 512B = 1024kB = 16 tracks + */ + max = 2048; + } else { + max = block->base->discipline->max_blocks << block->s2b_shift; + } + blk_queue_logical_block_size(block->request_queue, + block->bp_block); blk_queue_max_hw_sectors(block->request_queue, max); blk_queue_max_segments(block->request_queue, -1L); /* with page sized segments we can translate each segement into @@ -2588,10 +2661,53 @@ int dasd_generic_set_offline(struct ccw_device *cdev) return 0; } +int dasd_generic_last_path_gone(struct dasd_device *device) +{ + struct dasd_ccw_req *cqr; + + dev_warn(&device->cdev->dev, "No operational channel path is left " + "for the device\n"); + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "last path gone"); + /* First of all call extended error reporting. */ + dasd_eer_write(device, NULL, DASD_EER_NOPATH); + + if (device->state < DASD_STATE_BASIC) + return 0; + /* Device is active. We want to keep it. */ + list_for_each_entry(cqr, &device->ccw_queue, devlist) + if ((cqr->status == DASD_CQR_IN_IO) || + (cqr->status == DASD_CQR_CLEAR_PENDING)) { + cqr->status = DASD_CQR_QUEUED; + cqr->retries++; + } + dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT); + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); + return 1; +} +EXPORT_SYMBOL_GPL(dasd_generic_last_path_gone); + +int dasd_generic_path_operational(struct dasd_device *device) +{ + dev_info(&device->cdev->dev, "A channel path to the device has become " + "operational\n"); + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "path operational"); + dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT); + if (device->stopped & DASD_UNRESUMED_PM) { + dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM); + dasd_restore_device(device); + return 1; + } + dasd_schedule_device_bh(device); + if (device->block) + dasd_schedule_block_bh(device->block); + return 1; +} +EXPORT_SYMBOL_GPL(dasd_generic_path_operational); + int dasd_generic_notify(struct ccw_device *cdev, int event) { struct dasd_device *device; - struct dasd_ccw_req *cqr; int ret; device = dasd_device_from_cdev_locked(cdev); @@ -2602,41 +2718,64 @@ int dasd_generic_notify(struct ccw_device *cdev, int event) case CIO_GONE: case CIO_BOXED: case CIO_NO_PATH: - /* First of all call extended error reporting. */ - dasd_eer_write(device, NULL, DASD_EER_NOPATH); - - if (device->state < DASD_STATE_BASIC) - break; - /* Device is active. We want to keep it. */ - list_for_each_entry(cqr, &device->ccw_queue, devlist) - if (cqr->status == DASD_CQR_IN_IO) { - cqr->status = DASD_CQR_QUEUED; - cqr->retries++; - } - dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT); - dasd_device_clear_timer(device); - dasd_schedule_device_bh(device); - ret = 1; + device->path_data.opm = 0; + device->path_data.ppm = 0; + device->path_data.npm = 0; + ret = dasd_generic_last_path_gone(device); break; case CIO_OPER: - /* FIXME: add a sanity check. */ - dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT); - if (device->stopped & DASD_UNRESUMED_PM) { - dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM); - dasd_restore_device(device); - ret = 1; - break; - } - dasd_schedule_device_bh(device); - if (device->block) - dasd_schedule_block_bh(device->block); ret = 1; + if (device->path_data.opm) + ret = dasd_generic_path_operational(device); break; } dasd_put_device(device); return ret; } +void dasd_generic_path_event(struct ccw_device *cdev, int *path_event) +{ + int chp; + __u8 oldopm, eventlpm; + struct dasd_device *device; + + device = dasd_device_from_cdev_locked(cdev); + if (IS_ERR(device)) + return; + for (chp = 0; chp < 8; chp++) { + eventlpm = 0x80 >> chp; + if (path_event[chp] & PE_PATH_GONE) { + oldopm = device->path_data.opm; + device->path_data.opm &= ~eventlpm; + device->path_data.ppm &= ~eventlpm; + device->path_data.npm &= ~eventlpm; + if (oldopm && !device->path_data.opm) + dasd_generic_last_path_gone(device); + } + if (path_event[chp] & PE_PATH_AVAILABLE) { + device->path_data.opm &= ~eventlpm; + device->path_data.ppm &= ~eventlpm; + device->path_data.npm &= ~eventlpm; + device->path_data.tbvpm |= eventlpm; + dasd_schedule_device_bh(device); + } + } + dasd_put_device(device); +} +EXPORT_SYMBOL_GPL(dasd_generic_path_event); + +int dasd_generic_verify_path(struct dasd_device *device, __u8 lpm) +{ + if (!device->path_data.opm && lpm) { + device->path_data.opm = lpm; + dasd_generic_path_operational(device); + } else + device->path_data.opm |= lpm; + return 0; +} +EXPORT_SYMBOL_GPL(dasd_generic_verify_path); + + int dasd_generic_pm_freeze(struct ccw_device *cdev) { struct dasd_ccw_req *cqr, *n; @@ -2646,6 +2785,10 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev) if (IS_ERR(device)) return PTR_ERR(device); + + if (device->discipline->freeze) + rc = device->discipline->freeze(device); + /* disallow new I/O */ dasd_device_set_stop_bits(device, DASD_STOPPED_PM); /* clear active requests */ @@ -2682,9 +2825,6 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev) list_splice_tail(&freeze_queue, &device->ccw_queue); spin_unlock_irq(get_ccwdev_lock(cdev)); - if (device->discipline->freeze) - rc = device->discipline->freeze(device); - dasd_put_device(device); return rc; } diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 968c76cf712..1654a24817b 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -152,9 +152,9 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); opm = ccw_device_get_path_mask(device->cdev); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); - //FIXME: start with get_opm ? if (erp->lpm == 0) - erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum); + erp->lpm = device->path_data.opm & + ~(erp->irb.esw.esw0.sublog.lpum); else erp->lpm &= ~(erp->irb.esw.esw0.sublog.lpum); @@ -270,10 +270,11 @@ static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp) { erp->function = dasd_3990_erp_action_1; dasd_3990_erp_alternate_path(erp); - if (erp->status == DASD_CQR_FAILED) { + if (erp->status == DASD_CQR_FAILED && + !test_bit(DASD_CQR_VERIFY_PATH, &erp->flags)) { erp->status = DASD_CQR_FILLED; erp->retries = 10; - erp->lpm = LPM_ANYPATH; + erp->lpm = erp->startdev->path_data.opm; erp->function = dasd_3990_erp_action_1_sec; } return erp; @@ -1907,15 +1908,14 @@ dasd_3990_erp_compound_retry(struct dasd_ccw_req * erp, char *sense) static void dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense) { - if (sense[25] & DASD_SENSE_BIT_3) { dasd_3990_erp_alternate_path(erp); - if (erp->status == DASD_CQR_FAILED) { + if (erp->status == DASD_CQR_FAILED && + !test_bit(DASD_CQR_VERIFY_PATH, &erp->flags)) { /* reset the lpm and the status to be able to * try further actions. */ - - erp->lpm = 0; + erp->lpm = erp->startdev->path_data.opm; erp->status = DASD_CQR_NEED_ERP; } } diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 8d41f3ed38d..cb6a67bc89f 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -208,6 +208,8 @@ dasd_feature_list(char *str, char **endp) features |= DASD_FEATURE_READONLY; else if (len == 4 && !strncmp(str, "diag", 4)) features |= DASD_FEATURE_USEDIAG; + else if (len == 3 && !strncmp(str, "raw", 3)) + features |= DASD_FEATURE_USERAW; else if (len == 6 && !strncmp(str, "erplog", 6)) features |= DASD_FEATURE_ERPLOG; else if (len == 8 && !strncmp(str, "failfast", 8)) @@ -639,6 +641,7 @@ dasd_put_device_wake(struct dasd_device *device) { wake_up(&dasd_delete_wq); } +EXPORT_SYMBOL_GPL(dasd_put_device_wake); /* * Return dasd_device structure associated with cdev. @@ -856,7 +859,7 @@ dasd_use_diag_store(struct device *dev, struct device_attribute *attr, spin_lock(&dasd_devmap_lock); /* Changing diag discipline flag is only allowed in offline state. */ rc = count; - if (!devmap->device) { + if (!devmap->device && !(devmap->features & DASD_FEATURE_USERAW)) { if (val) devmap->features |= DASD_FEATURE_USEDIAG; else @@ -869,6 +872,56 @@ dasd_use_diag_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store); +/* + * use_raw controls whether the driver should give access to raw eckd data or + * operate in standard mode + */ +static ssize_t +dasd_use_raw_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dasd_devmap *devmap; + int use_raw; + + devmap = dasd_find_busid(dev_name(dev)); + if (!IS_ERR(devmap)) + use_raw = (devmap->features & DASD_FEATURE_USERAW) != 0; + else + use_raw = (DASD_FEATURE_DEFAULT & DASD_FEATURE_USERAW) != 0; + return sprintf(buf, use_raw ? "1\n" : "0\n"); +} + +static ssize_t +dasd_use_raw_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_devmap *devmap; + ssize_t rc; + unsigned long val; + + devmap = dasd_devmap_from_cdev(to_ccwdev(dev)); + if (IS_ERR(devmap)) + return PTR_ERR(devmap); + + if ((strict_strtoul(buf, 10, &val) != 0) || val > 1) + return -EINVAL; + + spin_lock(&dasd_devmap_lock); + /* Changing diag discipline flag is only allowed in offline state. */ + rc = count; + if (!devmap->device && !(devmap->features & DASD_FEATURE_USEDIAG)) { + if (val) + devmap->features |= DASD_FEATURE_USERAW; + else + devmap->features &= ~DASD_FEATURE_USERAW; + } else + rc = -EPERM; + spin_unlock(&dasd_devmap_lock); + return rc; +} + +static DEVICE_ATTR(raw_track_access, 0644, dasd_use_raw_show, + dasd_use_raw_store); + static ssize_t dasd_discipline_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1126,6 +1179,103 @@ dasd_expires_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store); +static ssize_t dasd_reservation_policy_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dasd_devmap *devmap; + int rc = 0; + + devmap = dasd_find_busid(dev_name(dev)); + if (IS_ERR(devmap)) { + rc = snprintf(buf, PAGE_SIZE, "ignore\n"); + } else { + spin_lock(&dasd_devmap_lock); + if (devmap->features & DASD_FEATURE_FAILONSLCK) + rc = snprintf(buf, PAGE_SIZE, "fail\n"); + else + rc = snprintf(buf, PAGE_SIZE, "ignore\n"); + spin_unlock(&dasd_devmap_lock); + } + return rc; +} + +static ssize_t dasd_reservation_policy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_devmap *devmap; + int rc; + + devmap = dasd_devmap_from_cdev(to_ccwdev(dev)); + if (IS_ERR(devmap)) + return PTR_ERR(devmap); + rc = 0; + spin_lock(&dasd_devmap_lock); + if (sysfs_streq("ignore", buf)) + devmap->features &= ~DASD_FEATURE_FAILONSLCK; + else if (sysfs_streq("fail", buf)) + devmap->features |= DASD_FEATURE_FAILONSLCK; + else + rc = -EINVAL; + if (devmap->device) + devmap->device->features = devmap->features; + spin_unlock(&dasd_devmap_lock); + if (rc) + return rc; + else + return count; +} + +static DEVICE_ATTR(reservation_policy, 0644, + dasd_reservation_policy_show, dasd_reservation_policy_store); + +static ssize_t dasd_reservation_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dasd_device *device; + int rc = 0; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return snprintf(buf, PAGE_SIZE, "none\n"); + + if (test_bit(DASD_FLAG_IS_RESERVED, &device->flags)) + rc = snprintf(buf, PAGE_SIZE, "reserved\n"); + else if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags)) + rc = snprintf(buf, PAGE_SIZE, "lost\n"); + else + rc = snprintf(buf, PAGE_SIZE, "none\n"); + dasd_put_device(device); + return rc; +} + +static ssize_t dasd_reservation_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_device *device; + int rc = 0; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + if (sysfs_streq("reset", buf)) + clear_bit(DASD_FLAG_LOCK_STOLEN, &device->flags); + else + rc = -EINVAL; + dasd_put_device(device); + + if (rc) + return rc; + else + return count; +} + +static DEVICE_ATTR(last_known_reservation_state, 0644, + dasd_reservation_state_show, dasd_reservation_state_store); + static struct attribute * dasd_attrs[] = { &dev_attr_readonly.attr, &dev_attr_discipline.attr, @@ -1134,10 +1284,13 @@ static struct attribute * dasd_attrs[] = { &dev_attr_vendor.attr, &dev_attr_uid.attr, &dev_attr_use_diag.attr, + &dev_attr_raw_track_access.attr, &dev_attr_eer_enabled.attr, &dev_attr_erplog.attr, &dev_attr_failfast.attr, &dev_attr_expires.attr, + &dev_attr_reservation_policy.attr, + &dev_attr_last_known_reservation_state.attr, NULL, }; diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 266b34b5540..29143eda9dd 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -10,6 +10,7 @@ #define KMSG_COMPONENT "dasd" +#include <linux/kernel_stat.h> #include <linux/stddef.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -238,6 +239,7 @@ static void dasd_ext_handler(unsigned int ext_int_code, addr_t ip; int rc; + kstat_cpu(smp_processor_id()).irqs[EXTINT_DSD]++; switch (ext_int_code >> 24) { case DASD_DIAG_CODE_31BIT: ip = (addr_t) param32; @@ -617,6 +619,7 @@ static struct dasd_discipline dasd_diag_discipline = { .ebcname = "DIAG", .max_blocks = DIAG_MAX_BLOCKS, .check_device = dasd_diag_check_device, + .verify_path = dasd_generic_verify_path, .fill_geometry = dasd_diag_fill_geometry, .start_IO = dasd_start_diag, .term_IO = dasd_diag_term_IO, diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index bf61274af3b..318672d0556 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -54,6 +54,15 @@ #define ECKD_F7(i) (i->factor7) #define ECKD_F8(i) (i->factor8) +/* + * 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 + MODULE_LICENSE("GPL"); static struct dasd_discipline dasd_eckd_discipline; @@ -90,6 +99,18 @@ static struct { } *dasd_reserve_req; static DEFINE_MUTEX(dasd_reserve_mutex); +/* definitions for the path verification worker */ +struct path_verification_work_data { + struct work_struct worker; + struct dasd_device *device; + struct dasd_ccw_req cqr; + struct ccw1 ccw; + __u8 rcd_buffer[DASD_ECKD_RCD_DATA_SIZE]; + int isglobal; + __u8 tbvpm; +}; +static struct path_verification_work_data *path_verification_worker; +static DEFINE_MUTEX(dasd_path_verification_mutex); /* initial attempt at a probe function. this can be simplified once * the other detection code is gone */ @@ -373,6 +394,23 @@ static void fill_LRE_data(struct LRE_eckd_data *data, unsigned int trk, data->length = reclen; data->operation.operation = 0x03; break; + case DASD_ECKD_CCW_WRITE_FULL_TRACK: + data->operation.orientation = 0x0; + data->operation.operation = 0x3F; + data->extended_operation = 0x11; + data->length = 0; + data->extended_parameter_length = 0x02; + if (data->count > 8) { + data->extended_parameter[0] = 0xFF; + data->extended_parameter[1] = 0xFF; + data->extended_parameter[1] <<= (16 - count); + } else { + data->extended_parameter[0] = 0xFF; + data->extended_parameter[0] <<= (8 - count); + data->extended_parameter[1] = 0x00; + } + data->sector = 0xFF; + break; case DASD_ECKD_CCW_WRITE_TRACK_DATA: data->auxiliary.length_valid = 0x1; data->length = reclen; /* not tlf, as one might think */ @@ -396,6 +434,12 @@ static void fill_LRE_data(struct LRE_eckd_data *data, unsigned int trk, case DASD_ECKD_CCW_READ_COUNT: data->operation.operation = 0x06; break; + case DASD_ECKD_CCW_READ_TRACK: + data->operation.orientation = 0x1; + data->operation.operation = 0x0C; + data->extended_parameter_length = 0; + data->sector = 0xFF; + break; case DASD_ECKD_CCW_READ_TRACK_DATA: data->auxiliary.length_valid = 0x1; data->length = tlf; @@ -439,10 +483,16 @@ static int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, ccw->cmd_code = DASD_ECKD_CCW_PFX; ccw->flags = 0; - ccw->count = sizeof(*pfxdata); - ccw->cda = (__u32) __pa(pfxdata); + if (cmd == DASD_ECKD_CCW_WRITE_FULL_TRACK) { + ccw->count = sizeof(*pfxdata) + 2; + ccw->cda = (__u32) __pa(pfxdata); + memset(pfxdata, 0, sizeof(*pfxdata) + 2); + } else { + ccw->count = sizeof(*pfxdata); + ccw->cda = (__u32) __pa(pfxdata); + memset(pfxdata, 0, sizeof(*pfxdata)); + } - memset(pfxdata, 0, sizeof(*pfxdata)); /* prefix data */ if (format > 1) { DBF_DEV_EVENT(DBF_ERR, basedev, @@ -476,6 +526,7 @@ static int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, dedata->mask.perm = 0x1; dedata->attributes.operation = basepriv->attrib.operation; break; + case DASD_ECKD_CCW_READ_TRACK: case DASD_ECKD_CCW_READ_TRACK_DATA: dedata->mask.perm = 0x1; dedata->attributes.operation = basepriv->attrib.operation; @@ -502,6 +553,11 @@ static int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, dedata->attributes.operation = DASD_BYPASS_CACHE; rc = check_XRC_on_prefix(pfxdata, basedev); break; + case DASD_ECKD_CCW_WRITE_FULL_TRACK: + dedata->mask.perm = 0x03; + dedata->attributes.operation = basepriv->attrib.operation; + dedata->blk_size = 0; + break; case DASD_ECKD_CCW_WRITE_TRACK_DATA: dedata->mask.perm = 0x02; dedata->attributes.operation = basepriv->attrib.operation; @@ -755,26 +811,27 @@ static int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid) return -EINVAL; } -static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, - void *rcd_buffer, - struct ciw *ciw, __u8 lpm) +static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device, + struct dasd_ccw_req *cqr, + __u8 *rcd_buffer, + __u8 lpm) { - struct dasd_ccw_req *cqr; struct ccw1 *ccw; - - cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, ciw->count, - device); - - if (IS_ERR(cqr)) { - DBF_DEV_EVENT(DBF_WARNING, device, "%s", - "Could not allocate RCD request"); - return cqr; - } + /* + * buffer has to start with EBCDIC "V1.0" to show + * support for virtual device SNEQ + */ + rcd_buffer[0] = 0xE5; + rcd_buffer[1] = 0xF1; + rcd_buffer[2] = 0x4B; + rcd_buffer[3] = 0xF0; ccw = cqr->cpaddr; - ccw->cmd_code = ciw->cmd; + ccw->cmd_code = DASD_ECKD_CCW_RCD; + ccw->flags = 0; ccw->cda = (__u32)(addr_t)rcd_buffer; - ccw->count = ciw->count; + ccw->count = DASD_ECKD_RCD_DATA_SIZE; + cqr->magic = DASD_ECKD_MAGIC; cqr->startdev = device; cqr->memdev = device; @@ -784,7 +841,30 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; - return cqr; + set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); +} + +static int dasd_eckd_read_conf_immediately(struct dasd_device *device, + struct dasd_ccw_req *cqr, + __u8 *rcd_buffer, + __u8 lpm) +{ + struct ciw *ciw; + int rc; + /* + * 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) + return -EOPNOTSUPP; + + dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buffer, lpm); + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); + cqr->retries = 5; + rc = dasd_sleep_on_immediatly(cqr); + return rc; } static int dasd_eckd_read_conf_lpm(struct dasd_device *device, @@ -797,32 +877,29 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, struct dasd_ccw_req *cqr; /* - * scan for RCD command in extended SenseID data + * 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 == 0) { + if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) { ret = -EOPNOTSUPP; goto out_error; } - rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA); + rcd_buf = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL | GFP_DMA); if (!rcd_buf) { ret = -ENOMEM; goto out_error; } - - /* - * buffer has to start with EBCDIC "V1.0" to show - * support for virtual device SNEQ - */ - rcd_buf[0] = 0xE5; - rcd_buf[1] = 0xF1; - rcd_buf[2] = 0x4B; - rcd_buf[3] = 0xF0; - cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, + 0, /* use rcd_buf as data ara */ + device); if (IS_ERR(cqr)) { - ret = PTR_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); ret = dasd_sleep_on(cqr); /* * on success we update the user input parms @@ -831,7 +908,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, if (ret) goto out_error; - *rcd_buffer_size = ciw->count; + *rcd_buffer_size = DASD_ECKD_RCD_DATA_SIZE; *rcd_buffer = rcd_buf; return 0; out_error: @@ -901,18 +978,18 @@ static int dasd_eckd_read_conf(struct dasd_device *device) void *conf_data; int conf_len, conf_data_saved; int rc; - __u8 lpm; + __u8 lpm, opm; struct dasd_eckd_private *private; - struct dasd_eckd_path *path_data; + struct dasd_path *path_data; private = (struct dasd_eckd_private *) device->private; - path_data = (struct dasd_eckd_path *) &private->path_data; - path_data->opm = ccw_device_get_path_mask(device->cdev); + path_data = &device->path_data; + opm = ccw_device_get_path_mask(device->cdev); lpm = 0x80; conf_data_saved = 0; /* get configuration data per operational path */ for (lpm = 0x80; lpm; lpm>>= 1) { - if (lpm & path_data->opm){ + if (lpm & opm) { rc = dasd_eckd_read_conf_lpm(device, &conf_data, &conf_len, lpm); if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ @@ -925,6 +1002,8 @@ static int dasd_eckd_read_conf(struct dasd_device *device) DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "No configuration data " "retrieved"); + /* no further analysis possible */ + path_data->opm |= lpm; continue; /* no error */ } /* save first valid configuration data */ @@ -948,6 +1027,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device) path_data->ppm |= lpm; break; } + path_data->opm |= lpm; if (conf_data != private->conf_data) kfree(conf_data); } @@ -955,6 +1035,140 @@ static int dasd_eckd_read_conf(struct dasd_device *device) return 0; } +static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm) +{ + struct dasd_eckd_private *private; + int mdc; + u32 fcx_max_data; + + private = (struct dasd_eckd_private *) device->private; + 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 = 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; +} + +static void do_path_verification_work(struct work_struct *work) +{ + struct path_verification_work_data *data; + struct dasd_device *device; + __u8 lpm, opm, npm, ppm, epm; + unsigned long flags; + int rc; + + data = container_of(work, struct path_verification_work_data, worker); + device = data->device; + + opm = 0; + npm = 0; + ppm = 0; + epm = 0; + for (lpm = 0x80; lpm; lpm >>= 1) { + if (lpm & data->tbvpm) { + memset(data->rcd_buffer, 0, sizeof(data->rcd_buffer)); + memset(&data->cqr, 0, sizeof(data->cqr)); + data->cqr.cpaddr = &data->ccw; + rc = dasd_eckd_read_conf_immediately(device, &data->cqr, + data->rcd_buffer, + lpm); + if (!rc) { + switch (dasd_eckd_path_access(data->rcd_buffer, + DASD_ECKD_RCD_DATA_SIZE)) { + case 0x02: + npm |= lpm; + break; + case 0x03: + ppm |= lpm; + break; + } + opm |= lpm; + } else if (rc == -EOPNOTSUPP) { + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "path verification: No configuration " + "data retrieved"); + opm |= lpm; + } else if (rc == -EAGAIN) { + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "path verification: device is stopped," + " try again later"); + epm |= lpm; + } else { + dev_warn(&device->cdev->dev, + "Reading device feature codes failed " + "(rc=%d) for new path %x\n", rc, lpm); + continue; + } + if (verify_fcx_max_data(device, lpm)) { + opm &= ~lpm; + npm &= ~lpm; + ppm &= ~lpm; + } + } + } + /* + * 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 (!device->path_data.opm && opm) { + device->path_data.opm = opm; + dasd_generic_path_operational(device); + } else + device->path_data.opm |= opm; + device->path_data.npm |= npm; + device->path_data.ppm |= ppm; + device->path_data.tbvpm |= epm; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + + dasd_put_device(device); + if (data->isglobal) + mutex_unlock(&dasd_path_verification_mutex); + else + kfree(data); +} + +static int dasd_eckd_verify_path(struct dasd_device *device, __u8 lpm) +{ + struct path_verification_work_data *data; + + data = kmalloc(sizeof(*data), GFP_ATOMIC | GFP_DMA); + if (!data) { + if (mutex_trylock(&dasd_path_verification_mutex)) { + data = path_verification_worker; + data->isglobal = 1; + } else + return -ENOMEM; + } else { + memset(data, 0, sizeof(*data)); + data->isglobal = 0; + } + INIT_WORK(&data->worker, do_path_verification_work); + dasd_get_device(device); + data->device = device; + data->tbvpm = lpm; + schedule_work(&data->worker); + return 0; +} + static int dasd_eckd_read_features(struct dasd_device *device) { struct dasd_psf_prssd_data *prssdp; @@ -1105,6 +1319,37 @@ static void dasd_eckd_validate_server(struct dasd_device *device) "returned rc=%d", private->uid.ssid, rc); } +static u32 get_fcx_max_data(struct dasd_device *device) +{ +#if defined(CONFIG_64BIT) + int tpm, mdc; + int fcx_in_css, fcx_in_gneq, fcx_in_features; + struct dasd_eckd_private *private; + + if (dasd_nofcx) + return 0; + /* is transport mode supported? */ + private = (struct dasd_eckd_private *) device->private; + fcx_in_css = css_general_characteristics.fcx; + fcx_in_gneq = private->gneq->reserved2[7] & 0x04; + fcx_in_features = private->features.feature[40] & 0x80; + tpm = fcx_in_css && fcx_in_gneq && fcx_in_features; + + if (!tpm) + return 0; + + mdc = ccw_device_get_mdc(device->cdev, 0); + if (mdc < 0) { + dev_warn(&device->cdev->dev, "Detecting the maximum supported" + " data size for zHPF requests failed\n"); + return 0; + } else + return mdc * FCX_MAX_DATA_FACTOR; +#else + return 0; +#endif +} + /* * Check device characteristics. * If the device is accessible using ECKD discipline, the device is enabled. @@ -1223,6 +1468,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device) 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); @@ -1404,6 +1651,13 @@ static int dasd_eckd_end_analysis(struct dasd_block *block) dasd_sfree_request(init_cqr, device); } + if (device->features & DASD_FEATURE_USERAW) { + block->bp_block = DASD_RAW_BLOCKSIZE; + blk_per_trk = DASD_RAW_BLOCK_PER_TRACK; + block->s2b_shift = 3; + goto raw; + } + if (status == INIT_CQR_UNFORMATTED) { dev_warn(&device->cdev->dev, "The DASD is not formatted\n"); return -EMEDIUMTYPE; @@ -1441,6 +1695,7 @@ static int dasd_eckd_end_analysis(struct dasd_block *block) dev_warn(&device->cdev->dev, "Track 0 has no records following the VTOC\n"); } + if (count_area != NULL && count_area->kl == 0) { /* we found notthing violating our disk layout */ if (dasd_check_blocksize(count_area->dl) == 0) @@ -1456,6 +1711,8 @@ static int dasd_eckd_end_analysis(struct dasd_block *block) block->s2b_shift++; blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); + +raw: block->blocks = (private->real_cyl * private->rdc_data.trk_per_cyl * blk_per_trk); @@ -1716,6 +1973,7 @@ static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) if (cqr->block && (cqr->startdev != cqr->block->base)) { dasd_eckd_reset_ccw_to_base_io(cqr); cqr->startdev = cqr->block->base; + cqr->lpm = cqr->block->base->path_data.opm; } }; @@ -1744,9 +2002,9 @@ dasd_eckd_erp_postaction(struct dasd_ccw_req * cqr) return dasd_default_erp_postaction; } - -static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, - struct irb *irb) +static void dasd_eckd_check_for_device_change(struct dasd_device *device, + struct dasd_ccw_req *cqr, + struct irb *irb) { char mask; char *sense = NULL; @@ -1770,40 +2028,41 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, /* schedule worker to reload device */ dasd_reload_device(device); } - dasd_generic_handle_state_change(device); return; } - /* summary unit check */ sense = dasd_get_sense(irb); - if (sense && (sense[7] == 0x0D) && + if (!sense) + return; + + /* summary unit check */ + if ((sense[7] == 0x0D) && (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) { dasd_alias_handle_summary_unit_check(device, irb); return; } /* service information message SIM */ - if (sense && !(sense[27] & DASD_SENSE_BIT_0) && + if (!cqr && !(sense[27] & DASD_SENSE_BIT_0) && ((sense[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) { dasd_3990_erp_handle_sim(device, sense); - dasd_schedule_device_bh(device); return; } - if ((scsw_cc(&irb->scsw) == 1) && !sense && - (scsw_fctl(&irb->scsw) == SCSW_FCTL_START_FUNC) && - (scsw_actl(&irb->scsw) == SCSW_ACTL_START_PEND) && - (scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND)) { - /* fake irb do nothing, they are handled elsewhere */ - dasd_schedule_device_bh(device); - return; + /* loss of device reservation is handled via base devices only + * as alias devices may be used with several bases + */ + if (device->block && (sense[7] == 0x3F) && + (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) && + test_bit(DASD_FLAG_IS_RESERVED, &device->flags)) { + if (device->features & DASD_FEATURE_FAILONSLCK) + set_bit(DASD_FLAG_LOCK_STOLEN, &device->flags); + clear_bit(DASD_FLAG_IS_RESERVED, &device->flags); + dev_err(&device->cdev->dev, + "The device reservation was lost\n"); } - - dasd_schedule_device_bh(device); - return; -}; - +} static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( struct dasd_device *startdev, @@ -1984,7 +2243,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( cqr->memdev = startdev; cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ - cqr->lpm = private->path_data.ppm; + cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -2161,7 +2420,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( cqr->memdev = startdev; cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ - cqr->lpm = private->path_data.ppm; + cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -2326,6 +2585,12 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( struct tidaw *last_tidaw = NULL; int itcw_op; size_t itcw_size; + u8 tidaw_flags; + unsigned int seg_len, part_len, len_to_track_end; + unsigned char new_track; + sector_t recid, trkid; + unsigned int offs; + unsigned int count, count_to_trk_end; basedev = block->base; private = (struct dasd_eckd_private *) basedev->private; @@ -2341,12 +2606,16 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( /* trackbased I/O needs address all memory via TIDAWs, * not just for 64 bit addresses. This allows us to map * each segment directly to one tidaw. + * In the case of write requests, additional tidaws may + * be needed when a segment crosses a track boundary. */ trkcount = last_trk - first_trk + 1; ctidaw = 0; rq_for_each_segment(bv, req, iter) { ++ctidaw; } + if (rq_data_dir(req) == WRITE) + ctidaw += (last_trk - first_trk); /* Allocate the ccw request. */ itcw_size = itcw_calc_size(0, ctidaw, 0); @@ -2354,15 +2623,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( if (IS_ERR(cqr)) return cqr; - cqr->cpmode = 1; - cqr->startdev = startdev; - cqr->memdev = startdev; - cqr->block = block; - cqr->expires = 100*HZ; - cqr->buildclk = get_clock(); - cqr->status = DASD_CQR_FILLED; - cqr->retries = 10; - /* transfer length factor: how many bytes to read from the last track */ if (first_trk == last_trk) tlf = last_offs - first_offs + 1; @@ -2371,8 +2631,11 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( tlf *= blksize; itcw = itcw_init(cqr->data, itcw_size, itcw_op, 0, ctidaw, 0); + if (IS_ERR(itcw)) { + dasd_sfree_request(cqr, startdev); + return ERR_PTR(-EINVAL); + } cqr->cpaddr = itcw_get_tcw(itcw); - if (prepare_itcw(itcw, first_trk, last_trk, cmd, basedev, startdev, first_offs + 1, @@ -2385,31 +2648,69 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( dasd_sfree_request(cqr, startdev); return ERR_PTR(-EAGAIN); } - /* * A tidaw can address 4k of memory, but must not cross page boundaries * We can let the block layer handle this by setting * blk_queue_segment_boundary to page boundaries and * blk_max_segment_size to page size when setting up the request queue. + * For write requests, a TIDAW must not cross track boundaries, because + * we have to set the CBC flag on the last tidaw for each track. */ - rq_for_each_segment(bv, req, iter) { - dst = page_address(bv->bv_page) + bv->bv_offset; - last_tidaw = itcw_add_tidaw(itcw, 0x00, dst, bv->bv_len); - if (IS_ERR(last_tidaw)) - return (struct dasd_ccw_req *)last_tidaw; + if (rq_data_dir(req) == WRITE) { + new_track = 1; + recid = first_rec; + rq_for_each_segment(bv, req, iter) { + dst = page_address(bv->bv_page) + bv->bv_offset; + seg_len = bv->bv_len; + while (seg_len) { + if (new_track) { + trkid = recid; + offs = sector_div(trkid, blk_per_trk); + count_to_trk_end = blk_per_trk - offs; + count = min((last_rec - recid + 1), + (sector_t)count_to_trk_end); + len_to_track_end = count * blksize; + recid += count; + new_track = 0; + } + part_len = min(seg_len, len_to_track_end); + seg_len -= part_len; + len_to_track_end -= part_len; + /* We need to end the tidaw at track end */ + if (!len_to_track_end) { + new_track = 1; + tidaw_flags = TIDAW_FLAGS_INSERT_CBC; + } else + tidaw_flags = 0; + last_tidaw = itcw_add_tidaw(itcw, tidaw_flags, + dst, part_len); + if (IS_ERR(last_tidaw)) + return ERR_PTR(-EINVAL); + dst += part_len; + } + } + } else { + rq_for_each_segment(bv, req, iter) { + dst = page_address(bv->bv_page) + bv->bv_offset; + last_tidaw = itcw_add_tidaw(itcw, 0x00, + dst, bv->bv_len); + if (IS_ERR(last_tidaw)) + return ERR_PTR(-EINVAL); + } } - - last_tidaw->flags |= 0x80; + last_tidaw->flags |= TIDAW_FLAGS_LAST; + last_tidaw->flags &= ~TIDAW_FLAGS_INSERT_CBC; itcw_finalize(itcw); if (blk_noretry_request(req) || block->base->features & DASD_FEATURE_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); + cqr->cpmode = 1; cqr->startdev = startdev; cqr->memdev = startdev; cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ - cqr->lpm = private->path_data.ppm; + cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -2420,11 +2721,9 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, struct dasd_block *block, struct request *req) { - int tpm, cmdrtd, cmdwtd; + int cmdrtd, cmdwtd; int use_prefix; -#if defined(CONFIG_64BIT) - int fcx_in_css, fcx_in_gneq, fcx_in_features; -#endif + int fcx_multitrack; struct dasd_eckd_private *private; struct dasd_device *basedev; sector_t first_rec, last_rec; @@ -2432,6 +2731,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, unsigned int first_offs, last_offs; unsigned int blk_per_trk, blksize; int cdlspecial; + unsigned int data_size; struct dasd_ccw_req *cqr; basedev = block->base; @@ -2450,15 +2750,11 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, last_offs = sector_div(last_trk, blk_per_trk); cdlspecial = (private->uses_cdl && first_rec < 2*blk_per_trk); - /* is transport mode supported? */ -#if defined(CONFIG_64BIT) - fcx_in_css = css_general_characteristics.fcx; - fcx_in_gneq = private->gneq->reserved2[7] & 0x04; - fcx_in_features = private->features.feature[40] & 0x80; - tpm = fcx_in_css && fcx_in_gneq && fcx_in_features; -#else - tpm = 0; -#endif + fcx_multitrack = private->features.feature[40] & 0x20; + data_size = blk_rq_bytes(req); + /* tpm write request add CBC data on each track boundary */ + if (rq_data_dir(req) == WRITE) + data_size += (last_trk - first_trk) * 4; /* is read track data and write track data in command mode supported? */ cmdrtd = private->features.feature[9] & 0x20; @@ -2468,13 +2764,15 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, cqr = NULL; if (cdlspecial || dasd_page_cache) { /* do nothing, just fall through to the cmd mode single case */ - } else if (!dasd_nofcx && tpm && (first_trk == last_trk)) { + } else if ((data_size <= private->fcx_max_data) + && (fcx_multitrack || (first_trk == last_trk))) { cqr = dasd_eckd_build_cp_tpm_track(startdev, block, req, first_rec, last_rec, first_trk, last_trk, first_offs, last_offs, blk_per_trk, blksize); - if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN) + if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) && + (PTR_ERR(cqr) != -ENOMEM)) cqr = NULL; } else if (use_prefix && (((rq_data_dir(req) == READ) && cmdrtd) || @@ -2484,7 +2782,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, first_trk, last_trk, first_offs, last_offs, blk_per_trk, blksize); - if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN) + if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) && + (PTR_ERR(cqr) != -ENOMEM)) cqr = NULL; } if (!cqr) @@ -2496,6 +2795,135 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, return cqr; } +static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, + struct dasd_block *block, + struct request *req) +{ + struct dasd_eckd_private *private; + unsigned long *idaws; + struct dasd_device *basedev; + struct dasd_ccw_req *cqr; + struct ccw1 *ccw; + struct req_iterator iter; + struct bio_vec *bv; + char *dst; + unsigned char cmd; + unsigned int trkcount; + unsigned int seg_len, len_to_track_end; + unsigned int first_offs; + unsigned int cidaw, cplength, datasize; + sector_t first_trk, last_trk; + unsigned int pfx_datasize; + + /* + * raw track access needs to be mutiple of 64k and on 64k boundary + */ + if ((blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK) != 0) { + cqr = ERR_PTR(-EINVAL); + goto out; + } + if (((blk_rq_pos(req) + blk_rq_sectors(req)) % + DASD_RAW_SECTORS_PER_TRACK) != 0) { + cqr = ERR_PTR(-EINVAL); + goto out; + } + + first_trk = blk_rq_pos(req) / DASD_RAW_SECTORS_PER_TRACK; + last_trk = (blk_rq_pos(req) + blk_rq_sectors(req) - 1) / + DASD_RAW_SECTORS_PER_TRACK; + trkcount = last_trk - first_trk + 1; + first_offs = 0; + basedev = block->base; + private = (struct dasd_eckd_private *) basedev->private; + + if (rq_data_dir(req) == READ) + cmd = DASD_ECKD_CCW_READ_TRACK; + else if (rq_data_dir(req) == WRITE) + cmd = DASD_ECKD_CCW_WRITE_FULL_TRACK; + else { + cqr = ERR_PTR(-EINVAL); + goto out; + } + + /* + * Raw track based I/O needs IDAWs for each page, + * and not just for 64 bit addresses. + */ + cidaw = trkcount * DASD_RAW_BLOCK_PER_TRACK; + + /* 1x prefix + one read/write ccw per track */ + cplength = 1 + trkcount; + + /* + * struct PFX_eckd_data has up to 2 byte as extended parameter + * this is needed for write full track and has to be mentioned + * seperately + * add 8 instead of 2 to keep 8 byte boundary + */ + pfx_datasize = sizeof(struct PFX_eckd_data) + 8; + + datasize = pfx_datasize + cidaw * sizeof(unsigned long long); + + /* Allocate the ccw request. */ + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, + datasize, startdev); + if (IS_ERR(cqr)) + goto out; + ccw = cqr->cpaddr; + + if (prefix_LRE(ccw++, cqr->data, first_trk, last_trk, cmd, + basedev, startdev, 1 /* format */, first_offs + 1, + trkcount, 0, 0) == -EAGAIN) { + /* Clock not in sync and XRC is enabled. + * Try again later. + */ + dasd_sfree_request(cqr, startdev); + cqr = ERR_PTR(-EAGAIN); + goto out; + } + + idaws = (unsigned long *)(cqr->data + pfx_datasize); + + len_to_track_end = 0; + + rq_for_each_segment(bv, req, iter) { + dst = page_address(bv->bv_page) + bv->bv_offset; + seg_len = bv->bv_len; + if (!len_to_track_end) { + ccw[-1].flags |= CCW_FLAG_CC; + ccw->cmd_code = cmd; + /* maximum 3390 track size */ + ccw->count = 57326; + /* 64k map to one track */ + len_to_track_end = 65536; + ccw->cda = (__u32)(addr_t)idaws; + ccw->flags |= CCW_FLAG_IDA; + ccw->flags |= CCW_FLAG_SLI; + ccw++; + } + len_to_track_end -= seg_len; + idaws = idal_create_words(idaws, dst, seg_len); + } + + if (blk_noretry_request(req) || + block->base->features & DASD_FEATURE_FAILFAST) + set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); + cqr->startdev = startdev; + cqr->memdev = startdev; + cqr->block = block; + cqr->expires = startdev->default_expires * HZ; + cqr->lpm = startdev->path_data.ppm; + cqr->retries = 256; + cqr->buildclk = get_clock(); + cqr->status = DASD_CQR_FILLED; + + if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN) + cqr = NULL; +out: + return cqr; +} + + static int dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) { @@ -2600,7 +3028,10 @@ static struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base, spin_lock_irqsave(get_ccwdev_lock(startdev->cdev), flags); private->count++; - cqr = dasd_eckd_build_cp(startdev, block, req); + if ((base->features & DASD_FEATURE_USERAW)) + cqr = dasd_raw_build_cp(startdev, block, req); + else + cqr = dasd_eckd_build_cp(startdev, block, req); if (IS_ERR(cqr)) private->count--; spin_unlock_irqrestore(get_ccwdev_lock(startdev->cdev), flags); @@ -2688,6 +3119,8 @@ dasd_eckd_release(struct dasd_device *device) cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); + if (!rc) + clear_bit(DASD_FLAG_IS_RESERVED, &device->flags); if (useglobal) mutex_unlock(&dasd_reserve_mutex); @@ -2741,6 +3174,8 @@ dasd_eckd_reserve(struct dasd_device *device) cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); + if (!rc) + set_bit(DASD_FLAG_IS_RESERVED, &device->flags); if (useglobal) mutex_unlock(&dasd_reserve_mutex); @@ -2793,6 +3228,8 @@ dasd_eckd_steal_lock(struct dasd_device *device) cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); + if (!rc) + set_bit(DASD_FLAG_IS_RESERVED, &device->flags); if (useglobal) mutex_unlock(&dasd_reserve_mutex); @@ -2845,6 +3282,7 @@ static int dasd_eckd_snid(struct dasd_device *device, cqr->memdev = device; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); + set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); cqr->retries = 5; cqr->expires = 10 * HZ; cqr->buildclk = get_clock(); @@ -3279,10 +3717,8 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, { char *page; int len, sl, sct, residual; - struct tsb *tsb; - u8 *sense; - + u8 *sense, *rcq; page = (char *) get_zeroed_page(GFP_ATOMIC); if (page == NULL) { @@ -3348,12 +3784,15 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, case 2: /* ts_ddpc */ len += sprintf(page + len, KERN_ERR PRINTK_HEADER " tsb->tsa.ddpc.rc %d\n", tsb->tsa.ddpc.rc); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " tsb->tsa.ddpc.rcq: "); - for (sl = 0; sl < 16; sl++) { + for (sl = 0; sl < 2; sl++) { + len += sprintf(page + len, + KERN_ERR PRINTK_HEADER + " tsb->tsa.ddpc.rcq %2d-%2d: ", + (8 * sl), ((8 * sl) + 7)); + rcq = tsb->tsa.ddpc.rcq; for (sct = 0; sct < 8; sct++) { len += sprintf(page + len, " %02x", - tsb->tsa.ddpc.rcq[sl]); + rcq[8 * sl + sct]); } len += sprintf(page + len, "\n"); } @@ -3550,6 +3989,7 @@ static struct ccw_driver dasd_eckd_driver = { .set_offline = dasd_generic_set_offline, .set_online = dasd_eckd_set_online, .notify = dasd_generic_notify, + .path_event = dasd_generic_path_event, .freeze = dasd_generic_pm_freeze, .thaw = dasd_generic_restore_device, .restore = dasd_generic_restore_device, @@ -3573,10 +4013,11 @@ static struct dasd_discipline dasd_eckd_discipline = { .owner = THIS_MODULE, .name = "ECKD", .ebcname = "ECKD", - .max_blocks = 240, + .max_blocks = 190, .check_device = dasd_eckd_check_characteristics, .uncheck_device = dasd_eckd_uncheck_device, .do_analysis = dasd_eckd_do_analysis, + .verify_path = dasd_eckd_verify_path, .ready_to_online = dasd_eckd_ready_to_online, .online_to_ready = dasd_eckd_online_to_ready, .fill_geometry = dasd_eckd_fill_geometry, @@ -3586,7 +4027,7 @@ static struct dasd_discipline dasd_eckd_discipline = { .format_device = dasd_eckd_format_device, .erp_action = dasd_eckd_erp_action, .erp_postaction = dasd_eckd_erp_postaction, - .handle_unsolicited_interrupt = dasd_eckd_handle_unsolicited_interrupt, + .check_for_device_change = dasd_eckd_check_for_device_change, .build_cp = dasd_eckd_build_alias_cp, .free_cp = dasd_eckd_free_alias_cp, .dump_sense = dasd_eckd_dump_sense, @@ -3609,11 +4050,19 @@ dasd_eckd_init(void) GFP_KERNEL | GFP_DMA); if (!dasd_reserve_req) return -ENOMEM; + path_verification_worker = kmalloc(sizeof(*path_verification_worker), + GFP_KERNEL | GFP_DMA); + if (!path_verification_worker) { + kfree(dasd_reserve_req); + return -ENOMEM; + } ret = ccw_driver_register(&dasd_eckd_driver); if (!ret) wait_for_device_probe(); - else + else { + kfree(path_verification_worker); kfree(dasd_reserve_req); + } return ret; } @@ -3621,6 +4070,7 @@ static void __exit dasd_eckd_cleanup(void) { ccw_driver_unregister(&dasd_eckd_driver); + kfree(path_verification_worker); kfree(dasd_reserve_req); } diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index 12097c24f2f..4a688a873a7 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -37,14 +37,17 @@ #define DASD_ECKD_CCW_WRITE_KD_MT 0x8d #define DASD_ECKD_CCW_READ_KD_MT 0x8e #define DASD_ECKD_CCW_RELEASE 0x94 +#define DASD_ECKD_CCW_WRITE_FULL_TRACK 0x95 #define DASD_ECKD_CCW_READ_CKD_MT 0x9e #define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d #define DASD_ECKD_CCW_WRITE_TRACK_DATA 0xA5 #define DASD_ECKD_CCW_READ_TRACK_DATA 0xA6 #define DASD_ECKD_CCW_RESERVE 0xB4 +#define DASD_ECKD_CCW_READ_TRACK 0xDE #define DASD_ECKD_CCW_PFX 0xE7 #define DASD_ECKD_CCW_PFX_READ 0xEA #define DASD_ECKD_CCW_RSCK 0xF9 +#define DASD_ECKD_CCW_RCD 0xFA /* * Perform Subsystem Function / Sub-Orders @@ -57,6 +60,11 @@ */ #define LV_COMPAT_CYL 0xFFFE + +#define FCX_MAX_DATA_FACTOR 65536 +#define DASD_ECKD_RCD_DATA_SIZE 256 + + /***************************************************************************** * SECTION: Type Definitions ****************************************************************************/ @@ -331,12 +339,6 @@ struct dasd_gneq { __u8 reserved2[22]; } __attribute__ ((packed)); -struct dasd_eckd_path { - __u8 opm; - __u8 ppm; - __u8 npm; -}; - struct dasd_rssd_features { char feature[256]; } __attribute__((packed)); @@ -442,7 +444,6 @@ struct dasd_eckd_private { struct vd_sneq *vdsneq; struct dasd_gneq *gneq; - struct dasd_eckd_path path_data; struct eckd_count count_area[5]; int init_cqr_status; int uses_cdl; @@ -455,6 +456,8 @@ struct dasd_eckd_private { struct alias_pav_group *pavgroup; struct alias_lcu *lcu; int count; + + u32 fcx_max_data; }; diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index c71d89dba30..77f778b7b07 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -17,7 +17,6 @@ #include <linux/device.h> #include <linux/poll.h> #include <linux/mutex.h> -#include <linux/smp_lock.h> #include <linux/err.h> #include <linux/slab.h> @@ -474,6 +473,7 @@ int dasd_eer_enable(struct dasd_device *device) cqr->retries = 255; cqr->expires = 10 * HZ; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); ccw = cqr->cpaddr; ccw->cmd_code = DASD_ECKD_CCW_SNSS; diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index 7656384a811..0eafe2e421e 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -96,7 +96,8 @@ dasd_default_erp_action(struct dasd_ccw_req *cqr) DBF_DEV_EVENT(DBF_DEBUG, device, "default ERP called (%i retries left)", cqr->retries); - cqr->lpm = LPM_ANYPATH; + if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) + cqr->lpm = device->path_data.opm; cqr->status = DASD_CQR_FILLED; } else { pr_err("%s: default ERP has run out of retries and failed\n", diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index bec5486e0e6..be89b3a893d 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -73,6 +73,7 @@ static struct ccw_driver dasd_fba_driver = { .set_offline = dasd_generic_set_offline, .set_online = dasd_fba_set_online, .notify = dasd_generic_notify, + .path_event = dasd_generic_path_event, .freeze = dasd_generic_pm_freeze, .thaw = dasd_generic_restore_device, .restore = dasd_generic_restore_device, @@ -164,6 +165,7 @@ dasd_fba_check_characteristics(struct dasd_device *device) } device->default_expires = DASD_EXPIRES; + device->path_data.opm = LPM_ANYPATH; readonly = dasd_device_is_ro(device); if (readonly) @@ -231,24 +233,16 @@ dasd_fba_erp_postaction(struct dasd_ccw_req * cqr) return NULL; } -static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device, - struct irb *irb) +static void dasd_fba_check_for_device_change(struct dasd_device *device, + struct dasd_ccw_req *cqr, + struct irb *irb) { char mask; /* first of all check for state change pending interrupt */ mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; - if ((irb->scsw.cmd.dstat & mask) == mask) { + if ((irb->scsw.cmd.dstat & mask) == mask) dasd_generic_handle_state_change(device); - return; - } - - /* check for unsolicited interrupts */ - DBF_DEV_EVENT(DBF_WARNING, device, "%s", - "unsolicited interrupt received"); - device->discipline->dump_sense_dbf(device, irb, "unsolicited"); - dasd_schedule_device_bh(device); - return; }; static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, @@ -596,13 +590,14 @@ static struct dasd_discipline dasd_fba_discipline = { .max_blocks = 96, .check_device = dasd_fba_check_characteristics, .do_analysis = dasd_fba_do_analysis, + .verify_path = dasd_generic_verify_path, .fill_geometry = dasd_fba_fill_geometry, .start_IO = dasd_start_IO, .term_IO = dasd_term_IO, .handle_terminated_request = dasd_fba_handle_terminated_request, .erp_action = dasd_fba_erp_action, .erp_postaction = dasd_fba_erp_postaction, - .handle_unsolicited_interrupt = dasd_fba_handle_unsolicited_interrupt, + .check_for_device_change = dasd_fba_check_for_device_change, .build_cp = dasd_fba_build_cp, .free_cp = dasd_fba_free_cp, .dump_sense = dasd_fba_dump_sense, diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 500678d7116..df9f6999411 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -231,6 +231,11 @@ struct dasd_ccw_req { /* per dasd_ccw_req flags */ #define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */ #define DASD_CQR_FLAGS_FAILFAST 1 /* FAILFAST */ +#define DASD_CQR_VERIFY_PATH 2 /* path verification request */ +#define DASD_CQR_ALLOW_SLOCK 3 /* Try this request even when lock was + * stolen. Should not be combined with + * DASD_CQR_FLAGS_USE_ERP + */ /* Signature for error recovery functions. */ typedef struct dasd_ccw_req *(*dasd_erp_fn_t) (struct dasd_ccw_req *); @@ -287,6 +292,14 @@ struct dasd_discipline { int (*do_analysis) (struct dasd_block *); /* + * This function is called, when new paths become available. + * Disciplins may use this callback to do necessary setup work, + * e.g. verify that new path is compatible with the current + * configuration. + */ + int (*verify_path)(struct dasd_device *, __u8); + + /* * Last things to do when a device is set online, and first things * when it is set offline. */ @@ -325,9 +338,9 @@ struct dasd_discipline { void (*dump_sense) (struct dasd_device *, struct dasd_ccw_req *, struct irb *); void (*dump_sense_dbf) (struct dasd_device *, struct irb *, char *); - - void (*handle_unsolicited_interrupt) (struct dasd_device *, - struct irb *); + void (*check_for_device_change) (struct dasd_device *, + struct dasd_ccw_req *, + struct irb *); /* i/o control functions. */ int (*fill_geometry) (struct dasd_block *, struct hd_geometry *); @@ -362,6 +375,13 @@ extern struct dasd_discipline *dasd_diag_discipline_pointer; #define DASD_EER_STATECHANGE 3 #define DASD_EER_PPRCSUSPEND 4 +struct dasd_path { + __u8 opm; + __u8 tbvpm; + __u8 ppm; + __u8 npm; +}; + struct dasd_device { /* Block device stuff. */ struct dasd_block *block; @@ -377,6 +397,7 @@ struct dasd_device { struct dasd_discipline *discipline; struct dasd_discipline *base_discipline; char *private; + struct dasd_path path_data; /* Device state and target state. */ int state, target; @@ -456,6 +477,9 @@ struct dasd_block { * confuse this with the user specified * read-only feature. */ +#define DASD_FLAG_IS_RESERVED 7 /* The device is reserved */ +#define DASD_FLAG_LOCK_STOLEN 8 /* The device lock was stolen */ + void dasd_put_device_wake(struct dasd_device *); @@ -620,10 +644,15 @@ void dasd_generic_remove (struct ccw_device *cdev); int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *); int dasd_generic_set_offline (struct ccw_device *cdev); int dasd_generic_notify(struct ccw_device *, int); +int dasd_generic_last_path_gone(struct dasd_device *); +int dasd_generic_path_operational(struct dasd_device *); + void dasd_generic_handle_state_change(struct dasd_device *); int dasd_generic_pm_freeze(struct ccw_device *); int dasd_generic_restore_device(struct ccw_device *); enum uc_todo dasd_generic_uc_handler(struct ccw_device *, struct irb *); +void dasd_generic_path_event(struct ccw_device *, int *); +int dasd_generic_verify_path(struct dasd_device *, __u8); int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int); char *dasd_get_sense(struct irb *); diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 40834f18754..dcee3c5c895 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -2,76 +2,85 @@ comment "S/390 character device drivers" depends on S390 config TN3270 - tristate "Support for locally attached 3270 terminals" + def_tristate y + prompt "Support for locally attached 3270 terminals" depends on CCW help Include support for IBM 3270 terminals. config TN3270_TTY - tristate "Support for tty input/output on 3270 terminals" + def_tristate y + prompt "Support for tty input/output on 3270 terminals" depends on TN3270 help Include support for using an IBM 3270 terminal as a Linux tty. config TN3270_FS - tristate "Support for fullscreen applications on 3270 terminals" + def_tristate m + prompt "Support for fullscreen applications on 3270 terminals" depends on TN3270 help Include support for fullscreen applications on an IBM 3270 terminal. config TN3270_CONSOLE - bool "Support for console on 3270 terminal" + def_bool y + prompt "Support for console on 3270 terminal" depends on TN3270=y && TN3270_TTY=y help Include support for using an IBM 3270 terminal as a Linux system console. Available only if 3270 support is compiled in statically. config TN3215 - bool "Support for 3215 line mode terminal" + def_bool y + prompt "Support for 3215 line mode terminal" depends on CCW help Include support for IBM 3215 line-mode terminals. config TN3215_CONSOLE - bool "Support for console on 3215 line mode terminal" + def_bool y + prompt "Support for console on 3215 line mode terminal" depends on TN3215 help Include support for using an IBM 3215 line-mode terminal as a Linux system console. config CCW_CONSOLE - bool - depends on TN3215_CONSOLE || TN3270_CONSOLE - default y + def_bool y if TN3215_CONSOLE || TN3270_CONSOLE config SCLP_TTY - bool "Support for SCLP line mode terminal" + def_bool y + prompt "Support for SCLP line mode terminal" depends on S390 help Include support for IBM SCLP line-mode terminals. config SCLP_CONSOLE - bool "Support for console on SCLP line mode terminal" + def_bool y + prompt "Support for console on SCLP line mode terminal" depends on SCLP_TTY help Include support for using an IBM HWC line-mode terminal as the Linux system console. config SCLP_VT220_TTY - bool "Support for SCLP VT220-compatible terminal" + def_bool y + prompt "Support for SCLP VT220-compatible terminal" depends on S390 help Include support for an IBM SCLP VT220-compatible terminal. config SCLP_VT220_CONSOLE - bool "Support for console on SCLP VT220-compatible terminal" + def_bool y + prompt "Support for console on SCLP VT220-compatible terminal" depends on SCLP_VT220_TTY help Include support for using an IBM SCLP VT220-compatible terminal as a Linux system console. config SCLP_CPI - tristate "Control-Program Identification" + def_tristate m + prompt "Control-Program Identification" depends on S390 help This option enables the hardware console interface for system @@ -83,7 +92,8 @@ config SCLP_CPI need this feature and intend to run your kernel in LPAR. config SCLP_ASYNC - tristate "Support for Call Home via Asynchronous SCLP Records" + def_tristate m + prompt "Support for Call Home via Asynchronous SCLP Records" depends on S390 help This option enables the call home function, which is able to inform @@ -93,7 +103,8 @@ config SCLP_ASYNC need this feature and intend to run your kernel in LPAR. config S390_TAPE - tristate "S/390 tape device support" + def_tristate m + prompt "S/390 tape device support" depends on CCW help Select this option if you want to access channel-attached tape @@ -109,7 +120,8 @@ comment "S/390 tape interface support" depends on S390_TAPE config S390_TAPE_BLOCK - bool "Support for tape block devices" + def_bool y + prompt "Support for tape block devices" depends on S390_TAPE && BLOCK help Select this option if you want to access your channel-attached tape @@ -123,7 +135,8 @@ comment "S/390 tape hardware support" depends on S390_TAPE config S390_TAPE_34XX - tristate "Support for 3480/3490 tape hardware" + def_tristate m + prompt "Support for 3480/3490 tape hardware" depends on S390_TAPE help Select this option if you want to access IBM 3480/3490 magnetic @@ -131,7 +144,8 @@ config S390_TAPE_34XX It is safe to say "Y" here. config S390_TAPE_3590 - tristate "Support for 3590 tape hardware" + def_tristate m + prompt "Support for 3590 tape hardware" depends on S390_TAPE help Select this option if you want to access IBM 3590 magnetic @@ -139,7 +153,8 @@ config S390_TAPE_3590 It is safe to say "Y" here. config VMLOGRDR - tristate "Support for the z/VM recording system services (VM only)" + def_tristate m + prompt "Support for the z/VM recording system services (VM only)" depends on IUCV help Select this option if you want to be able to receive records collected @@ -148,29 +163,31 @@ config VMLOGRDR This driver depends on the IUCV support driver. config VMCP - bool "Support for the z/VM CP interface" + def_bool y + prompt "Support for the z/VM CP interface" depends on S390 help Select this option if you want to be able to interact with the control program on z/VM config MONREADER - tristate "API for reading z/VM monitor service records" + def_tristate m + prompt "API for reading z/VM monitor service records" depends on IUCV help Character device driver for reading z/VM monitor service records config MONWRITER - tristate "API for writing z/VM monitor service records" + def_tristate m + prompt "API for writing z/VM monitor service records" depends on S390 - default "m" help Character device driver for writing z/VM monitor service records config S390_VMUR - tristate "z/VM unit record device driver" + def_tristate m + prompt "z/VM unit record device driver" depends on S390 - default "m" help Character device driver for z/VM reader, puncher and printer. diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 59ec073724b..3fb4335d491 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -9,6 +9,7 @@ * Dan Morrison, IBM Corporation <dmorriso@cse.buffalo.edu> */ +#include <linux/kernel_stat.h> #include <linux/module.h> #include <linux/types.h> #include <linux/kdev_t.h> @@ -361,6 +362,7 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm, int cstat, dstat; int count; + kstat_cpu(smp_processor_id()).irqs[IOINT_C15]++; raw = dev_get_drvdata(&cdev->dev); req = (struct raw3215_req *) intparm; cstat = irb->scsw.cmd.cstat; diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index eb28fb01a38..f6489eb7e97 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -14,7 +14,6 @@ #include <linux/list.h> #include <linux/slab.h> #include <linux/types.h> -#include <linux/smp_lock.h> #include <asm/compat.h> #include <asm/ccwdev.h> diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 2a4c566456e..96ba2fd1c8a 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -7,6 +7,7 @@ * Copyright IBM Corp. 2003, 2009 */ +#include <linux/kernel_stat.h> #include <linux/module.h> #include <linux/err.h> #include <linux/init.h> @@ -329,6 +330,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) struct raw3270_request *rq; int rc; + kstat_cpu(smp_processor_id()).irqs[IOINT_C70]++; rp = dev_get_drvdata(&cdev->dev); if (!rp) return; diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 35cc4686b99..b76c61f8248 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -7,6 +7,7 @@ * Martin Schwidefsky <schwidefsky@de.ibm.com> */ +#include <linux/kernel_stat.h> #include <linux/module.h> #include <linux/err.h> #include <linux/spinlock.h> @@ -18,16 +19,14 @@ #include <linux/suspend.h> #include <linux/completion.h> #include <linux/platform_device.h> -#include <asm/types.h> #include <asm/s390_ext.h> +#include <asm/types.h> +#include <asm/irq.h> #include "sclp.h" #define SCLP_HEADER "sclp: " -/* Structure for register_early_external_interrupt. */ -static ext_int_info_t ext_int_info_hwc; - /* Lock to protect internal data consistency. */ static DEFINE_SPINLOCK(sclp_lock); @@ -402,6 +401,7 @@ static void sclp_interrupt_handler(unsigned int ext_int_code, u32 finished_sccb; u32 evbuf_pending; + kstat_cpu(smp_processor_id()).irqs[EXTINT_SCP]++; spin_lock(&sclp_lock); finished_sccb = param32 & 0xfffffff8; evbuf_pending = param32 & 0x3; @@ -824,6 +824,7 @@ static void sclp_check_handler(unsigned int ext_int_code, { u32 finished_sccb; + kstat_cpu(smp_processor_id()).irqs[EXTINT_SCP]++; finished_sccb = param32 & 0xfffffff8; /* Is this the interrupt we are waiting for? */ if (finished_sccb == 0) @@ -866,8 +867,7 @@ sclp_check_interface(void) spin_lock_irqsave(&sclp_lock, flags); /* Prepare init mask command */ - rc = register_early_external_interrupt(0x2401, sclp_check_handler, - &ext_int_info_hwc); + rc = register_external_interrupt(0x2401, sclp_check_handler); if (rc) { spin_unlock_irqrestore(&sclp_lock, flags); return rc; @@ -900,8 +900,7 @@ sclp_check_interface(void) } else rc = -EBUSY; } - unregister_early_external_interrupt(0x2401, sclp_check_handler, - &ext_int_info_hwc); + unregister_external_interrupt(0x2401, sclp_check_handler); spin_unlock_irqrestore(&sclp_lock, flags); return rc; } @@ -1064,8 +1063,7 @@ sclp_init(void) if (rc) goto fail_init_state_uninitialized; /* Register interrupt handler */ - rc = register_early_external_interrupt(0x2401, sclp_interrupt_handler, - &ext_int_info_hwc); + rc = register_external_interrupt(0x2401, sclp_interrupt_handler); if (rc) goto fail_unregister_reboot_notifier; sclp_init_state = sclp_init_state_initialized; diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c index b497afe061c..16e232a99fb 100644 --- a/drivers/s390/char/sclp_config.c +++ b/drivers/s390/char/sclp_config.c @@ -33,6 +33,7 @@ static void sclp_cpu_capability_notify(struct work_struct *work) int cpu; struct sys_device *sysdev; + s390_adjust_jiffies(); pr_warning("cpu capability changed.\n"); get_online_cpus(); for_each_online_cpu(cpu) { diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index deff2c3361e..fbe361fcd2c 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -24,6 +24,8 @@ #include "tape_std.h" #include "tape_3590.h" +static struct workqueue_struct *tape_3590_wq; + /* * Pointer to debug area. */ @@ -613,7 +615,7 @@ tape_3590_schedule_work(struct tape_device *device, enum tape_op op) p->device = tape_get_device(device); p->op = op; - schedule_work(&p->work); + queue_work(tape_3590_wq, &p->work); return 0; } @@ -1629,7 +1631,7 @@ fail_kmalloc: static void tape_3590_cleanup_device(struct tape_device *device) { - flush_scheduled_work(); + flush_workqueue(tape_3590_wq); tape_std_unassign(device); kfree(device->discdata); @@ -1733,11 +1735,17 @@ tape_3590_init(void) #endif DBF_EVENT(3, "3590 init\n"); + + tape_3590_wq = alloc_workqueue("tape_3590", 0, 0); + if (!tape_3590_wq) + return -ENOMEM; + /* Register driver for 3590 tapes. */ rc = ccw_driver_register(&tape_3590_driver); - if (rc) + if (rc) { + destroy_workqueue(tape_3590_wq); DBF_EVENT(3, "3590 init failed\n"); - else + } else DBF_EVENT(3, "3590 registered\n"); return rc; } @@ -1746,7 +1754,7 @@ static void tape_3590_exit(void) { ccw_driver_unregister(&tape_3590_driver); - + destroy_workqueue(tape_3590_wq); debug_unregister(TAPE_DBF_AREA); } diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index f0fa9ca5cb2..55d2d0f4eab 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -264,7 +264,7 @@ cleanup_queue: void tapeblock_cleanup_device(struct tape_device *device) { - flush_scheduled_work(); + flush_work_sync(&device->blk_data.requeue_task); tape_put_device(device); if (!device->blk_data.disk) { diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index 883e2db02bd..e090a307fde 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c @@ -17,7 +17,6 @@ #include <linux/types.h> #include <linux/proc_fs.h> #include <linux/mtio.h> -#include <linux/smp_lock.h> #include <linux/compat.h> #include <asm/uaccess.h> diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 6c408670e08..7978a0adeaf 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -14,6 +14,7 @@ #define KMSG_COMPONENT "tape" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/kernel_stat.h> #include <linux/module.h> #include <linux/init.h> // for kernel parameters #include <linux/kmod.h> // for requesting modules @@ -209,29 +210,79 @@ tape_state_set(struct tape_device *device, enum tape_state newstate) wake_up(&device->state_change_wq); } +struct tape_med_state_work_data { + struct tape_device *device; + enum tape_medium_state state; + struct work_struct work; +}; + +static void +tape_med_state_work_handler(struct work_struct *work) +{ + static char env_state_loaded[] = "MEDIUM_STATE=LOADED"; + static char env_state_unloaded[] = "MEDIUM_STATE=UNLOADED"; + struct tape_med_state_work_data *p = + container_of(work, struct tape_med_state_work_data, work); + struct tape_device *device = p->device; + char *envp[] = { NULL, NULL }; + + switch (p->state) { + case MS_UNLOADED: + pr_info("%s: The tape cartridge has been successfully " + "unloaded\n", dev_name(&device->cdev->dev)); + envp[0] = env_state_unloaded; + kobject_uevent_env(&device->cdev->dev.kobj, KOBJ_CHANGE, envp); + break; + case MS_LOADED: + pr_info("%s: A tape cartridge has been mounted\n", + dev_name(&device->cdev->dev)); + envp[0] = env_state_loaded; + kobject_uevent_env(&device->cdev->dev.kobj, KOBJ_CHANGE, envp); + break; + default: + break; + } + tape_put_device(device); + kfree(p); +} + +static void +tape_med_state_work(struct tape_device *device, enum tape_medium_state state) +{ + struct tape_med_state_work_data *p; + + p = kzalloc(sizeof(*p), GFP_ATOMIC); + if (p) { + INIT_WORK(&p->work, tape_med_state_work_handler); + p->device = tape_get_device(device); + p->state = state; + schedule_work(&p->work); + } +} + void tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate) { - if (device->medium_state == newstate) + enum tape_medium_state oldstate; + + oldstate = device->medium_state; + if (oldstate == newstate) return; + device->medium_state = newstate; switch(newstate){ case MS_UNLOADED: device->tape_generic_status |= GMT_DR_OPEN(~0); - if (device->medium_state == MS_LOADED) - pr_info("%s: The tape cartridge has been successfully " - "unloaded\n", dev_name(&device->cdev->dev)); + if (oldstate == MS_LOADED) + tape_med_state_work(device, MS_UNLOADED); break; case MS_LOADED: device->tape_generic_status &= ~GMT_DR_OPEN(~0); - if (device->medium_state == MS_UNLOADED) - pr_info("%s: A tape cartridge has been mounted\n", - dev_name(&device->cdev->dev)); + if (oldstate == MS_UNLOADED) + tape_med_state_work(device, MS_LOADED); break; default: - // print nothing break; } - device->medium_state = newstate; wake_up(&device->state_change_wq); } @@ -1064,6 +1115,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) struct tape_request *request; int rc; + kstat_cpu(smp_processor_id()).irqs[IOINT_TAP]++; device = dev_get_drvdata(&cdev->dev); if (device == NULL) { return; diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 9f661426e4a..c837d7419a6 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -30,7 +30,6 @@ #include <linux/kmod.h> #include <linux/cdev.h> #include <linux/device.h> -#include <linux/smp_lock.h> #include <linux/string.h> MODULE_AUTHOR @@ -249,27 +248,25 @@ static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, char cp_command[80]; char cp_response[160]; char *onoff, *qid_string; + int rc; - memset(cp_command, 0x00, sizeof(cp_command)); - memset(cp_response, 0x00, sizeof(cp_response)); - - onoff = ((action == 1) ? "ON" : "OFF"); + onoff = ((action == 1) ? "ON" : "OFF"); qid_string = ((recording_class_AB == 1) ? " QID * " : ""); - /* + /* * The recording commands needs to be called with option QID * for guests that have previlege classes A or B. * Purging has to be done as separate step, because recording * can't be switched on as long as records are on the queue. * Doing both at the same time doesn't work. */ - - if (purge) { + if (purge && (action == 1)) { + memset(cp_command, 0x00, sizeof(cp_command)); + memset(cp_response, 0x00, sizeof(cp_response)); snprintf(cp_command, sizeof(cp_command), "RECORDING %s PURGE %s", logptr->recording_name, qid_string); - cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); } @@ -279,19 +276,33 @@ static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, logptr->recording_name, onoff, qid_string); - cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); /* The recording command will usually answer with 'Command complete' * on success, but when the specific service was never connected * before then there might be an additional informational message * 'HCPCRC8072I Recording entry not found' before the - * 'Command complete'. So I use strstr rather then the strncmp. + * 'Command complete'. So I use strstr rather then the strncmp. */ if (strstr(cp_response,"Command complete")) - return 0; + rc = 0; else - return -EIO; + rc = -EIO; + /* + * If we turn recording off, we have to purge any remaining records + * afterwards, as a large number of queued records may impact z/VM + * performance. + */ + if (purge && (action == 0)) { + memset(cp_command, 0x00, sizeof(cp_command)); + memset(cp_response, 0x00, sizeof(cp_response)); + snprintf(cp_command, sizeof(cp_command), + "RECORDING %s PURGE %s", + logptr->recording_name, + qid_string); + cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); + } + return rc; } diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 1de672f2103..caef1757341 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -11,9 +11,9 @@ #define KMSG_COMPONENT "vmur" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/kernel_stat.h> #include <linux/cdev.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/cio.h> @@ -303,6 +303,7 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm, { struct urdev *urd; + kstat_cpu(smp_processor_id()).irqs[IOINT_VMR]++; TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n", intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat, irb->scsw.cmd.count); diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 97b25d68e3e..2864581d8ec 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -67,6 +67,27 @@ __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev) } /* + * Remove references from ccw devices to ccw group device and from + * ccw group device to ccw devices. + */ +static void __ccwgroup_remove_cdev_refs(struct ccwgroup_device *gdev) +{ + struct ccw_device *cdev; + int i; + + for (i = 0; i < gdev->count; i++) { + cdev = gdev->cdev[i]; + if (!cdev) + continue; + spin_lock_irq(cdev->ccwlock); + dev_set_drvdata(&cdev->dev, NULL); + spin_unlock_irq(cdev->ccwlock); + gdev->cdev[i] = NULL; + put_device(&cdev->dev); + } +} + +/* * Provide an 'ungroup' attribute so the user can remove group devices no * longer needed or accidentially created. Saves memory :) */ @@ -78,6 +99,7 @@ static void ccwgroup_ungroup_callback(struct device *dev) if (device_is_registered(&gdev->dev)) { __ccwgroup_remove_symlinks(gdev); device_unregister(dev); + __ccwgroup_remove_cdev_refs(gdev); } mutex_unlock(&gdev->reg_mutex); } @@ -116,21 +138,7 @@ static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store); static void ccwgroup_release (struct device *dev) { - struct ccwgroup_device *gdev; - int i; - - gdev = to_ccwgroupdev(dev); - - for (i = 0; i < gdev->count; i++) { - if (gdev->cdev[i]) { - spin_lock_irq(gdev->cdev[i]->ccwlock); - if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) - dev_set_drvdata(&gdev->cdev[i]->dev, NULL); - spin_unlock_irq(gdev->cdev[i]->ccwlock); - put_device(&gdev->cdev[i]->dev); - } - } - kfree(gdev); + kfree(to_ccwgroupdev(dev)); } static int @@ -639,6 +647,7 @@ void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver) mutex_lock(&gdev->reg_mutex); __ccwgroup_remove_symlinks(gdev); device_unregister(dev); + __ccwgroup_remove_cdev_refs(gdev); mutex_unlock(&gdev->reg_mutex); put_device(dev); } @@ -660,25 +669,6 @@ int ccwgroup_probe_ccwdev(struct ccw_device *cdev) return 0; } -static struct ccwgroup_device * -__ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev) -{ - struct ccwgroup_device *gdev; - - gdev = dev_get_drvdata(&cdev->dev); - if (gdev) { - if (get_device(&gdev->dev)) { - mutex_lock(&gdev->reg_mutex); - if (device_is_registered(&gdev->dev)) - return gdev; - mutex_unlock(&gdev->reg_mutex); - put_device(&gdev->dev); - } - return NULL; - } - return NULL; -} - /** * ccwgroup_remove_ccwdev() - remove function for slave devices * @cdev: ccw device to be removed @@ -694,13 +684,25 @@ void ccwgroup_remove_ccwdev(struct ccw_device *cdev) /* Ignore offlining errors, device is gone anyway. */ ccw_device_set_offline(cdev); /* If one of its devices is gone, the whole group is done for. */ - gdev = __ccwgroup_get_gdev_by_cdev(cdev); - if (gdev) { + spin_lock_irq(cdev->ccwlock); + gdev = dev_get_drvdata(&cdev->dev); + if (!gdev) { + spin_unlock_irq(cdev->ccwlock); + return; + } + /* Get ccwgroup device reference for local processing. */ + get_device(&gdev->dev); + spin_unlock_irq(cdev->ccwlock); + /* Unregister group device. */ + mutex_lock(&gdev->reg_mutex); + if (device_is_registered(&gdev->dev)) { __ccwgroup_remove_symlinks(gdev); device_unregister(&gdev->dev); - mutex_unlock(&gdev->reg_mutex); - put_device(&gdev->dev); + __ccwgroup_remove_cdev_refs(gdev); } + mutex_unlock(&gdev->reg_mutex); + /* Release ccwgroup device reference for local processing. */ + put_device(&gdev->dev); } MODULE_LICENSE("GPL"); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 1aaddea673e..0689fcf23a1 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -695,6 +695,25 @@ out: return ret; } +int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid, + struct channel_path_desc_fmt1 *desc) +{ + struct chsc_response_struct *chsc_resp; + struct chsc_scpd *scpd_area; + int ret; + + spin_lock_irq(&chsc_page_lock); + scpd_area = chsc_page; + ret = chsc_determine_channel_path_desc(chpid, 0, 0, 1, 0, scpd_area); + if (ret) + goto out; + chsc_resp = (void *)&scpd_area->response; + memcpy(desc, &chsc_resp->data, sizeof(*desc)); +out: + spin_unlock_irq(&chsc_page_lock); + return ret; +} + static void chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, struct cmg_chars *chars) diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 6693f5e3176..3f15b2aaeae 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -35,6 +35,22 @@ struct channel_path_desc { u8 chpp; } __attribute__ ((packed)); +struct channel_path_desc_fmt1 { + u8 flags; + u8 lsn; + u8 desc; + u8 chpid; + u32:24; + u8 chpp; + u32 unused[3]; + u16 mdc; + u16:13; + u8 r:1; + u8 s:1; + u8 f:1; + u32 zeros[2]; +} __attribute__ ((packed)); + struct channel_path; struct css_chsc_char { @@ -92,6 +108,8 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, int c, int m, void *page); int chsc_determine_base_channel_path_desc(struct chp_id chpid, struct channel_path_desc *desc); +int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid, + struct channel_path_desc_fmt1 *desc); void chsc_chp_online(struct chp_id chpid); void chsc_chp_offline(struct chp_id chpid); int chsc_get_channel_measurement_chars(struct channel_path *chp); diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index f4e6cf3aceb..430f875006f 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -619,7 +619,7 @@ void __irq_entry do_IRQ(struct pt_regs *regs) s390_idle_check(regs, S390_lowcore.int_clock, S390_lowcore.async_enter_timer); irq_enter(); - __get_cpu_var(s390_idle).nohz_delay = 1; + __this_cpu_write(s390_idle.nohz_delay, 1); if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) /* Serve timer interrupts first. */ clock_comparator_work(); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index a5050e21715..24d8e97355b 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -618,6 +618,7 @@ EXPORT_SYMBOL_GPL(css_schedule_reprobe); static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) { struct subchannel_id mchk_schid; + struct subchannel *sch; if (overflow) { css_schedule_eval_all(); @@ -635,8 +636,15 @@ static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) init_subchannel_id(&mchk_schid); mchk_schid.sch_no = crw0->rsid; if (crw1) - mchk_schid.ssid = (crw1->rsid >> 8) & 3; + mchk_schid.ssid = (crw1->rsid >> 4) & 3; + if (crw0->erc == CRW_ERC_PMOD) { + sch = get_subchannel_by_schid(mchk_schid); + if (sch) { + css_update_ssd_info(sch); + put_device(&sch->dev); + } + } /* * Since we are always presented with IPI in the CRW, we have to * use stsch() to find out if the subchannel in question has come diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 2ff8a22d425..e8391b89eff 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1455,7 +1455,16 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) break; case IO_SCH_UNREG_ATTACH: case IO_SCH_UNREG: - if (cdev) + if (!cdev) + break; + if (cdev->private->state == DEV_STATE_SENSE_ID) { + /* + * Note: delayed work triggered by this event + * and repeated calls to sch_event are synchronized + * by the above check for work_pending(cdev). + */ + dev_fsm_event(cdev, DEV_EVENT_NOTOPER); + } else ccw_device_set_notoper(cdev); break; case IO_SCH_NOP: diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 6da84543dfe..651976b54af 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -687,6 +687,46 @@ int ccw_device_tm_start_timeout(struct ccw_device *cdev, struct tcw *tcw, EXPORT_SYMBOL(ccw_device_tm_start_timeout); /** + * ccw_device_get_mdc - accumulate max data count + * @cdev: ccw device for which the max data count is accumulated + * @mask: mask of paths to use + * + * Return the number of 64K-bytes blocks all paths at least support + * for a transport command. Return values <= 0 indicate failures. + */ +int ccw_device_get_mdc(struct ccw_device *cdev, u8 mask) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct channel_path_desc_fmt1 desc; + struct chp_id chpid; + int mdc = 0, ret, i; + + /* Adjust requested path mask to excluded varied off paths. */ + if (mask) + mask &= sch->lpm; + else + mask = sch->lpm; + + chp_id_init(&chpid); + for (i = 0; i < 8; i++) { + if (!(mask & (0x80 >> i))) + continue; + chpid.id = sch->schib.pmcw.chpid[i]; + ret = chsc_determine_fmt1_channel_path_desc(chpid, &desc); + if (ret) + return ret; + if (!desc.f) + return 0; + if (!desc.r) + mdc = 1; + mdc = mdc ? min(mdc, (int)desc.mdc) : desc.mdc; + } + + return mdc; +} +EXPORT_SYMBOL(ccw_device_get_mdc); + +/** * ccw_device_tm_intrg - perform interrogate function * @cdev: ccw device on which to perform the interrogate function * diff --git a/drivers/s390/cio/itcw.c b/drivers/s390/cio/itcw.c index a0ae2956477..358ee16d10a 100644 --- a/drivers/s390/cio/itcw.c +++ b/drivers/s390/cio/itcw.c @@ -93,6 +93,7 @@ EXPORT_SYMBOL(itcw_get_tcw); size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) { size_t len; + int cross_count; /* Main data. */ len = sizeof(struct itcw); @@ -105,12 +106,27 @@ size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) /* TSB */ sizeof(struct tsb) + /* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw); } + /* Maximum required alignment padding. */ len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7; - /* Maximum padding for structures that may not cross 4k boundary. */ - if ((max_tidaws > 0) || (intrg_max_tidaws > 0)) - len += max(max_tidaws, intrg_max_tidaws) * - sizeof(struct tidaw) - 1; + + /* TIDAW lists may not cross a 4k boundary. To cross a + * boundary we need to add a TTIC TIDAW. We need to reserve + * one additional TIDAW for a TTIC that we may need to add due + * to the placement of the data chunk in memory, and a further + * TIDAW for each page boundary that the TIDAW list may cross + * due to it's own size. + */ + if (max_tidaws) { + cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1) + >> PAGE_SHIFT); + len += cross_count * sizeof(struct tidaw); + } + if (intrg_max_tidaws) { + cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1) + >> PAGE_SHIFT); + len += cross_count * sizeof(struct tidaw); + } return len; } EXPORT_SYMBOL(itcw_calc_size); @@ -165,6 +181,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, void *chunk; addr_t start; addr_t end; + int cross_count; /* Check for 2G limit. */ start = (addr_t) buffer; @@ -177,8 +194,17 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, if (IS_ERR(chunk)) return chunk; itcw = chunk; - itcw->max_tidaws = max_tidaws; - itcw->intrg_max_tidaws = intrg_max_tidaws; + /* allow for TTIC tidaws that may be needed to cross a page boundary */ + cross_count = 0; + if (max_tidaws) + cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1) + >> PAGE_SHIFT); + itcw->max_tidaws = max_tidaws + cross_count; + cross_count = 0; + if (intrg_max_tidaws) + cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1) + >> PAGE_SHIFT); + itcw->intrg_max_tidaws = intrg_max_tidaws + cross_count; /* Main TCW. */ chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0); if (IS_ERR(chunk)) @@ -198,7 +224,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, /* Data TIDAL. */ if (max_tidaws > 0) { chunk = fit_chunk(&start, end, sizeof(struct tidaw) * - max_tidaws, 16, 1); + itcw->max_tidaws, 16, 0); if (IS_ERR(chunk)) return chunk; tcw_set_data(itcw->tcw, chunk, 1); @@ -206,7 +232,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, /* Interrogate data TIDAL. */ if (intrg && (intrg_max_tidaws > 0)) { chunk = fit_chunk(&start, end, sizeof(struct tidaw) * - intrg_max_tidaws, 16, 1); + itcw->intrg_max_tidaws, 16, 0); if (IS_ERR(chunk)) return chunk; tcw_set_data(itcw->intrg_tcw, chunk, 1); @@ -283,13 +309,29 @@ EXPORT_SYMBOL(itcw_add_dcw); * the new tidaw on success or -%ENOSPC if the new tidaw would exceed the * available space. * - * Note: the tidaw-list is assumed to be contiguous with no ttics. The - * last-tidaw flag for the last tidaw in the list will be set by itcw_finalize. + * Note: TTIC tidaws are automatically added when needed, so explicitly calling + * this interface with the TTIC flag is not supported. The last-tidaw flag + * for the last tidaw in the list will be set by itcw_finalize. */ struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count) { + struct tidaw *following; + if (itcw->num_tidaws >= itcw->max_tidaws) return ERR_PTR(-ENOSPC); + /* + * Is the tidaw, which follows the one we are about to fill, on the next + * page? Then we have to insert a TTIC tidaw first, that points to the + * tidaw on the new page. + */ + following = ((struct tidaw *) tcw_get_data(itcw->tcw)) + + itcw->num_tidaws + 1; + if (itcw->num_tidaws && !((unsigned long) following & ~PAGE_MASK)) { + tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, + TIDAW_FLAGS_TTIC, following, 0); + if (itcw->num_tidaws >= itcw->max_tidaws) + return ERR_PTR(-ENOSPC); + } return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count); } EXPORT_SYMBOL(itcw_add_tidaw); diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 0f4ef8769a3..7bc643f3f5a 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -91,6 +91,12 @@ enum qdio_irq_states { #define AC1_SC_QEBSM_AVAILABLE 0x02 /* available for subchannel */ #define AC1_SC_QEBSM_ENABLED 0x01 /* enabled for subchannel */ +/* SIGA flags */ +#define QDIO_SIGA_WRITE 0x00 +#define QDIO_SIGA_READ 0x01 +#define QDIO_SIGA_SYNC 0x02 +#define QDIO_SIGA_QEBSM_FLAG 0x80 + #ifdef CONFIG_64BIT static inline int do_sqbs(u64 token, unsigned char state, int queue, int *start, int *count) @@ -142,10 +148,9 @@ struct siga_flag { u8 input:1; u8 output:1; u8 sync:1; - u8 no_sync_ti:1; - u8 no_sync_out_ti:1; - u8 no_sync_out_pci:1; - u8:2; + u8 sync_after_ai:1; + u8 sync_out_after_pci:1; + u8:3; } __attribute__ ((packed)); struct chsc_ssqd_area { @@ -202,6 +207,7 @@ struct qdio_dev_perf_stat { unsigned int inbound_queue_full; unsigned int outbound_call; unsigned int outbound_handler; + unsigned int outbound_queue_full; unsigned int fast_requeue; unsigned int target_full; unsigned int eqbs; @@ -245,10 +251,10 @@ struct qdio_input_q { struct qdio_output_q { /* PCIs are enabled for the queue */ int pci_out_enabled; - /* IQDIO: output multiple buffers (enhanced SIGA) */ - int use_enh_siga; /* timer to check for more outbound work */ struct timer_list timer; + /* used SBALs before tasklet schedule */ + int scan_threshold; }; /* @@ -383,12 +389,13 @@ static inline int multicast_outbound(struct qdio_q *q) (q->irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) #define is_qebsm(q) (q->irq_ptr->sch_token != 0) -#define need_siga_sync_thinint(q) (!q->irq_ptr->siga_flag.no_sync_ti) -#define need_siga_sync_out_thinint(q) (!q->irq_ptr->siga_flag.no_sync_out_ti) #define need_siga_in(q) (q->irq_ptr->siga_flag.input) #define need_siga_out(q) (q->irq_ptr->siga_flag.output) -#define need_siga_sync(q) (q->irq_ptr->siga_flag.sync) -#define siga_syncs_out_pci(q) (q->irq_ptr->siga_flag.no_sync_out_pci) +#define need_siga_sync(q) (unlikely(q->irq_ptr->siga_flag.sync)) +#define need_siga_sync_after_ai(q) \ + (unlikely(q->irq_ptr->siga_flag.sync_after_ai)) +#define need_siga_sync_out_after_pci(q) \ + (unlikely(q->irq_ptr->siga_flag.sync_out_after_pci)) #define for_each_input_queue(irq_ptr, q, i) \ for (i = 0, q = irq_ptr->input_qs[0]; \ @@ -423,9 +430,9 @@ struct indicator_t { extern struct indicator_t *q_indicators; -static inline int shared_ind(struct qdio_irq *irq_ptr) +static inline int shared_ind(u32 *dsci) { - return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; + return dsci == &q_indicators[TIQDIO_SHARED_IND].ind; } /* prototypes for thin interrupt */ diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 28868e7471a..f8b03a636e4 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -151,6 +151,7 @@ static char *qperf_names[] = { "Inbound queue full", "Outbound calls", "Outbound handler", + "Outbound queue full", "Outbound fast_requeue", "Outbound target_full", "QEBSM eqbs", diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 5fcfa7f9e9e..e9fff2b9bce 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -14,6 +14,7 @@ #include <linux/timer.h> #include <linux/delay.h> #include <linux/gfp.h> +#include <linux/kernel_stat.h> #include <asm/atomic.h> #include <asm/debug.h> #include <asm/qdio.h> @@ -29,11 +30,12 @@ MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>,"\ MODULE_DESCRIPTION("QDIO base support"); MODULE_LICENSE("GPL"); -static inline int do_siga_sync(struct subchannel_id schid, - unsigned int out_mask, unsigned int in_mask) +static inline int do_siga_sync(unsigned long schid, + unsigned int out_mask, unsigned int in_mask, + unsigned int fc) { - register unsigned long __fc asm ("0") = 2; - register struct subchannel_id __schid asm ("1") = schid; + register unsigned long __fc asm ("0") = fc; + register unsigned long __schid asm ("1") = schid; register unsigned long out asm ("2") = out_mask; register unsigned long in asm ("3") = in_mask; int cc; @@ -47,10 +49,11 @@ static inline int do_siga_sync(struct subchannel_id schid, return cc; } -static inline int do_siga_input(struct subchannel_id schid, unsigned int mask) +static inline int do_siga_input(unsigned long schid, unsigned int mask, + unsigned int fc) { - register unsigned long __fc asm ("0") = 1; - register struct subchannel_id __schid asm ("1") = schid; + register unsigned long __fc asm ("0") = fc; + register unsigned long __schid asm ("1") = schid; register unsigned long __mask asm ("2") = mask; int cc; @@ -279,16 +282,20 @@ void qdio_init_buf_states(struct qdio_irq *irq_ptr) static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output, unsigned int input) { + unsigned long schid = *((u32 *) &q->irq_ptr->schid); + unsigned int fc = QDIO_SIGA_SYNC; int cc; - if (!need_siga_sync(q)) - return 0; - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr); qperf_inc(q, siga_sync); - cc = do_siga_sync(q->irq_ptr->schid, output, input); - if (cc) + if (is_qebsm(q)) { + schid = q->irq_ptr->sch_token; + fc |= QDIO_SIGA_QEBSM_FLAG; + } + + cc = do_siga_sync(schid, output, input, fc); + if (unlikely(cc)) DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc); return cc; } @@ -301,38 +308,22 @@ static inline int qdio_siga_sync_q(struct qdio_q *q) return qdio_siga_sync(q, q->mask, 0); } -static inline int qdio_siga_sync_out(struct qdio_q *q) -{ - return qdio_siga_sync(q, ~0U, 0); -} - -static inline int qdio_siga_sync_all(struct qdio_q *q) -{ - return qdio_siga_sync(q, ~0U, ~0U); -} - static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit) { - unsigned long schid; - unsigned int fc = 0; + unsigned long schid = *((u32 *) &q->irq_ptr->schid); + unsigned int fc = QDIO_SIGA_WRITE; u64 start_time = 0; int cc; - if (q->u.out.use_enh_siga) - fc = 3; - if (is_qebsm(q)) { schid = q->irq_ptr->sch_token; - fc |= 0x80; + fc |= QDIO_SIGA_QEBSM_FLAG; } - else - schid = *((u32 *)&q->irq_ptr->schid); - again: cc = do_siga_output(schid, q->mask, busy_bit, fc); /* hipersocket busy condition */ - if (*busy_bit) { + if (unlikely(*busy_bit)) { WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2); if (!start_time) { @@ -347,32 +338,41 @@ again: static inline int qdio_siga_input(struct qdio_q *q) { + unsigned long schid = *((u32 *) &q->irq_ptr->schid); + unsigned int fc = QDIO_SIGA_READ; int cc; DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr); qperf_inc(q, siga_read); - cc = do_siga_input(q->irq_ptr->schid, q->mask); - if (cc) + if (is_qebsm(q)) { + schid = q->irq_ptr->sch_token; + fc |= QDIO_SIGA_QEBSM_FLAG; + } + + cc = do_siga_input(schid, q->mask, fc); + if (unlikely(cc)) DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc); return cc; } -static inline void qdio_sync_after_thinint(struct qdio_q *q) +#define qdio_siga_sync_out(q) qdio_siga_sync(q, ~0U, 0) +#define qdio_siga_sync_all(q) qdio_siga_sync(q, ~0U, ~0U) + +static inline void qdio_sync_queues(struct qdio_q *q) { - if (pci_out_supported(q)) { - if (need_siga_sync_thinint(q)) - qdio_siga_sync_all(q); - else if (need_siga_sync_out_thinint(q)) - qdio_siga_sync_out(q); - } else + /* PCI capable outbound queues will also be scanned so sync them too */ + if (pci_out_supported(q)) + qdio_siga_sync_all(q); + else qdio_siga_sync_q(q); } int debug_get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state) { - qdio_siga_sync_q(q); + if (need_siga_sync(q)) + qdio_siga_sync_q(q); return get_buf_states(q, bufnr, state, 1, 0); } @@ -549,7 +549,8 @@ static inline int qdio_inbound_q_done(struct qdio_q *q) if (!atomic_read(&q->nr_buf_used)) return 1; - qdio_siga_sync_q(q); + if (need_siga_sync(q)) + qdio_siga_sync_q(q); get_buf_state(q, q->first_to_check, &state, 0); if (state == SLSB_P_INPUT_PRIMED || state == SLSB_P_INPUT_ERROR) @@ -644,9 +645,12 @@ static int get_outbound_buffer_frontier(struct qdio_q *q) int count, stop; unsigned char state; - if (((queue_type(q) != QDIO_IQDIO_QFMT) && !pci_out_supported(q)) || - (queue_type(q) == QDIO_IQDIO_QFMT && multicast_outbound(q))) - qdio_siga_sync_q(q); + if (need_siga_sync(q)) + if (((queue_type(q) != QDIO_IQDIO_QFMT) && + !pci_out_supported(q)) || + (queue_type(q) == QDIO_IQDIO_QFMT && + multicast_outbound(q))) + qdio_siga_sync_q(q); /* * Don't check 128 buffers, as otherwise qdio_inbound_q_moved @@ -818,7 +822,8 @@ static inline void qdio_check_outbound_after_thinint(struct qdio_q *q) static void __tiqdio_inbound_processing(struct qdio_q *q) { qperf_inc(q, tasklet_inbound); - qdio_sync_after_thinint(q); + if (need_siga_sync(q) && need_siga_sync_after_ai(q)) + qdio_sync_queues(q); /* * The interrupt could be caused by a PCI request. Check the @@ -898,16 +903,14 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr) tasklet_schedule(&q->tasklet); } - if (!(irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED)) + if (!pci_out_supported(q)) return; for_each_output_queue(irq_ptr, q, i) { if (qdio_outbound_q_done(q)) continue; - - if (!siga_syncs_out_pci(q)) + if (need_siga_sync(q) && need_siga_sync_out_after_pci(q)) qdio_siga_sync_q(q); - tasklet_schedule(&q->tasklet); } } @@ -970,6 +973,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, return; } + kstat_cpu(smp_processor_id()).irqs[IOINT_QDI]++; if (irq_ptr->perf_stat_enabled) irq_ptr->perf_stat.qdio_int++; @@ -1273,7 +1277,6 @@ int qdio_establish(struct qdio_initialize *init_data) } qdio_setup_ssqd_info(irq_ptr); - DBF_EVENT("qDmmwc:%2x", irq_ptr->ssqd_desc.mmwc); DBF_EVENT("qib ac:%4x", irq_ptr->qib.ac); /* qebsm is now setup if available, initialize buffer states */ @@ -1445,52 +1448,38 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, used = atomic_add_return(count, &q->nr_buf_used); BUG_ON(used > QDIO_MAX_BUFFERS_PER_Q); + if (used == QDIO_MAX_BUFFERS_PER_Q) + qperf_inc(q, outbound_queue_full); + if (callflags & QDIO_FLAG_PCI_OUT) { q->u.out.pci_out_enabled = 1; qperf_inc(q, pci_request_int); - } - else + } else q->u.out.pci_out_enabled = 0; if (queue_type(q) == QDIO_IQDIO_QFMT) { - if (multicast_outbound(q)) + /* One SIGA-W per buffer required for unicast HiperSockets. */ + WARN_ON_ONCE(count > 1 && !multicast_outbound(q)); + + rc = qdio_kick_outbound_q(q); + } else if (need_siga_sync(q)) { + rc = qdio_siga_sync_q(q); + } else { + /* try to fast requeue buffers */ + get_buf_state(q, prev_buf(bufnr), &state, 0); + if (state != SLSB_CU_OUTPUT_PRIMED) rc = qdio_kick_outbound_q(q); else - if ((q->irq_ptr->ssqd_desc.mmwc > 1) && - (count > 1) && - (count <= q->irq_ptr->ssqd_desc.mmwc)) { - /* exploit enhanced SIGA */ - q->u.out.use_enh_siga = 1; - rc = qdio_kick_outbound_q(q); - } else { - /* - * One siga-w per buffer required for unicast - * HiperSockets. - */ - q->u.out.use_enh_siga = 0; - while (count--) { - rc = qdio_kick_outbound_q(q); - if (rc) - goto out; - } - } - goto out; - } - - if (need_siga_sync(q)) { - qdio_siga_sync_q(q); - goto out; + qperf_inc(q, fast_requeue); } - /* try to fast requeue buffers */ - get_buf_state(q, prev_buf(bufnr), &state, 0); - if (state != SLSB_CU_OUTPUT_PRIMED) - rc = qdio_kick_outbound_q(q); + /* in case of SIGA errors we must process the error immediately */ + if (used >= q->u.out.scan_threshold || rc) + tasklet_schedule(&q->tasklet); else - qperf_inc(q, fast_requeue); - -out: - tasklet_schedule(&q->tasklet); + /* free the SBALs in case of no further traffic */ + if (!timer_pending(&q->u.out.timer)) + mod_timer(&q->u.out.timer, jiffies + HZ); return rc; } @@ -1550,7 +1539,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr) WARN_ON(queue_irqs_enabled(q)); - if (!shared_ind(q->irq_ptr)) + if (!shared_ind(q->irq_ptr->dsci)) xchg(q->irq_ptr->dsci, 0); qdio_stop_polling(q); @@ -1560,7 +1549,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr) * We need to check again to not lose initiative after * resetting the ACK state. */ - if (!shared_ind(q->irq_ptr) && *q->irq_ptr->dsci) + if (!shared_ind(q->irq_ptr->dsci) && *q->irq_ptr->dsci) goto rescan; if (!qdio_inbound_q_done(q)) goto rescan; @@ -1600,12 +1589,14 @@ int qdio_get_next_buffers(struct ccw_device *cdev, int nr, int *bufnr, q = irq_ptr->input_qs[nr]; WARN_ON(queue_irqs_enabled(q)); - qdio_sync_after_thinint(q); - /* - * The interrupt could be caused by a PCI request. Check the - * PCI capable outbound queues. + * Cannot rely on automatic sync after interrupt since queues may + * also be examined without interrupt. */ + if (need_siga_sync(q)) + qdio_sync_queues(q); + + /* check the PCI capable outbound queues. */ qdio_check_outbound_after_thinint(q); if (!qdio_inbound_q_moved(q)) diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index a13cf7ec64b..89107d0938c 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -178,6 +178,7 @@ static void setup_queues(struct qdio_irq *irq_ptr, setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i); q->is_input_q = 0; + q->u.out.scan_threshold = qdio_init->scan_threshold; setup_storage_lists(q, irq_ptr, output_sbal_array, i); output_sbal_array += QDIO_MAX_BUFFERS_PER_Q; @@ -196,14 +197,10 @@ static void process_ac_flags(struct qdio_irq *irq_ptr, unsigned char qdioac) irq_ptr->siga_flag.output = 1; if (qdioac & AC1_SIGA_SYNC_NEEDED) irq_ptr->siga_flag.sync = 1; - if (qdioac & AC1_AUTOMATIC_SYNC_ON_THININT) - irq_ptr->siga_flag.no_sync_ti = 1; - if (qdioac & AC1_AUTOMATIC_SYNC_ON_OUT_PCI) - irq_ptr->siga_flag.no_sync_out_pci = 1; - - if (irq_ptr->siga_flag.no_sync_out_pci && - irq_ptr->siga_flag.no_sync_ti) - irq_ptr->siga_flag.no_sync_out_ti = 1; + if (!(qdioac & AC1_AUTOMATIC_SYNC_ON_THININT)) + irq_ptr->siga_flag.sync_after_ai = 1; + if (!(qdioac & AC1_AUTOMATIC_SYNC_ON_OUT_PCI)) + irq_ptr->siga_flag.sync_out_after_pci = 1; } static void check_and_setup_qebsm(struct qdio_irq *irq_ptr, @@ -451,7 +448,7 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, char s[80]; snprintf(s, 80, "qdio: %s %s on SC %x using " - "AI:%d QEBSM:%d PCI:%d TDD:%d SIGA:%s%s%s%s%s%s\n", + "AI:%d QEBSM:%d PCI:%d TDD:%d SIGA:%s%s%s%s%s\n", dev_name(&cdev->dev), (irq_ptr->qib.qfmt == QDIO_QETH_QFMT) ? "OSA" : ((irq_ptr->qib.qfmt == QDIO_ZFCP_QFMT) ? "ZFCP" : "HS"), @@ -463,9 +460,8 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, (irq_ptr->siga_flag.input) ? "R" : " ", (irq_ptr->siga_flag.output) ? "W" : " ", (irq_ptr->siga_flag.sync) ? "S" : " ", - (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " ", - (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " ", - (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " "); + (irq_ptr->siga_flag.sync_after_ai) ? "A" : " ", + (irq_ptr->siga_flag.sync_out_after_pci) ? "P" : " "); printk(KERN_INFO "%s", s); } diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 752dbee06af..5c4e741d822 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -8,6 +8,7 @@ */ #include <linux/io.h> #include <linux/slab.h> +#include <linux/kernel_stat.h> #include <asm/atomic.h> #include <asm/debug.h> #include <asm/qdio.h> @@ -35,22 +36,8 @@ static u8 *tiqdio_alsi; struct indicator_t *q_indicators; -static int css_qdio_omit_svs; - static u64 last_ai_time; -static inline unsigned long do_clear_global_summary(void) -{ - register unsigned long __fn asm("1") = 3; - register unsigned long __tmp asm("2"); - register unsigned long __time asm("3"); - - asm volatile( - " .insn rre,0xb2650000,2,0" - : "+d" (__fn), "=d" (__tmp), "=d" (__time)); - return __time; -} - /* returns addr for the device state change indicator */ static u32 *get_indicator(void) { @@ -83,10 +70,6 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) struct qdio_q *q; int i; - /* No TDD facility? If we must use SIGA-s we can also omit SVS. */ - if (!css_qdio_omit_svs && irq_ptr->siga_flag.sync) - css_qdio_omit_svs = 1; - mutex_lock(&tiq_list_lock); for_each_input_queue(irq_ptr, q, i) list_add_rcu(&q->entry, &tiq_list); @@ -112,9 +95,9 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) } } -static inline int shared_ind_used(void) +static inline u32 shared_ind_set(void) { - return atomic_read(&q_indicators[TIQDIO_SHARED_IND].count); + return q_indicators[TIQDIO_SHARED_IND].ind; } /** @@ -124,20 +107,11 @@ static inline int shared_ind_used(void) */ static void tiqdio_thinint_handler(void *alsi, void *data) { + u32 si_used = shared_ind_set(); struct qdio_q *q; last_ai_time = S390_lowcore.int_clock; - - /* - * SVS only when needed: issue SVS to benefit from iqdio interrupt - * avoidance (SVS clears adapter interrupt suppression overwrite). - */ - if (!css_qdio_omit_svs) - do_clear_global_summary(); - - /* reset local summary indicator */ - if (shared_ind_used()) - xchg(tiqdio_alsi, 0); + kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++; /* protect tiq_list entries, only changed in activate or shutdown */ rcu_read_lock(); @@ -146,7 +120,10 @@ static void tiqdio_thinint_handler(void *alsi, void *data) list_for_each_entry_rcu(q, &tiq_list, entry) { /* only process queues from changed sets */ - if (!*q->irq_ptr->dsci) + if (unlikely(shared_ind(q->irq_ptr->dsci))) { + if (!si_used) + continue; + } else if (!*q->irq_ptr->dsci) continue; if (q->u.in.queue_start_poll) { @@ -162,7 +139,7 @@ static void tiqdio_thinint_handler(void *alsi, void *data) q->irq_ptr->int_parm); } else { /* only clear it if the indicator is non-shared */ - if (!shared_ind(q->irq_ptr)) + if (!shared_ind(q->irq_ptr->dsci)) xchg(q->irq_ptr->dsci, 0); /* * Call inbound processing but not directly @@ -178,13 +155,8 @@ static void tiqdio_thinint_handler(void *alsi, void *data) * If the shared indicator was used clear it now after all queues * were processed. */ - if (shared_ind_used()) { + if (si_used && shared_ind_set()) xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); - - /* prevent racing */ - if (*tiqdio_alsi) - xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1 << 7); - } } static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) @@ -269,12 +241,6 @@ int qdio_establish_thinint(struct qdio_irq *irq_ptr) { if (!is_thinint_irq(irq_ptr)) return 0; - - /* Check for aif time delay disablement. If installed, - * omit SVS even under LPAR - */ - if (css_general_characteristics.aif_tdd) - css_qdio_omit_svs = 1; return set_subchannel_ind(irq_ptr, 0); } @@ -292,8 +258,8 @@ void qdio_shutdown_thinint(struct qdio_irq *irq_ptr) return; /* reset adapter interrupt indicators */ - put_indicator(irq_ptr->dsci); set_subchannel_ind(irq_ptr, 1); + put_indicator(irq_ptr->dsci); } void __exit tiqdio_unregister_thinints(void) diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 8fd8c62455e..67302b944ab 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -27,6 +27,7 @@ #define KMSG_COMPONENT "ap" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/kernel_stat.h> #include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> @@ -154,7 +155,7 @@ static inline int ap_instructions_available(void) */ static int ap_interrupts_available(void) { - return test_facility(1) && test_facility(2); + return test_facility(2) && test_facility(65); } /** @@ -221,6 +222,69 @@ ap_queue_interruption_control(ap_qid_t qid, void *ind) } #endif +static inline struct ap_queue_status __ap_4096_commands_available(ap_qid_t qid, + int *support) +{ + register unsigned long reg0 asm ("0") = 0UL | qid | (1UL << 23); + register struct ap_queue_status reg1 asm ("1"); + register unsigned long reg2 asm ("2") = 0UL; + + asm volatile( + ".long 0xb2af0000\n" + "0: la %1,0\n" + "1:\n" + EX_TABLE(0b, 1b) + : "+d" (reg0), "=d" (reg1), "=d" (reg2) + : + : "cc"); + + if (reg2 & 0x6000000000000000ULL) + *support = 1; + else + *support = 0; + + return reg1; +} + +/** + * ap_4096_commands_availablen(): Check for availability of 4096 bit RSA + * support. + * @qid: The AP queue number + * + * Returns 1 if 4096 bit RSA keys are support fo the AP, returns 0 if not. + */ +int ap_4096_commands_available(ap_qid_t qid) +{ + struct ap_queue_status status; + int i, support = 0; + status = __ap_4096_commands_available(qid, &support); + + for (i = 0; i < AP_MAX_RESET; i++) { + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + return support; + case AP_RESPONSE_RESET_IN_PROGRESS: + case AP_RESPONSE_BUSY: + break; + case AP_RESPONSE_Q_NOT_AVAIL: + case AP_RESPONSE_DECONFIGURED: + case AP_RESPONSE_CHECKSTOPPED: + case AP_RESPONSE_INVALID_ADDRESS: + return 0; + case AP_RESPONSE_OTHERWISE_CHANGED: + break; + default: + break; + } + if (i < AP_MAX_RESET - 1) { + udelay(5); + status = __ap_4096_commands_available(qid, &support); + } + } + return support; +} +EXPORT_SYMBOL(ap_4096_commands_available); + /** * ap_queue_enable_interruption(): Enable interruption on an AP. * @qid: The AP queue number @@ -1042,6 +1106,7 @@ out: static void ap_interrupt_handler(void *unused1, void *unused2) { + kstat_cpu(smp_processor_id()).irqs[IOINT_APB]++; tasklet_schedule(&ap_tasklet); } diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 4785d07cd44..08b9738285b 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -196,4 +196,6 @@ void ap_flush_queue(struct ap_device *ap_dev); int ap_module_init(void); void ap_module_exit(void); +int ap_4096_commands_available(ap_qid_t qid); + #endif /* _AP_BUS_H_ */ diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index f5221749d18..8e65447f76b 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -35,7 +35,6 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/compat.h> -#include <linux/smp_lock.h> #include <linux/slab.h> #include <asm/atomic.h> #include <asm/uaccess.h> @@ -397,8 +396,15 @@ static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt) if (copied == 0) { unsigned int len; spin_unlock_bh(&zcrypt_device_lock); - /* len is max 256 / 2 - 120 = 8 */ - len = crt->inputdatalength / 2 - 120; + /* len is max 256 / 2 - 120 = 8 + * For bigger device just assume len of leading + * 0s is 8 as stated in the requirements for + * ica_rsa_modexpo_crt struct in zcrypt.h. + */ + if (crt->inputdatalength <= 256) + len = crt->inputdatalength / 2 - 120; + else + len = 8; if (len > sizeof(z1)) return -EFAULT; z1 = z2 = z3 = 0; @@ -406,6 +412,7 @@ static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt) copy_from_user(&z2, crt->bp_key, len) || copy_from_user(&z3, crt->u_mult_inv, len)) return -EFAULT; + z1 = z2 = z3 = 0; copied = 1; /* * We have to restart device lookup - diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h index 8e7ffbf2466..88ebd114735 100644 --- a/drivers/s390/crypto/zcrypt_api.h +++ b/drivers/s390/crypto/zcrypt_api.h @@ -109,6 +109,7 @@ struct zcrypt_device { int request_count; /* # current requests. */ struct ap_message reply; /* Per-device reply structure. */ + int max_exp_bit_length; }; struct zcrypt_device *zcrypt_device_alloc(size_t); diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index 9c409efa1ec..2176d00b395 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -41,7 +41,7 @@ #define CEX2A_MIN_MOD_SIZE 1 /* 8 bits */ #define CEX2A_MAX_MOD_SIZE 256 /* 2048 bits */ #define CEX3A_MIN_MOD_SIZE CEX2A_MIN_MOD_SIZE -#define CEX3A_MAX_MOD_SIZE CEX2A_MAX_MOD_SIZE +#define CEX3A_MAX_MOD_SIZE 512 /* 4096 bits */ #define CEX2A_SPEED_RATING 970 #define CEX3A_SPEED_RATING 900 /* Fixme: Needs finetuning */ @@ -49,8 +49,10 @@ #define CEX2A_MAX_MESSAGE_SIZE 0x390 /* sizeof(struct type50_crb2_msg) */ #define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */ -#define CEX3A_MAX_MESSAGE_SIZE CEX2A_MAX_MESSAGE_SIZE -#define CEX3A_MAX_RESPONSE_SIZE CEX2A_MAX_RESPONSE_SIZE +#define CEX3A_MAX_RESPONSE_SIZE 0x210 /* 512 bit modulus + * (max outputdatalength) + + * type80_hdr*/ +#define CEX3A_MAX_MESSAGE_SIZE sizeof(struct type50_crb3_msg) #define CEX2A_CLEANUP_TIME (15*HZ) #define CEX3A_CLEANUP_TIME CEX2A_CLEANUP_TIME @@ -110,7 +112,7 @@ static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev, mod = meb1->modulus + sizeof(meb1->modulus) - mod_len; exp = meb1->exponent + sizeof(meb1->exponent) - mod_len; inp = meb1->message + sizeof(meb1->message) - mod_len; - } else { + } else if (mod_len <= 256) { struct type50_meb2_msg *meb2 = ap_msg->message; memset(meb2, 0, sizeof(*meb2)); ap_msg->length = sizeof(*meb2); @@ -120,6 +122,17 @@ static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev, mod = meb2->modulus + sizeof(meb2->modulus) - mod_len; exp = meb2->exponent + sizeof(meb2->exponent) - mod_len; inp = meb2->message + sizeof(meb2->message) - mod_len; + } else { + /* mod_len > 256 = 4096 bit RSA Key */ + struct type50_meb3_msg *meb3 = ap_msg->message; + memset(meb3, 0, sizeof(*meb3)); + ap_msg->length = sizeof(*meb3); + meb3->header.msg_type_code = TYPE50_TYPE_CODE; + meb3->header.msg_len = sizeof(*meb3); + meb3->keyblock_type = TYPE50_MEB3_FMT; + mod = meb3->modulus + sizeof(meb3->modulus) - mod_len; + exp = meb3->exponent + sizeof(meb3->exponent) - mod_len; + inp = meb3->message + sizeof(meb3->message) - mod_len; } if (copy_from_user(mod, mex->n_modulus, mod_len) || @@ -142,7 +155,7 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev, struct ap_message *ap_msg, struct ica_rsa_modexpo_crt *crt) { - int mod_len, short_len, long_len, long_offset; + int mod_len, short_len, long_len, long_offset, limit; unsigned char *p, *q, *dp, *dq, *u, *inp; mod_len = crt->inputdatalength; @@ -152,14 +165,20 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev, /* * CEX2A cannot handle p, dp, or U > 128 bytes. * If we have one of these, we need to do extra checking. + * For CEX3A the limit is 256 bytes. */ - if (long_len > 128) { + if (zdev->max_mod_size == CEX3A_MAX_MOD_SIZE) + limit = 256; + else + limit = 128; + + if (long_len > limit) { /* * zcrypt_rsa_crt already checked for the leading * zeroes of np_prime, bp_key and u_mult_inc. */ - long_offset = long_len - 128; - long_len = 128; + long_offset = long_len - limit; + long_len = limit; } else long_offset = 0; @@ -180,7 +199,7 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev, dq = crb1->dq + sizeof(crb1->dq) - short_len; u = crb1->u + sizeof(crb1->u) - long_len; inp = crb1->message + sizeof(crb1->message) - mod_len; - } else { + } else if (long_len <= 128) { struct type50_crb2_msg *crb2 = ap_msg->message; memset(crb2, 0, sizeof(*crb2)); ap_msg->length = sizeof(*crb2); @@ -193,6 +212,20 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev, dq = crb2->dq + sizeof(crb2->dq) - short_len; u = crb2->u + sizeof(crb2->u) - long_len; inp = crb2->message + sizeof(crb2->message) - mod_len; + } else { + /* long_len >= 256 */ + struct type50_crb3_msg *crb3 = ap_msg->message; + memset(crb3, 0, sizeof(*crb3)); + ap_msg->length = sizeof(*crb3); + crb3->header.msg_type_code = TYPE50_TYPE_CODE; + crb3->header.msg_len = sizeof(*crb3); + crb3->keyblock_type = TYPE50_CRB3_FMT; + p = crb3->p + sizeof(crb3->p) - long_len; + q = crb3->q + sizeof(crb3->q) - short_len; + dp = crb3->dp + sizeof(crb3->dp) - long_len; + dq = crb3->dq + sizeof(crb3->dq) - short_len; + u = crb3->u + sizeof(crb3->u) - long_len; + inp = crb3->message + sizeof(crb3->message) - mod_len; } if (copy_from_user(p, crt->np_prime + long_offset, long_len) || @@ -203,7 +236,6 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev, copy_from_user(inp, crt->inputdata, mod_len)) return -EFAULT; - return 0; } @@ -230,7 +262,10 @@ static int convert_type80(struct zcrypt_device *zdev, zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ } - BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE); + if (zdev->user_space_type == ZCRYPT_CEX2A) + BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE); + else + BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE); data = reply->message + t80h->len - outputdatalength; if (copy_to_user(outputdata, data, outputdatalength)) return -EFAULT; @@ -282,7 +317,10 @@ static void zcrypt_cex2a_receive(struct ap_device *ap_dev, } t80h = reply->message; if (t80h->type == TYPE80_RSP_CODE) { - length = min(CEX2A_MAX_RESPONSE_SIZE, (int) t80h->len); + if (ap_dev->device_type == AP_DEVICE_TYPE_CEX2A) + length = min(CEX2A_MAX_RESPONSE_SIZE, (int) t80h->len); + else + length = min(CEX3A_MAX_RESPONSE_SIZE, (int) t80h->len); memcpy(msg->message, reply->message, length); } else memcpy(msg->message, reply->message, sizeof error_reply); @@ -307,7 +345,10 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev, int rc; ap_init_message(&ap_msg); - ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); + if (zdev->user_space_type == ZCRYPT_CEX2A) + ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); + else + ap_msg.message = kmalloc(CEX3A_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; ap_msg.psmid = (((unsigned long long) current->pid) << 32) + @@ -345,7 +386,10 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev, int rc; ap_init_message(&ap_msg); - ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); + if (zdev->user_space_type == ZCRYPT_CEX2A) + ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); + else + ap_msg.message = kmalloc(CEX3A_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; ap_msg.psmid = (((unsigned long long) current->pid) << 32) + @@ -397,6 +441,7 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev) zdev->max_mod_size = CEX2A_MAX_MOD_SIZE; zdev->short_crt = 1; zdev->speed_rating = CEX2A_SPEED_RATING; + zdev->max_exp_bit_length = CEX2A_MAX_MOD_SIZE; break; case AP_DEVICE_TYPE_CEX3A: zdev = zcrypt_device_alloc(CEX3A_MAX_RESPONSE_SIZE); @@ -404,8 +449,13 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev) return -ENOMEM; zdev->user_space_type = ZCRYPT_CEX3A; zdev->type_string = "CEX3A"; - zdev->min_mod_size = CEX3A_MIN_MOD_SIZE; - zdev->max_mod_size = CEX3A_MAX_MOD_SIZE; + zdev->min_mod_size = CEX2A_MIN_MOD_SIZE; + zdev->max_mod_size = CEX2A_MAX_MOD_SIZE; + zdev->max_exp_bit_length = CEX2A_MAX_MOD_SIZE; + if (ap_4096_commands_available(ap_dev->qid)) { + zdev->max_mod_size = CEX3A_MAX_MOD_SIZE; + zdev->max_exp_bit_length = CEX3A_MAX_MOD_SIZE; + } zdev->short_crt = 1; zdev->speed_rating = CEX3A_SPEED_RATING; break; diff --git a/drivers/s390/crypto/zcrypt_cex2a.h b/drivers/s390/crypto/zcrypt_cex2a.h index 8f69d1dacab..0350665810c 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.h +++ b/drivers/s390/crypto/zcrypt_cex2a.h @@ -51,8 +51,10 @@ struct type50_hdr { #define TYPE50_MEB1_FMT 0x0001 #define TYPE50_MEB2_FMT 0x0002 +#define TYPE50_MEB3_FMT 0x0003 #define TYPE50_CRB1_FMT 0x0011 #define TYPE50_CRB2_FMT 0x0012 +#define TYPE50_CRB3_FMT 0x0013 /* Mod-Exp, with a small modulus */ struct type50_meb1_msg { @@ -74,6 +76,16 @@ struct type50_meb2_msg { unsigned char message[256]; } __attribute__((packed)); +/* Mod-Exp, with a larger modulus */ +struct type50_meb3_msg { + struct type50_hdr header; + unsigned short keyblock_type; /* 0x0003 */ + unsigned char reserved[6]; + unsigned char exponent[512]; + unsigned char modulus[512]; + unsigned char message[512]; +} __attribute__((packed)); + /* CRT, with a small modulus */ struct type50_crb1_msg { struct type50_hdr header; @@ -100,6 +112,19 @@ struct type50_crb2_msg { unsigned char message[256]; } __attribute__((packed)); +/* CRT, with a larger modulus */ +struct type50_crb3_msg { + struct type50_hdr header; + unsigned short keyblock_type; /* 0x0013 */ + unsigned char reserved[6]; + unsigned char p[256]; + unsigned char q[256]; + unsigned char dp[256]; + unsigned char dq[256]; + unsigned char u[256]; + unsigned char message[512]; +} __attribute__((packed)); + /** * The type 80 response family is associated with a CEX2A card. * diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c index 09e934b295a..1afb69c75fe 100644 --- a/drivers/s390/crypto/zcrypt_pcica.c +++ b/drivers/s390/crypto/zcrypt_pcica.c @@ -373,6 +373,7 @@ static int zcrypt_pcica_probe(struct ap_device *ap_dev) zdev->min_mod_size = PCICA_MIN_MOD_SIZE; zdev->max_mod_size = PCICA_MAX_MOD_SIZE; zdev->speed_rating = PCICA_SPEED_RATING; + zdev->max_exp_bit_length = PCICA_MAX_MOD_SIZE; ap_dev->reply = &zdev->reply; ap_dev->private = zdev; rc = zcrypt_device_register(zdev); diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c index 9dec5c77cff..aa4c050a569 100644 --- a/drivers/s390/crypto/zcrypt_pcicc.c +++ b/drivers/s390/crypto/zcrypt_pcicc.c @@ -579,6 +579,7 @@ static int zcrypt_pcicc_probe(struct ap_device *ap_dev) zdev->min_mod_size = PCICC_MIN_MOD_SIZE; zdev->max_mod_size = PCICC_MAX_MOD_SIZE; zdev->speed_rating = PCICC_SPEED_RATING; + zdev->max_exp_bit_length = PCICC_MAX_MOD_SIZE; ap_dev->reply = &zdev->reply; ap_dev->private = zdev; rc = zcrypt_device_register(zdev); diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 510fab4577d..4f85eb725f4 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -45,12 +45,12 @@ #define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */ #define PCIXCC_MAX_MOD_SIZE 256 /* 2048 bits */ #define CEX3C_MIN_MOD_SIZE PCIXCC_MIN_MOD_SIZE -#define CEX3C_MAX_MOD_SIZE PCIXCC_MAX_MOD_SIZE +#define CEX3C_MAX_MOD_SIZE 512 /* 4096 bits */ #define PCIXCC_MCL2_SPEED_RATING 7870 #define PCIXCC_MCL3_SPEED_RATING 7870 #define CEX2C_SPEED_RATING 7000 -#define CEX3C_SPEED_RATING 6500 /* FIXME: needs finetuning */ +#define CEX3C_SPEED_RATING 6500 #define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c /* max size type6 v2 crt message */ #define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */ @@ -567,6 +567,15 @@ static int convert_response_ica(struct zcrypt_device *zdev, case TYPE88_RSP_CODE: return convert_error(zdev, reply); case TYPE86_RSP_CODE: + if (msg->cprbx.ccp_rtcode && + (msg->cprbx.ccp_rscode == 0x14f) && + (outputdatalength > 256)) { + if (zdev->max_exp_bit_length <= 17) { + zdev->max_exp_bit_length = 17; + return -EAGAIN; + } else + return -EINVAL; + } if (msg->hdr.reply_code) return convert_error(zdev, reply); if (msg->cprbx.cprb_ver_id == 0x02) @@ -1052,11 +1061,13 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) zdev->speed_rating = PCIXCC_MCL2_SPEED_RATING; zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD; zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; + zdev->max_exp_bit_length = PCIXCC_MAX_MOD_SIZE; } else { zdev->type_string = "PCIXCC_MCL3"; zdev->speed_rating = PCIXCC_MCL3_SPEED_RATING; zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE; zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; + zdev->max_exp_bit_length = PCIXCC_MAX_MOD_SIZE; } break; case AP_DEVICE_TYPE_CEX2C: @@ -1065,6 +1076,7 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) zdev->speed_rating = CEX2C_SPEED_RATING; zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE; zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; + zdev->max_exp_bit_length = PCIXCC_MAX_MOD_SIZE; break; case AP_DEVICE_TYPE_CEX3C: zdev->user_space_type = ZCRYPT_CEX3C; @@ -1072,6 +1084,7 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) zdev->speed_rating = CEX3C_SPEED_RATING; zdev->min_mod_size = CEX3C_MIN_MOD_SIZE; zdev->max_mod_size = CEX3C_MAX_MOD_SIZE; + zdev->max_exp_bit_length = CEX3C_MAX_MOD_SIZE; break; default: goto out_free; diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 375aeeaf9ea..414427d64a8 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -10,6 +10,7 @@ * Author(s): Christian Borntraeger <borntraeger@de.ibm.com> */ +#include <linux/kernel_stat.h> #include <linux/init.h> #include <linux/bootmem.h> #include <linux/err.h> @@ -25,6 +26,7 @@ #include <asm/kvm_virtio.h> #include <asm/setup.h> #include <asm/s390_ext.h> +#include <asm/irq.h> #define VIRTIO_SUBCODE_64 0x0D00 @@ -379,6 +381,7 @@ static void kvm_extint_handler(unsigned int ext_int_code, u16 subcode; u32 param; + kstat_cpu(smp_processor_id()).irqs[EXTINT_VRT]++; subcode = ext_int_code >> 16; if ((subcode & 0xff00) != VIRTIO_SUBCODE_64) return; diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig index 456b1874339..fa80ba1f034 100644 --- a/drivers/s390/net/Kconfig +++ b/drivers/s390/net/Kconfig @@ -2,7 +2,8 @@ menu "S/390 network device drivers" depends on NETDEVICES && S390 config LCS - tristate "Lan Channel Station Interface" + def_tristate m + prompt "Lan Channel Station Interface" depends on CCW && NETDEVICES && (NET_ETHERNET || TR || FDDI) help Select this option if you want to use LCS networking on IBM System z. @@ -12,7 +13,8 @@ config LCS If you do not know what it is, it's safe to choose Y. config CTCM - tristate "CTC and MPC SNA device support" + def_tristate m + prompt "CTC and MPC SNA device support" depends on CCW && NETDEVICES help Select this option if you want to use channel-to-channel @@ -26,7 +28,8 @@ config CTCM If you do not need any channel-to-channel connection, choose N. config NETIUCV - tristate "IUCV network device support (VM only)" + def_tristate m + prompt "IUCV network device support (VM only)" depends on IUCV && NETDEVICES help Select this option if you want to use inter-user communication @@ -37,14 +40,16 @@ config NETIUCV The module name is netiucv. If unsure, choose Y. config SMSGIUCV - tristate "IUCV special message support (VM only)" + def_tristate m + prompt "IUCV special message support (VM only)" depends on IUCV help Select this option if you want to be able to receive SMSG messages from other VM guest systems. config SMSGIUCV_EVENT - tristate "Deliver IUCV special messages as uevents (VM only)" + def_tristate m + prompt "Deliver IUCV special messages as uevents (VM only)" depends on SMSGIUCV help Select this option to deliver CP special messages (SMSGs) as @@ -54,7 +59,8 @@ config SMSGIUCV_EVENT To compile as a module, choose M. The module name is "smsgiucv_app". config CLAW - tristate "CLAW device support" + def_tristate m + prompt "CLAW device support" depends on CCW && NETDEVICES help This driver supports channel attached CLAW devices. @@ -64,7 +70,8 @@ config CLAW To compile into the kernel, choose Y. config QETH - tristate "Gigabit Ethernet device support" + def_tristate y + prompt "Gigabit Ethernet device support" depends on CCW && NETDEVICES && IP_MULTICAST && QDIO help This driver supports the IBM System z OSA Express adapters @@ -78,25 +85,25 @@ config QETH The module name is qeth. config QETH_L2 - tristate "qeth layer 2 device support" - depends on QETH - help - Select this option to be able to run qeth devices in layer 2 mode. - To compile as a module, choose M. The module name is qeth_l2. - If unsure, choose y. + def_tristate y + prompt "qeth layer 2 device support" + depends on QETH + help + Select this option to be able to run qeth devices in layer 2 mode. + To compile as a module, choose M. The module name is qeth_l2. + If unsure, choose y. config QETH_L3 - tristate "qeth layer 3 device support" - depends on QETH - help - Select this option to be able to run qeth devices in layer 3 mode. - To compile as a module choose M. The module name is qeth_l3. - If unsure, choose Y. + def_tristate y + prompt "qeth layer 3 device support" + depends on QETH + help + Select this option to be able to run qeth devices in layer 3 mode. + To compile as a module choose M. The module name is qeth_l3. + If unsure, choose Y. config QETH_IPV6 - bool - depends on (QETH_L3 = IPV6) || (QETH_L3 && IPV6 = 'y') - default y + def_bool y if (QETH_L3 = IPV6) || (QETH_L3 && IPV6 = 'y') config CCWGROUP tristate diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 8e4153d740f..ce3a5c13ce0 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -63,6 +63,7 @@ #define KMSG_COMPONENT "claw" +#include <linux/kernel_stat.h> #include <asm/ccwdev.h> #include <asm/ccwgroup.h> #include <asm/debug.h> @@ -640,6 +641,7 @@ claw_irq_handler(struct ccw_device *cdev, struct claw_env *p_env; struct chbk *p_ch_r=NULL; + kstat_cpu(smp_processor_id()).irqs[IOINT_CLW]++; CLAW_DBF_TEXT(4, trace, "clawirq"); /* Bypass all 'unsolicited interrupts' */ privptr = dev_get_drvdata(&cdev->dev); diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 2c7d2d9be4d..4c284598592 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -24,6 +24,7 @@ #define KMSG_COMPONENT "ctcm" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/kernel_stat.h> #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> @@ -1204,6 +1205,7 @@ static void ctcm_irq_handler(struct ccw_device *cdev, int cstat; int dstat; + kstat_cpu(smp_processor_id()).irqs[IOINT_CTC]++; CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "Enter %s(%s)", CTCM_FUNTAIL, dev_name(&cdev->dev)); diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 0f19d540b65..09e7a053c84 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -26,6 +26,7 @@ #define KMSG_COMPONENT "lcs" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/kernel_stat.h> #include <linux/module.h> #include <linux/if.h> #include <linux/netdevice.h> @@ -1188,7 +1189,8 @@ lcs_remove_mc_addresses(struct lcs_card *card, struct in_device *in4_dev) spin_lock_irqsave(&card->ipm_lock, flags); list_for_each(l, &card->ipm_list) { ipm = list_entry(l, struct lcs_ipm_list, list); - for (im4 = in4_dev->mc_list; im4 != NULL; im4 = im4->next) { + for (im4 = rcu_dereference(in4_dev->mc_list); + im4 != NULL; im4 = rcu_dereference(im4->next_rcu)) { lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev); if ( (ipm->ipm.ip_addr == im4->multiaddr) && (memcmp(buf, &ipm->ipm.mac_addr, @@ -1233,7 +1235,8 @@ lcs_set_mc_addresses(struct lcs_card *card, struct in_device *in4_dev) unsigned long flags; LCS_DBF_TEXT(4, trace, "setmclst"); - for (im4 = in4_dev->mc_list; im4; im4 = im4->next) { + for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL; + im4 = rcu_dereference(im4->next_rcu)) { lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev); ipm = lcs_check_addr_entry(card, im4, buf); if (ipm != NULL) @@ -1269,10 +1272,10 @@ lcs_register_mc_addresses(void *data) in4_dev = in_dev_get(card->dev); if (in4_dev == NULL) goto out; - read_lock(&in4_dev->mc_list_lock); + rcu_read_lock(); lcs_remove_mc_addresses(card,in4_dev); lcs_set_mc_addresses(card, in4_dev); - read_unlock(&in4_dev->mc_list_lock); + rcu_read_unlock(); in_dev_put(in4_dev); netif_carrier_off(card->dev); @@ -1396,6 +1399,7 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) int rc, index; int cstat, dstat; + kstat_cpu(smp_processor_id()).irqs[IOINT_LCS]++; if (lcs_check_irb_error(cdev, irb)) return; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index e6b2df0e73f..29f848bfc12 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -2840,6 +2840,7 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, queue->card->perf_stats.outbound_do_qdio_time += qeth_get_micros() - queue->card->perf_stats.outbound_do_qdio_start_time; + atomic_add(count, &queue->used_buffers); if (rc) { queue->card->stats.tx_errors += count; /* ignore temporary SIGA errors without busy condition */ @@ -2853,7 +2854,6 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, qeth_schedule_recovery(queue->card); return; } - atomic_add(count, &queue->used_buffers); if (queue->card->options.performance_stats) queue->card->perf_stats.bufs_sent += count; } @@ -3831,6 +3831,8 @@ static int qeth_qdio_establish(struct qeth_card *card) init_data.int_parm = (unsigned long) card; init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; + init_data.scan_threshold = + (card->info.type == QETH_CARD_TYPE_IQD) ? 8 : 32; if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED, QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) { diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index e37dd8c4bf4..07d588867b5 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -333,7 +333,7 @@ struct qeth_arp_query_data { __u16 request_bits; __u16 reply_bits; __u32 no_entries; - char data; + char data; /* only for replies */ } __attribute__((packed)); /* used as parameter for arp_query reply */ diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index 42fa783a70c..b5e967cf7e2 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -372,7 +372,7 @@ static ssize_t qeth_dev_performance_stats_store(struct device *dev, i = simple_strtoul(buf, &tmp, 16); if ((i == 0) || (i == 1)) { if (i == card->options.performance_stats) - goto out;; + goto out; card->options.performance_stats = i; if (i == 0) memset(&card->perf_stats, 0, diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 847e8797073..7a7a1b66478 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -849,8 +849,6 @@ static int qeth_l2_open(struct net_device *dev) card->state = CARD_STATE_UP; netif_start_queue(dev); - if (!card->lan_online && netif_carrier_ok(dev)) - netif_carrier_off(dev); if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) { napi_enable(&card->napi); napi_schedule(&card->napi); @@ -1013,13 +1011,14 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) dev_warn(&card->gdev->dev, "The LAN is offline\n"); card->lan_online = 0; - goto out; + goto contin; } rc = -ENODEV; goto out_remove; } else card->lan_online = 1; +contin: if ((card->info.type == QETH_CARD_TYPE_OSD) || (card->info.type == QETH_CARD_TYPE_OSX)) /* configure isolation level */ @@ -1038,7 +1037,10 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) goto out_remove; } card->state = CARD_STATE_SOFTSETUP; - netif_carrier_on(card->dev); + if (card->lan_online) + netif_carrier_on(card->dev); + else + netif_carrier_off(card->dev); qeth_set_allowed_threads(card, 0xffffffff, 0); if (recover_flag == CARD_STATE_RECOVER) { @@ -1055,7 +1057,6 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) } /* let user_space know that device is online */ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); -out: mutex_unlock(&card->conf_mutex); mutex_unlock(&card->discipline_mutex); return 0; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 74d1401a5d5..e227e465bfc 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -30,6 +30,7 @@ #include "qeth_l3.h" + static int qeth_l3_set_offline(struct ccwgroup_device *); static int qeth_l3_recover(void *); static int qeth_l3_stop(struct net_device *); @@ -455,8 +456,11 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card) QETH_CARD_TEXT(card, 2, "sdiplist"); QETH_CARD_HEX(card, 2, &card, sizeof(void *)); - if (card->options.sniffer) + if ((card->state != CARD_STATE_UP && + card->state != CARD_STATE_SOFTSETUP) || card->options.sniffer) { return; + } + spin_lock_irqsave(&card->ip_lock, flags); tbd_list = card->ip_tbd_list; card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC); @@ -1796,7 +1800,8 @@ static void qeth_l3_add_mc(struct qeth_card *card, struct in_device *in4_dev) char buf[MAX_ADDR_LEN]; QETH_CARD_TEXT(card, 4, "addmc"); - for (im4 = in4_dev->mc_list; im4; im4 = im4->next) { + for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL; + im4 = rcu_dereference(im4->next_rcu)) { qeth_l3_get_mac_for_ipm(im4->multiaddr, buf, in4_dev->dev); ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); if (!ipm) @@ -1828,9 +1833,9 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card) in_dev = in_dev_get(netdev); if (!in_dev) continue; - read_lock(&in_dev->mc_list_lock); + rcu_read_lock(); qeth_l3_add_mc(card, in_dev); - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); in_dev_put(in_dev); } } @@ -1843,10 +1848,10 @@ static void qeth_l3_add_multicast_ipv4(struct qeth_card *card) in4_dev = in_dev_get(card->dev); if (in4_dev == NULL) return; - read_lock(&in4_dev->mc_list_lock); + rcu_read_lock(); qeth_l3_add_mc(card, in4_dev); qeth_l3_add_vlan_mc(card); - read_unlock(&in4_dev->mc_list_lock); + rcu_read_unlock(); in_dev_put(in4_dev); } @@ -2454,22 +2459,46 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries) return rc; } -static void qeth_l3_copy_arp_entries_stripped(struct qeth_arp_query_info *qinfo, - struct qeth_arp_query_data *qdata, int entry_size, - int uentry_size) +static __u32 get_arp_entry_size(struct qeth_card *card, + struct qeth_arp_query_data *qdata, + struct qeth_arp_entrytype *type, __u8 strip_entries) { - char *entry_ptr; - char *uentry_ptr; - int i; + __u32 rc; + __u8 is_hsi; - entry_ptr = (char *)&qdata->data; - uentry_ptr = (char *)(qinfo->udata + qinfo->udata_offset); - for (i = 0; i < qdata->no_entries; ++i) { - /* strip off 32 bytes "media specific information" */ - memcpy(uentry_ptr, (entry_ptr + 32), entry_size - 32); - entry_ptr += entry_size; - uentry_ptr += uentry_size; + is_hsi = qdata->reply_bits == 5; + if (type->ip == QETHARP_IP_ADDR_V4) { + QETH_CARD_TEXT(card, 4, "arpev4"); + if (strip_entries) { + rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5_short) : + sizeof(struct qeth_arp_qi_entry7_short); + } else { + rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5) : + sizeof(struct qeth_arp_qi_entry7); + } + } else if (type->ip == QETHARP_IP_ADDR_V6) { + QETH_CARD_TEXT(card, 4, "arpev6"); + if (strip_entries) { + rc = is_hsi ? + sizeof(struct qeth_arp_qi_entry5_short_ipv6) : + sizeof(struct qeth_arp_qi_entry7_short_ipv6); + } else { + rc = is_hsi ? + sizeof(struct qeth_arp_qi_entry5_ipv6) : + sizeof(struct qeth_arp_qi_entry7_ipv6); + } + } else { + QETH_CARD_TEXT(card, 4, "arpinv"); + rc = 0; } + + return rc; +} + +static int arpentry_matches_prot(struct qeth_arp_entrytype *type, __u16 prot) +{ + return (type->ip == QETHARP_IP_ADDR_V4 && prot == QETH_PROT_IPV4) || + (type->ip == QETHARP_IP_ADDR_V6 && prot == QETH_PROT_IPV6); } static int qeth_l3_arp_query_cb(struct qeth_card *card, @@ -2478,72 +2507,77 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card, struct qeth_ipa_cmd *cmd; struct qeth_arp_query_data *qdata; struct qeth_arp_query_info *qinfo; - int entry_size; - int uentry_size; int i; + int e; + int entrybytes_done; + int stripped_bytes; + __u8 do_strip_entries; - QETH_CARD_TEXT(card, 4, "arpquecb"); + QETH_CARD_TEXT(card, 3, "arpquecb"); qinfo = (struct qeth_arp_query_info *) reply->param; cmd = (struct qeth_ipa_cmd *) data; + QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.prot_version); if (cmd->hdr.return_code) { - QETH_CARD_TEXT_(card, 4, "qaer1%i", cmd->hdr.return_code); + QETH_CARD_TEXT(card, 4, "arpcberr"); + QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code); return 0; } if (cmd->data.setassparms.hdr.return_code) { cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; - QETH_CARD_TEXT_(card, 4, "qaer2%i", cmd->hdr.return_code); + QETH_CARD_TEXT(card, 4, "setaperr"); + QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code); return 0; } qdata = &cmd->data.setassparms.data.query_arp; - switch (qdata->reply_bits) { - case 5: - uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry5); - if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) - uentry_size = sizeof(struct qeth_arp_qi_entry5_short); - break; - case 7: - /* fall through to default */ - default: - /* tr is the same as eth -> entry7 */ - uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry7); - if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) - uentry_size = sizeof(struct qeth_arp_qi_entry7_short); - break; - } - /* check if there is enough room in userspace */ - if ((qinfo->udata_len - qinfo->udata_offset) < - qdata->no_entries * uentry_size){ - QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM); - cmd->hdr.return_code = -ENOMEM; - goto out_error; - } - QETH_CARD_TEXT_(card, 4, "anore%i", - cmd->data.setassparms.hdr.number_of_replies); - QETH_CARD_TEXT_(card, 4, "aseqn%i", cmd->data.setassparms.hdr.seq_no); QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries); - if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) { - /* strip off "media specific information" */ - qeth_l3_copy_arp_entries_stripped(qinfo, qdata, entry_size, - uentry_size); - } else - /*copy entries to user buffer*/ - memcpy(qinfo->udata + qinfo->udata_offset, - (char *)&qdata->data, qdata->no_entries*uentry_size); + do_strip_entries = (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) > 0; + stripped_bytes = do_strip_entries ? QETH_QARP_MEDIASPECIFIC_BYTES : 0; + entrybytes_done = 0; + for (e = 0; e < qdata->no_entries; ++e) { + char *cur_entry; + __u32 esize; + struct qeth_arp_entrytype *etype; + + cur_entry = &qdata->data + entrybytes_done; + etype = &((struct qeth_arp_qi_entry5 *) cur_entry)->type; + if (!arpentry_matches_prot(etype, cmd->hdr.prot_version)) { + QETH_CARD_TEXT(card, 4, "pmis"); + QETH_CARD_TEXT_(card, 4, "%i", etype->ip); + break; + } + esize = get_arp_entry_size(card, qdata, etype, + do_strip_entries); + QETH_CARD_TEXT_(card, 5, "esz%i", esize); + if (!esize) + break; + + if ((qinfo->udata_len - qinfo->udata_offset) < esize) { + QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM); + cmd->hdr.return_code = -ENOMEM; + goto out_error; + } - qinfo->no_entries += qdata->no_entries; - qinfo->udata_offset += (qdata->no_entries*uentry_size); + memcpy(qinfo->udata + qinfo->udata_offset, + &qdata->data + entrybytes_done + stripped_bytes, + esize); + entrybytes_done += esize + stripped_bytes; + qinfo->udata_offset += esize; + ++qinfo->no_entries; + } /* check if all replies received ... */ if (cmd->data.setassparms.hdr.seq_no < cmd->data.setassparms.hdr.number_of_replies) return 1; + QETH_CARD_TEXT_(card, 4, "nove%i", qinfo->no_entries); memcpy(qinfo->udata, &qinfo->no_entries, 4); /* keep STRIP_ENTRIES flag so the user program can distinguish * stripped entries from normal ones */ if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES; memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2); + QETH_CARD_TEXT_(card, 4, "rc%i", 0); return 0; out_error: i = 0; @@ -2566,45 +2600,86 @@ static int qeth_l3_send_ipa_arp_cmd(struct qeth_card *card, reply_cb, reply_param); } -static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) +static int qeth_l3_query_arp_cache_info(struct qeth_card *card, + enum qeth_prot_versions prot, + struct qeth_arp_query_info *qinfo) { struct qeth_cmd_buffer *iob; - struct qeth_arp_query_info qinfo = {0, }; + struct qeth_ipa_cmd *cmd; int tmp; int rc; + QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot); + + iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_ARP_QUERY_INFO, + sizeof(struct qeth_arp_query_data) - sizeof(char), + prot); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setassparms.data.query_arp.request_bits = 0x000F; + cmd->data.setassparms.data.query_arp.reply_bits = 0; + cmd->data.setassparms.data.query_arp.no_entries = 0; + rc = qeth_l3_send_ipa_arp_cmd(card, iob, + QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN, + qeth_l3_arp_query_cb, (void *)qinfo); + if (rc) { + tmp = rc; + QETH_DBF_MESSAGE(2, + "Error while querying ARP cache on %s: %s " + "(0x%x/%d)\n", QETH_CARD_IFNAME(card), + qeth_l3_arp_get_error_cause(&rc), tmp, tmp); + } + + return rc; +} + +static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) +{ + struct qeth_arp_query_info qinfo = {0, }; + int rc; + QETH_CARD_TEXT(card, 3, "arpquery"); if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/ IPA_ARP_PROCESSING)) { - return -EOPNOTSUPP; + QETH_CARD_TEXT(card, 3, "arpqnsup"); + rc = -EOPNOTSUPP; + goto out; } /* get size of userspace buffer and mask_bits -> 6 bytes */ - if (copy_from_user(&qinfo, udata, 6)) - return -EFAULT; + if (copy_from_user(&qinfo, udata, 6)) { + rc = -EFAULT; + goto out; + } qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); - if (!qinfo.udata) - return -ENOMEM; + if (!qinfo.udata) { + rc = -ENOMEM; + goto out; + } qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET; - iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_QUERY_INFO, - sizeof(int), QETH_PROT_IPV4); - - rc = qeth_l3_send_ipa_arp_cmd(card, iob, - QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN, - qeth_l3_arp_query_cb, (void *)&qinfo); + rc = qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV4, &qinfo); if (rc) { - tmp = rc; - QETH_DBF_MESSAGE(2, "Error while querying ARP cache on %s: %s " - "(0x%x/%d)\n", QETH_CARD_IFNAME(card), - qeth_l3_arp_get_error_cause(&rc), tmp, tmp); if (copy_to_user(udata, qinfo.udata, 4)) rc = -EFAULT; + goto free_and_out; } else { - if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) +#ifdef CONFIG_QETH_IPV6 + if (qinfo.mask_bits & QETH_QARP_WITH_IPV6) { + /* fails in case of GuestLAN QDIO mode */ + qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV6, + &qinfo); + } +#endif + if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) { + QETH_CARD_TEXT(card, 4, "qactf"); rc = -EFAULT; + goto free_and_out; + } + QETH_CARD_TEXT_(card, 4, "qacts"); } +free_and_out: kfree(qinfo.udata); +out: return rc; } @@ -2938,6 +3013,7 @@ static void qeth_tso_fill_header(struct qeth_card *card, /*fix header to TSO values ...*/ hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO; + hdr->hdr.hdr.l3.length = skb->len - sizeof(struct qeth_hdr_tso); /*set values which are fix for the first approach ...*/ hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso); hdr->ext.imb_hdr_no = 1; @@ -3039,7 +3115,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) skb_pull(new_skb, ETH_HLEN); } - if (ipv == 6 && card->vlangrp && + if (ipv != 4 && card->vlangrp && vlan_tx_tag_present(new_skb)) { skb_push(new_skb, VLAN_HLEN); skb_copy_to_linear_data(new_skb, new_skb->data + 4, 4); @@ -3176,8 +3252,6 @@ static int qeth_l3_open(struct net_device *dev) card->state = CARD_STATE_UP; netif_start_queue(dev); - if (!card->lan_online && netif_carrier_ok(dev)) - netif_carrier_off(dev); if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) { napi_enable(&card->napi); napi_schedule(&card->napi); @@ -3449,13 +3523,14 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) dev_warn(&card->gdev->dev, "The LAN is offline\n"); card->lan_online = 0; - goto out; + goto contin; } rc = -ENODEV; goto out_remove; } else card->lan_online = 1; +contin: rc = qeth_l3_setadapter_parms(card); if (rc) QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); @@ -3480,10 +3555,13 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) goto out_remove; } card->state = CARD_STATE_SOFTSETUP; - netif_carrier_on(card->dev); qeth_set_allowed_threads(card, 0xffffffff, 0); qeth_l3_set_ip_addr_list(card); + if (card->lan_online) + netif_carrier_on(card->dev); + else + netif_carrier_off(card->dev); if (recover_flag == CARD_STATE_RECOVER) { if (recovery_mode) qeth_l3_open(card->dev); @@ -3496,7 +3574,6 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) } /* let user_space know that device is online */ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); -out: mutex_unlock(&card->conf_mutex); mutex_unlock(&card->discipline_mutex); return 0; diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 044fb22718d..51c666fb67a 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -45,8 +45,8 @@ static char *init_device; module_param_named(device, init_device, charp, 0400); MODULE_PARM_DESC(device, "specify initial device"); -static struct kmem_cache *zfcp_cache_hw_align(const char *name, - unsigned long size) +static struct kmem_cache * __init zfcp_cache_hw_align(const char *name, + unsigned long size) { return kmem_cache_create(name, size, roundup_pow_of_two(size), 0, NULL); } @@ -311,8 +311,7 @@ int zfcp_status_read_refill(struct zfcp_adapter *adapter) if (zfcp_fsf_status_read(adapter->qdio)) { if (atomic_read(&adapter->stat_miss) >= adapter->stat_read_buf_num) { - zfcp_erp_adapter_reopen(adapter, 0, "axsref1", - NULL); + zfcp_erp_adapter_reopen(adapter, 0, "axsref1"); return 1; } break; @@ -459,7 +458,7 @@ void zfcp_adapter_unregister(struct zfcp_adapter *adapter) sysfs_remove_group(&cdev->dev.kobj, &zfcp_sysfs_adapter_attrs); zfcp_erp_thread_kill(adapter); - zfcp_dbf_adapter_unregister(adapter->dbf); + zfcp_dbf_adapter_unregister(adapter); zfcp_qdio_destroy(adapter->qdio); zfcp_ccw_adapter_put(adapter); /* final put to release */ diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index 0833c2b51e3..4f7852dd30c 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -48,7 +48,7 @@ static int zfcp_ccw_activate(struct ccw_device *cdev) zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, - "ccresu2", NULL); + "ccresu2"); zfcp_erp_wait(adapter); flush_work(&adapter->scan_work); @@ -182,7 +182,7 @@ static int zfcp_ccw_set_offline(struct ccw_device *cdev) if (!adapter) return 0; - zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1", NULL); + zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1"); zfcp_erp_wait(adapter); zfcp_ccw_adapter_put(adapter); @@ -207,24 +207,24 @@ static int zfcp_ccw_notify(struct ccw_device *cdev, int event) switch (event) { case CIO_GONE: dev_warn(&cdev->dev, "The FCP device has been detached\n"); - zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti1", NULL); + zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti1"); break; case CIO_NO_PATH: dev_warn(&cdev->dev, "The CHPID for the FCP device is offline\n"); - zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti2", NULL); + zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti2"); break; case CIO_OPER: dev_info(&cdev->dev, "The FCP device is operational again\n"); zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, - "ccnoti4", NULL); + "ccnoti4"); break; case CIO_BOXED: dev_warn(&cdev->dev, "The FCP device did not respond within " "the specified time\n"); - zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti5", NULL); + zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti5"); break; } @@ -243,7 +243,7 @@ static void zfcp_ccw_shutdown(struct ccw_device *cdev) if (!adapter) return; - zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1", NULL); + zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1"); zfcp_erp_wait(adapter); zfcp_erp_thread_kill(adapter); diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c index d692e229ecb..46342fee394 100644 --- a/drivers/s390/scsi/zfcp_cfdc.c +++ b/drivers/s390/scsi/zfcp_cfdc.c @@ -288,7 +288,7 @@ void zfcp_cfdc_adapter_access_changed(struct zfcp_adapter *adapter) (status & ZFCP_STATUS_COMMON_ACCESS_BOXED)) zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, - "cfaac_1", NULL); + "cfaac_1"); } read_unlock_irqrestore(&adapter->port_list_lock, flags); @@ -299,7 +299,7 @@ void zfcp_cfdc_adapter_access_changed(struct zfcp_adapter *adapter) (status & ZFCP_STATUS_COMMON_ACCESS_BOXED)) zfcp_erp_lun_reopen(sdev, ZFCP_STATUS_COMMON_ERP_FAILED, - "cfaac_2", NULL); + "cfaac_2"); } } @@ -426,7 +426,7 @@ int zfcp_cfdc_open_lun_eval(struct scsi_device *sdev, zfcp_scsi_dev_lun(sdev), (unsigned long long)zfcp_sdev->port->wwpn); zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_ERP_FAILED); - zfcp_erp_lun_shutdown(sdev, 0, "fsouh_6", NULL); + zfcp_erp_lun_shutdown(sdev, 0, "fsouh_6"); return -EACCES; } @@ -437,7 +437,7 @@ int zfcp_cfdc_open_lun_eval(struct scsi_device *sdev, zfcp_scsi_dev_lun(sdev), (unsigned long long)zfcp_sdev->port->wwpn); zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_ERP_FAILED); - zfcp_erp_lun_shutdown(sdev, 0, "fsosh_8", NULL); + zfcp_erp_lun_shutdown(sdev, 0, "fsosh_8"); return -EACCES; } diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 2cdd6b28ff7..96d1462e0bf 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -3,7 +3,7 @@ * * Debug traces for zfcp. * - * Copyright IBM Corporation 2002, 2009 + * Copyright IBM Corporation 2002, 2010 */ #define KMSG_COMPONENT "zfcp" @@ -22,980 +22,392 @@ module_param(dbfsize, uint, 0400); MODULE_PARM_DESC(dbfsize, "number of pages for each debug feature area (default 4)"); -static void zfcp_dbf_hexdump(debug_info_t *dbf, void *to, int to_len, - int level, char *from, int from_len) +static inline unsigned int zfcp_dbf_plen(unsigned int offset) { - int offset; - struct zfcp_dbf_dump *dump = to; - int room = to_len - sizeof(*dump); - - for (offset = 0; offset < from_len; offset += dump->size) { - memset(to, 0, to_len); - strncpy(dump->tag, "dump", ZFCP_DBF_TAG_SIZE); - dump->total_size = from_len; - dump->offset = offset; - dump->size = min(from_len - offset, room); - memcpy(dump->data, from + offset, dump->size); - debug_event(dbf, level, dump, dump->size + sizeof(*dump)); - } + return sizeof(struct zfcp_dbf_pay) + offset - ZFCP_DBF_PAY_MAX_REC; } -static void zfcp_dbf_tag(char **p, const char *label, const char *tag) +static inline +void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area, + u64 req_id) { - int i; - - *p += sprintf(*p, "%-24s", label); - for (i = 0; i < ZFCP_DBF_TAG_SIZE; i++) - *p += sprintf(*p, "%c", tag[i]); - *p += sprintf(*p, "\n"); -} + struct zfcp_dbf_pay *pl = &dbf->pay_buf; + u16 offset = 0, rec_length; -static void zfcp_dbf_outs(char **buf, const char *s1, const char *s2) -{ - *buf += sprintf(*buf, "%-24s%s\n", s1, s2); -} + spin_lock(&dbf->pay_lock); + memset(pl, 0, sizeof(*pl)); + pl->fsf_req_id = req_id; + memcpy(pl->area, area, ZFCP_DBF_TAG_LEN); -static void zfcp_dbf_out(char **buf, const char *s, const char *format, ...) -{ - va_list arg; + while (offset < length) { + rec_length = min((u16) ZFCP_DBF_PAY_MAX_REC, + (u16) (length - offset)); + memcpy(pl->data, data + offset, rec_length); + debug_event(dbf->pay, 1, pl, zfcp_dbf_plen(rec_length)); - *buf += sprintf(*buf, "%-24s", s); - va_start(arg, format); - *buf += vsprintf(*buf, format, arg); - va_end(arg); - *buf += sprintf(*buf, "\n"); -} - -static void zfcp_dbf_outd(char **p, const char *label, char *buffer, - int buflen, int offset, int total_size) -{ - if (!offset) - *p += sprintf(*p, "%-24s ", label); - while (buflen--) { - if (offset > 0) { - if ((offset % 32) == 0) - *p += sprintf(*p, "\n%-24c ", ' '); - else if ((offset % 4) == 0) - *p += sprintf(*p, " "); - } - *p += sprintf(*p, "%02x", *buffer++); - if (++offset == total_size) { - *p += sprintf(*p, "\n"); - break; - } + offset += rec_length; + pl->counter++; } - if (!total_size) - *p += sprintf(*p, "\n"); -} -static int zfcp_dbf_view_header(debug_info_t *id, struct debug_view *view, - int area, debug_entry_t *entry, char *out_buf) -{ - struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)DEBUG_DATA(entry); - struct timespec t; - char *p = out_buf; - - if (strncmp(dump->tag, "dump", ZFCP_DBF_TAG_SIZE) != 0) { - stck_to_timespec(entry->id.stck, &t); - zfcp_dbf_out(&p, "timestamp", "%011lu:%06lu", - t.tv_sec, t.tv_nsec); - zfcp_dbf_out(&p, "cpu", "%02i", entry->id.fields.cpuid); - } else { - zfcp_dbf_outd(&p, "", dump->data, dump->size, dump->offset, - dump->total_size); - if ((dump->offset + dump->size) == dump->total_size) - p += sprintf(p, "\n"); - } - return p - out_buf; + spin_unlock(&dbf->pay_lock); } -void _zfcp_dbf_hba_fsf_response(const char *tag2, int level, - struct zfcp_fsf_req *fsf_req, - struct zfcp_dbf *dbf) +/** + * zfcp_dbf_hba_fsf_res - trace event for fsf responses + * @tag: tag indicating which kind of unsolicited status has been received + * @req: request for which a response was received + */ +void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req) { - struct fsf_qtcb *qtcb = fsf_req->qtcb; - union fsf_prot_status_qual *prot_status_qual = - &qtcb->prefix.prot_status_qual; - union fsf_status_qual *fsf_status_qual = &qtcb->header.fsf_status_qual; - struct scsi_cmnd *scsi_cmnd; - struct zfcp_port *port; - struct zfcp_unit *unit; - struct zfcp_send_els *send_els; - struct zfcp_dbf_hba_record *rec = &dbf->hba_buf; - struct zfcp_dbf_hba_record_response *response = &rec->u.response; + struct zfcp_dbf *dbf = req->adapter->dbf; + struct fsf_qtcb_prefix *q_pref = &req->qtcb->prefix; + struct fsf_qtcb_header *q_head = &req->qtcb->header; + struct zfcp_dbf_hba *rec = &dbf->hba_buf; unsigned long flags; spin_lock_irqsave(&dbf->hba_lock, flags); memset(rec, 0, sizeof(*rec)); - strncpy(rec->tag, "resp", ZFCP_DBF_TAG_SIZE); - strncpy(rec->tag2, tag2, ZFCP_DBF_TAG_SIZE); - - response->fsf_command = fsf_req->fsf_command; - response->fsf_reqid = fsf_req->req_id; - response->fsf_seqno = fsf_req->seq_no; - response->fsf_issued = fsf_req->issued; - response->fsf_prot_status = qtcb->prefix.prot_status; - response->fsf_status = qtcb->header.fsf_status; - memcpy(response->fsf_prot_status_qual, - prot_status_qual, FSF_PROT_STATUS_QUAL_SIZE); - memcpy(response->fsf_status_qual, - fsf_status_qual, FSF_STATUS_QUALIFIER_SIZE); - response->fsf_req_status = fsf_req->status; - response->sbal_first = fsf_req->qdio_req.sbal_first; - response->sbal_last = fsf_req->qdio_req.sbal_last; - response->sbal_response = fsf_req->qdio_req.sbal_response; - response->pool = fsf_req->pool != NULL; - response->erp_action = (unsigned long)fsf_req->erp_action; - - switch (fsf_req->fsf_command) { - case FSF_QTCB_FCP_CMND: - if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) - break; - scsi_cmnd = (struct scsi_cmnd *)fsf_req->data; - if (scsi_cmnd) { - response->u.fcp.cmnd = (unsigned long)scsi_cmnd; - response->u.fcp.data_dir = - qtcb->bottom.io.data_direction; - } - break; - - case FSF_QTCB_OPEN_PORT_WITH_DID: - case FSF_QTCB_CLOSE_PORT: - case FSF_QTCB_CLOSE_PHYSICAL_PORT: - port = (struct zfcp_port *)fsf_req->data; - response->u.port.wwpn = port->wwpn; - response->u.port.d_id = port->d_id; - response->u.port.port_handle = qtcb->header.port_handle; - break; - - case FSF_QTCB_OPEN_LUN: - case FSF_QTCB_CLOSE_LUN: - unit = (struct zfcp_unit *)fsf_req->data; - port = unit->port; - response->u.unit.wwpn = port->wwpn; - response->u.unit.fcp_lun = unit->fcp_lun; - response->u.unit.port_handle = qtcb->header.port_handle; - response->u.unit.lun_handle = qtcb->header.lun_handle; - break; - - case FSF_QTCB_SEND_ELS: - send_els = (struct zfcp_send_els *)fsf_req->data; - response->u.els.d_id = ntoh24(qtcb->bottom.support.d_id); - break; - - case FSF_QTCB_ABORT_FCP_CMND: - case FSF_QTCB_SEND_GENERIC: - case FSF_QTCB_EXCHANGE_CONFIG_DATA: - case FSF_QTCB_EXCHANGE_PORT_DATA: - case FSF_QTCB_DOWNLOAD_CONTROL_FILE: - case FSF_QTCB_UPLOAD_CONTROL_FILE: - break; - } - - debug_event(dbf->hba, level, rec, sizeof(*rec)); - /* have fcp channel microcode fixed to use as little as possible */ - if (fsf_req->fsf_command != FSF_QTCB_FCP_CMND) { - /* adjust length skipping trailing zeros */ - char *buf = (char *)qtcb + qtcb->header.log_start; - int len = qtcb->header.log_length; - for (; len && !buf[len - 1]; len--); - zfcp_dbf_hexdump(dbf->hba, rec, sizeof(*rec), level, buf, - len); + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->id = ZFCP_DBF_HBA_RES; + rec->fsf_req_id = req->req_id; + rec->fsf_req_status = req->status; + rec->fsf_cmd = req->fsf_command; + rec->fsf_seq_no = req->seq_no; + rec->u.res.req_issued = req->issued; + rec->u.res.prot_status = q_pref->prot_status; + rec->u.res.fsf_status = q_head->fsf_status; + + memcpy(rec->u.res.prot_status_qual, &q_pref->prot_status_qual, + FSF_PROT_STATUS_QUAL_SIZE); + memcpy(rec->u.res.fsf_status_qual, &q_head->fsf_status_qual, + FSF_STATUS_QUALIFIER_SIZE); + + if (req->fsf_command != FSF_QTCB_FCP_CMND) { + rec->pl_len = q_head->log_length; + zfcp_dbf_pl_write(dbf, (char *)q_pref + q_head->log_start, + rec->pl_len, "fsf_res", req->req_id); } - spin_unlock_irqrestore(&dbf->hba_lock, flags); -} - -void _zfcp_dbf_hba_fsf_unsol(const char *tag, int level, struct zfcp_dbf *dbf, - struct fsf_status_read_buffer *status_buffer) -{ - struct zfcp_dbf_hba_record *rec = &dbf->hba_buf; - unsigned long flags; - - spin_lock_irqsave(&dbf->hba_lock, flags); - memset(rec, 0, sizeof(*rec)); - strncpy(rec->tag, "stat", ZFCP_DBF_TAG_SIZE); - strncpy(rec->tag2, tag, ZFCP_DBF_TAG_SIZE); - - rec->u.status.failed = atomic_read(&dbf->adapter->stat_miss); - if (status_buffer != NULL) { - rec->u.status.status_type = status_buffer->status_type; - rec->u.status.status_subtype = status_buffer->status_subtype; - memcpy(&rec->u.status.queue_designator, - &status_buffer->queue_designator, - sizeof(struct fsf_queue_designator)); - - switch (status_buffer->status_type) { - case FSF_STATUS_READ_SENSE_DATA_AVAIL: - rec->u.status.payload_size = - ZFCP_DBF_UNSOL_PAYLOAD_SENSE_DATA_AVAIL; - break; - - case FSF_STATUS_READ_BIT_ERROR_THRESHOLD: - rec->u.status.payload_size = - ZFCP_DBF_UNSOL_PAYLOAD_BIT_ERROR_THRESHOLD; - break; - - case FSF_STATUS_READ_LINK_DOWN: - switch (status_buffer->status_subtype) { - case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK: - case FSF_STATUS_READ_SUB_FDISC_FAILED: - rec->u.status.payload_size = - sizeof(struct fsf_link_down_info); - } - break; - - case FSF_STATUS_READ_FEATURE_UPDATE_ALERT: - rec->u.status.payload_size = - ZFCP_DBF_UNSOL_PAYLOAD_FEATURE_UPDATE_ALERT; - break; - } - memcpy(&rec->u.status.payload, - &status_buffer->payload, rec->u.status.payload_size); - } - - debug_event(dbf->hba, level, rec, sizeof(*rec)); + debug_event(dbf->hba, 1, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->hba_lock, flags); } /** - * zfcp_dbf_hba_qdio - trace event for QDIO related failure - * @qdio: qdio structure affected by this QDIO related event - * @qdio_error: as passed by qdio module - * @sbal_index: first buffer with error condition, as passed by qdio module - * @sbal_count: number of buffers affected, as passed by qdio module + * zfcp_dbf_hba_fsf_uss - trace event for an unsolicited status buffer + * @tag: tag indicating which kind of unsolicited status has been received + * @req: request providing the unsolicited status */ -void zfcp_dbf_hba_qdio(struct zfcp_dbf *dbf, unsigned int qdio_error, - int sbal_index, int sbal_count) +void zfcp_dbf_hba_fsf_uss(char *tag, struct zfcp_fsf_req *req) { - struct zfcp_dbf_hba_record *r = &dbf->hba_buf; + struct zfcp_dbf *dbf = req->adapter->dbf; + struct fsf_status_read_buffer *srb = req->data; + struct zfcp_dbf_hba *rec = &dbf->hba_buf; unsigned long flags; spin_lock_irqsave(&dbf->hba_lock, flags); - memset(r, 0, sizeof(*r)); - strncpy(r->tag, "qdio", ZFCP_DBF_TAG_SIZE); - r->u.qdio.qdio_error = qdio_error; - r->u.qdio.sbal_index = sbal_index; - r->u.qdio.sbal_count = sbal_count; - debug_event(dbf->hba, 0, r, sizeof(*r)); + memset(rec, 0, sizeof(*rec)); + + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->id = ZFCP_DBF_HBA_USS; + rec->fsf_req_id = req->req_id; + rec->fsf_req_status = req->status; + rec->fsf_cmd = req->fsf_command; + + if (!srb) + goto log; + + rec->u.uss.status_type = srb->status_type; + rec->u.uss.status_subtype = srb->status_subtype; + rec->u.uss.d_id = ntoh24(srb->d_id); + rec->u.uss.lun = srb->fcp_lun; + memcpy(&rec->u.uss.queue_designator, &srb->queue_designator, + sizeof(rec->u.uss.queue_designator)); + + /* status read buffer payload length */ + rec->pl_len = (!srb->length) ? 0 : srb->length - + offsetof(struct fsf_status_read_buffer, payload); + + if (rec->pl_len) + zfcp_dbf_pl_write(dbf, srb->payload.data, rec->pl_len, + "fsf_uss", req->req_id); +log: + debug_event(dbf->hba, 2, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->hba_lock, flags); } /** - * zfcp_dbf_hba_berr - trace event for bit error threshold - * @dbf: dbf structure affected by this QDIO related event - * @req: fsf request + * zfcp_dbf_hba_bit_err - trace event for bit error conditions + * @tag: tag indicating which kind of unsolicited status has been received + * @req: request which caused the bit_error condition */ -void zfcp_dbf_hba_berr(struct zfcp_dbf *dbf, struct zfcp_fsf_req *req) +void zfcp_dbf_hba_bit_err(char *tag, struct zfcp_fsf_req *req) { - struct zfcp_dbf_hba_record *r = &dbf->hba_buf; + struct zfcp_dbf *dbf = req->adapter->dbf; + struct zfcp_dbf_hba *rec = &dbf->hba_buf; struct fsf_status_read_buffer *sr_buf = req->data; - struct fsf_bit_error_payload *err = &sr_buf->payload.bit_error; unsigned long flags; spin_lock_irqsave(&dbf->hba_lock, flags); - memset(r, 0, sizeof(*r)); - strncpy(r->tag, "berr", ZFCP_DBF_TAG_SIZE); - memcpy(&r->u.berr, err, sizeof(struct fsf_bit_error_payload)); - debug_event(dbf->hba, 0, r, sizeof(*r)); - spin_unlock_irqrestore(&dbf->hba_lock, flags); -} -static void zfcp_dbf_hba_view_response(char **p, - struct zfcp_dbf_hba_record_response *r) -{ - struct timespec t; - - zfcp_dbf_out(p, "fsf_command", "0x%08x", r->fsf_command); - zfcp_dbf_out(p, "fsf_reqid", "0x%0Lx", r->fsf_reqid); - zfcp_dbf_out(p, "fsf_seqno", "0x%08x", r->fsf_seqno); - stck_to_timespec(r->fsf_issued, &t); - zfcp_dbf_out(p, "fsf_issued", "%011lu:%06lu", t.tv_sec, t.tv_nsec); - zfcp_dbf_out(p, "fsf_prot_status", "0x%08x", r->fsf_prot_status); - zfcp_dbf_out(p, "fsf_status", "0x%08x", r->fsf_status); - zfcp_dbf_outd(p, "fsf_prot_status_qual", r->fsf_prot_status_qual, - FSF_PROT_STATUS_QUAL_SIZE, 0, FSF_PROT_STATUS_QUAL_SIZE); - zfcp_dbf_outd(p, "fsf_status_qual", r->fsf_status_qual, - FSF_STATUS_QUALIFIER_SIZE, 0, FSF_STATUS_QUALIFIER_SIZE); - zfcp_dbf_out(p, "fsf_req_status", "0x%08x", r->fsf_req_status); - zfcp_dbf_out(p, "sbal_first", "0x%02x", r->sbal_first); - zfcp_dbf_out(p, "sbal_last", "0x%02x", r->sbal_last); - zfcp_dbf_out(p, "sbal_response", "0x%02x", r->sbal_response); - zfcp_dbf_out(p, "pool", "0x%02x", r->pool); - - switch (r->fsf_command) { - case FSF_QTCB_FCP_CMND: - if (r->fsf_req_status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) - break; - zfcp_dbf_out(p, "data_direction", "0x%04x", r->u.fcp.data_dir); - zfcp_dbf_out(p, "scsi_cmnd", "0x%0Lx", r->u.fcp.cmnd); - *p += sprintf(*p, "\n"); - break; - - case FSF_QTCB_OPEN_PORT_WITH_DID: - case FSF_QTCB_CLOSE_PORT: - case FSF_QTCB_CLOSE_PHYSICAL_PORT: - zfcp_dbf_out(p, "wwpn", "0x%016Lx", r->u.port.wwpn); - zfcp_dbf_out(p, "d_id", "0x%06x", r->u.port.d_id); - zfcp_dbf_out(p, "port_handle", "0x%08x", r->u.port.port_handle); - break; - - case FSF_QTCB_OPEN_LUN: - case FSF_QTCB_CLOSE_LUN: - zfcp_dbf_out(p, "wwpn", "0x%016Lx", r->u.unit.wwpn); - zfcp_dbf_out(p, "fcp_lun", "0x%016Lx", r->u.unit.fcp_lun); - zfcp_dbf_out(p, "port_handle", "0x%08x", r->u.unit.port_handle); - zfcp_dbf_out(p, "lun_handle", "0x%08x", r->u.unit.lun_handle); - break; - - case FSF_QTCB_SEND_ELS: - zfcp_dbf_out(p, "d_id", "0x%06x", r->u.els.d_id); - break; - - case FSF_QTCB_ABORT_FCP_CMND: - case FSF_QTCB_SEND_GENERIC: - case FSF_QTCB_EXCHANGE_CONFIG_DATA: - case FSF_QTCB_EXCHANGE_PORT_DATA: - case FSF_QTCB_DOWNLOAD_CONTROL_FILE: - case FSF_QTCB_UPLOAD_CONTROL_FILE: - break; - } -} - -static void zfcp_dbf_hba_view_status(char **p, - struct zfcp_dbf_hba_record_status *r) -{ - zfcp_dbf_out(p, "failed", "0x%02x", r->failed); - zfcp_dbf_out(p, "status_type", "0x%08x", r->status_type); - zfcp_dbf_out(p, "status_subtype", "0x%08x", r->status_subtype); - zfcp_dbf_outd(p, "queue_designator", (char *)&r->queue_designator, - sizeof(struct fsf_queue_designator), 0, - sizeof(struct fsf_queue_designator)); - zfcp_dbf_outd(p, "payload", (char *)&r->payload, r->payload_size, 0, - r->payload_size); -} - -static void zfcp_dbf_hba_view_qdio(char **p, struct zfcp_dbf_hba_record_qdio *r) -{ - zfcp_dbf_out(p, "qdio_error", "0x%08x", r->qdio_error); - zfcp_dbf_out(p, "sbal_index", "0x%02x", r->sbal_index); - zfcp_dbf_out(p, "sbal_count", "0x%02x", r->sbal_count); -} + memset(rec, 0, sizeof(*rec)); -static void zfcp_dbf_hba_view_berr(char **p, struct fsf_bit_error_payload *r) -{ - zfcp_dbf_out(p, "link_failures", "%d", r->link_failure_error_count); - zfcp_dbf_out(p, "loss_of_sync_err", "%d", r->loss_of_sync_error_count); - zfcp_dbf_out(p, "loss_of_sig_err", "%d", r->loss_of_signal_error_count); - zfcp_dbf_out(p, "prim_seq_err", "%d", - r->primitive_sequence_error_count); - zfcp_dbf_out(p, "inval_trans_word_err", "%d", - r->invalid_transmission_word_error_count); - zfcp_dbf_out(p, "CRC_errors", "%d", r->crc_error_count); - zfcp_dbf_out(p, "prim_seq_event_to", "%d", - r->primitive_sequence_event_timeout_count); - zfcp_dbf_out(p, "elast_buf_overrun_err", "%d", - r->elastic_buffer_overrun_error_count); - zfcp_dbf_out(p, "adv_rec_buf2buf_cred", "%d", - r->advertised_receive_b2b_credit); - zfcp_dbf_out(p, "curr_rec_buf2buf_cred", "%d", - r->current_receive_b2b_credit); - zfcp_dbf_out(p, "adv_trans_buf2buf_cred", "%d", - r->advertised_transmit_b2b_credit); - zfcp_dbf_out(p, "curr_trans_buf2buf_cred", "%d", - r->current_transmit_b2b_credit); -} + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->id = ZFCP_DBF_HBA_BIT; + rec->fsf_req_id = req->req_id; + rec->fsf_req_status = req->status; + rec->fsf_cmd = req->fsf_command; + memcpy(&rec->u.be, &sr_buf->payload.bit_error, + sizeof(struct fsf_bit_error_payload)); -static int zfcp_dbf_hba_view_format(debug_info_t *id, struct debug_view *view, - char *out_buf, const char *in_buf) -{ - struct zfcp_dbf_hba_record *r = (struct zfcp_dbf_hba_record *)in_buf; - char *p = out_buf; - - if (strncmp(r->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0) - return 0; - - zfcp_dbf_tag(&p, "tag", r->tag); - if (isalpha(r->tag2[0])) - zfcp_dbf_tag(&p, "tag2", r->tag2); - - if (strncmp(r->tag, "resp", ZFCP_DBF_TAG_SIZE) == 0) - zfcp_dbf_hba_view_response(&p, &r->u.response); - else if (strncmp(r->tag, "stat", ZFCP_DBF_TAG_SIZE) == 0) - zfcp_dbf_hba_view_status(&p, &r->u.status); - else if (strncmp(r->tag, "qdio", ZFCP_DBF_TAG_SIZE) == 0) - zfcp_dbf_hba_view_qdio(&p, &r->u.qdio); - else if (strncmp(r->tag, "berr", ZFCP_DBF_TAG_SIZE) == 0) - zfcp_dbf_hba_view_berr(&p, &r->u.berr); - - if (strncmp(r->tag, "resp", ZFCP_DBF_TAG_SIZE) != 0) - p += sprintf(p, "\n"); - return p - out_buf; + debug_event(dbf->hba, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->hba_lock, flags); } -static struct debug_view zfcp_dbf_hba_view = { - .name = "structured", - .header_proc = zfcp_dbf_view_header, - .format_proc = zfcp_dbf_hba_view_format, -}; - -static const char *zfcp_dbf_rec_tags[] = { - [ZFCP_REC_DBF_ID_THREAD] = "thread", - [ZFCP_REC_DBF_ID_TARGET] = "target", - [ZFCP_REC_DBF_ID_TRIGGER] = "trigger", - [ZFCP_REC_DBF_ID_ACTION] = "action", -}; - -static int zfcp_dbf_rec_view_format(debug_info_t *id, struct debug_view *view, - char *buf, const char *_rec) +static void zfcp_dbf_set_common(struct zfcp_dbf_rec *rec, + struct zfcp_adapter *adapter, + struct zfcp_port *port, + struct scsi_device *sdev) { - struct zfcp_dbf_rec_record *r = (struct zfcp_dbf_rec_record *)_rec; - char *p = buf; - char hint[ZFCP_DBF_ID_SIZE + 1]; - - memcpy(hint, r->id2, ZFCP_DBF_ID_SIZE); - hint[ZFCP_DBF_ID_SIZE] = 0; - zfcp_dbf_outs(&p, "tag", zfcp_dbf_rec_tags[r->id]); - zfcp_dbf_outs(&p, "hint", hint); - switch (r->id) { - case ZFCP_REC_DBF_ID_THREAD: - zfcp_dbf_out(&p, "total", "%d", r->u.thread.total); - zfcp_dbf_out(&p, "ready", "%d", r->u.thread.ready); - zfcp_dbf_out(&p, "running", "%d", r->u.thread.running); - break; - case ZFCP_REC_DBF_ID_TARGET: - zfcp_dbf_out(&p, "reference", "0x%016Lx", r->u.target.ref); - zfcp_dbf_out(&p, "status", "0x%08x", r->u.target.status); - zfcp_dbf_out(&p, "erp_count", "%d", r->u.target.erp_count); - zfcp_dbf_out(&p, "d_id", "0x%06x", r->u.target.d_id); - zfcp_dbf_out(&p, "wwpn", "0x%016Lx", r->u.target.wwpn); - zfcp_dbf_out(&p, "fcp_lun", "0x%016Lx", r->u.target.fcp_lun); - break; - case ZFCP_REC_DBF_ID_TRIGGER: - zfcp_dbf_out(&p, "reference", "0x%016Lx", r->u.trigger.ref); - zfcp_dbf_out(&p, "erp_action", "0x%016Lx", r->u.trigger.action); - zfcp_dbf_out(&p, "requested", "%d", r->u.trigger.want); - zfcp_dbf_out(&p, "executed", "%d", r->u.trigger.need); - zfcp_dbf_out(&p, "wwpn", "0x%016Lx", r->u.trigger.wwpn); - zfcp_dbf_out(&p, "fcp_lun", "0x%016Lx", r->u.trigger.fcp_lun); - zfcp_dbf_out(&p, "adapter_status", "0x%08x", r->u.trigger.as); - zfcp_dbf_out(&p, "port_status", "0x%08x", r->u.trigger.ps); - zfcp_dbf_out(&p, "lun_status", "0x%08x", r->u.trigger.ls); - break; - case ZFCP_REC_DBF_ID_ACTION: - zfcp_dbf_out(&p, "erp_action", "0x%016Lx", r->u.action.action); - zfcp_dbf_out(&p, "fsf_req", "0x%016Lx", r->u.action.fsf_req); - zfcp_dbf_out(&p, "status", "0x%08Lx", r->u.action.status); - zfcp_dbf_out(&p, "step", "0x%08Lx", r->u.action.step); - break; + rec->adapter_status = atomic_read(&adapter->status); + if (port) { + rec->port_status = atomic_read(&port->status); + rec->wwpn = port->wwpn; + rec->d_id = port->d_id; + } + if (sdev) { + rec->lun_status = atomic_read(&sdev_to_zfcp(sdev)->status); + rec->lun = zfcp_scsi_dev_lun(sdev); } - p += sprintf(p, "\n"); - return p - buf; } -static struct debug_view zfcp_dbf_rec_view = { - .name = "structured", - .header_proc = zfcp_dbf_view_header, - .format_proc = zfcp_dbf_rec_view_format, -}; - /** - * zfcp_dbf_rec_thread - trace event related to recovery thread operation - * @id2: identifier for event - * @dbf: reference to dbf structure - * This function assumes that the caller is holding erp_lock. + * zfcp_dbf_rec_trig - trace event related to triggered recovery + * @tag: identifier for event + * @adapter: adapter on which the erp_action should run + * @port: remote port involved in the erp_action + * @sdev: scsi device involved in the erp_action + * @want: wanted erp_action + * @need: required erp_action + * + * The adapter->erp_lock has to be held. */ -void zfcp_dbf_rec_thread(char *id2, struct zfcp_dbf *dbf) +void zfcp_dbf_rec_trig(char *tag, struct zfcp_adapter *adapter, + struct zfcp_port *port, struct scsi_device *sdev, + u8 want, u8 need) { - struct zfcp_adapter *adapter = dbf->adapter; - struct zfcp_dbf_rec_record *r = &dbf->rec_buf; - unsigned long flags = 0; + struct zfcp_dbf *dbf = adapter->dbf; + struct zfcp_dbf_rec *rec = &dbf->rec_buf; struct list_head *entry; - unsigned ready = 0, running = 0, total; - - list_for_each(entry, &adapter->erp_ready_head) - ready++; - list_for_each(entry, &adapter->erp_running_head) - running++; - total = adapter->erp_total_count; - - spin_lock_irqsave(&dbf->rec_lock, flags); - memset(r, 0, sizeof(*r)); - r->id = ZFCP_REC_DBF_ID_THREAD; - memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE); - r->u.thread.total = total; - r->u.thread.ready = ready; - r->u.thread.running = running; - debug_event(dbf->rec, 6, r, sizeof(*r)); - spin_unlock_irqrestore(&dbf->rec_lock, flags); -} - -/** - * zfcp_dbf_rec_thread - trace event related to recovery thread operation - * @id2: identifier for event - * @adapter: adapter - * This function assumes that the caller does not hold erp_lock. - */ -void zfcp_dbf_rec_thread_lock(char *id2, struct zfcp_dbf *dbf) -{ - struct zfcp_adapter *adapter = dbf->adapter; - unsigned long flags; - - read_lock_irqsave(&adapter->erp_lock, flags); - zfcp_dbf_rec_thread(id2, dbf); - read_unlock_irqrestore(&adapter->erp_lock, flags); -} - -static void zfcp_dbf_rec_target(char *id2, void *ref, struct zfcp_dbf *dbf, - atomic_t *status, atomic_t *erp_count, u64 wwpn, - u32 d_id, u64 fcp_lun) -{ - struct zfcp_dbf_rec_record *r = &dbf->rec_buf; unsigned long flags; spin_lock_irqsave(&dbf->rec_lock, flags); - memset(r, 0, sizeof(*r)); - r->id = ZFCP_REC_DBF_ID_TARGET; - memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE); - r->u.target.ref = (unsigned long)ref; - r->u.target.status = atomic_read(status); - r->u.target.wwpn = wwpn; - r->u.target.d_id = d_id; - r->u.target.fcp_lun = fcp_lun; - r->u.target.erp_count = atomic_read(erp_count); - debug_event(dbf->rec, 3, r, sizeof(*r)); - spin_unlock_irqrestore(&dbf->rec_lock, flags); -} - -/** - * zfcp_dbf_rec_adapter - trace event for adapter state change - * @id: identifier for trigger of state change - * @ref: additional reference (e.g. request) - * @dbf: reference to dbf structure - */ -void zfcp_dbf_rec_adapter(char *id, void *ref, struct zfcp_dbf *dbf) -{ - struct zfcp_adapter *adapter = dbf->adapter; - - zfcp_dbf_rec_target(id, ref, dbf, &adapter->status, - &adapter->erp_counter, 0, 0, - ZFCP_DBF_INVALID_LUN); -} - -/** - * zfcp_dbf_rec_port - trace event for port state change - * @id: identifier for trigger of state change - * @ref: additional reference (e.g. request) - * @port: port - */ -void zfcp_dbf_rec_port(char *id, void *ref, struct zfcp_port *port) -{ - struct zfcp_dbf *dbf = port->adapter->dbf; + memset(rec, 0, sizeof(*rec)); - zfcp_dbf_rec_target(id, ref, dbf, &port->status, - &port->erp_counter, port->wwpn, port->d_id, - ZFCP_DBF_INVALID_LUN); -} + rec->id = ZFCP_DBF_REC_TRIG; + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + zfcp_dbf_set_common(rec, adapter, port, sdev); -/** - * zfcp_dbf_rec_lun - trace event for LUN state change - * @id: identifier for trigger of state change - * @ref: additional reference (e.g. request) - * @sdev: SCSI device - */ -void zfcp_dbf_rec_lun(char *id, void *ref, struct scsi_device *sdev) -{ - struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); - struct zfcp_port *port = zfcp_sdev->port; - struct zfcp_dbf *dbf = port->adapter->dbf; + list_for_each(entry, &adapter->erp_ready_head) + rec->u.trig.ready++; - zfcp_dbf_rec_target(id, ref, dbf, &zfcp_sdev->status, - &zfcp_sdev->erp_counter, port->wwpn, port->d_id, - zfcp_scsi_dev_lun(sdev)); -} + list_for_each(entry, &adapter->erp_running_head) + rec->u.trig.running++; -/** - * zfcp_dbf_rec_trigger - trace event for triggered error recovery - * @id2: identifier for error recovery trigger - * @ref: additional reference (e.g. request) - * @want: originally requested error recovery action - * @need: error recovery action actually initiated - * @action: address of error recovery action struct - * @adapter: adapter - * @port: port - * @sdev: SCSI device - */ -void zfcp_dbf_rec_trigger(char *id2, void *ref, u8 want, u8 need, void *action, - struct zfcp_adapter *adapter, struct zfcp_port *port, - struct scsi_device *sdev) -{ - struct zfcp_dbf *dbf = adapter->dbf; - struct zfcp_dbf_rec_record *r = &dbf->rec_buf; - unsigned long flags; + rec->u.trig.want = want; + rec->u.trig.need = need; - spin_lock_irqsave(&dbf->rec_lock, flags); - memset(r, 0, sizeof(*r)); - r->id = ZFCP_REC_DBF_ID_TRIGGER; - memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE); - r->u.trigger.ref = (unsigned long)ref; - r->u.trigger.want = want; - r->u.trigger.need = need; - r->u.trigger.action = (unsigned long)action; - r->u.trigger.as = atomic_read(&adapter->status); - if (port) { - r->u.trigger.ps = atomic_read(&port->status); - r->u.trigger.wwpn = port->wwpn; - } - if (sdev) - r->u.trigger.ls = atomic_read(&sdev_to_zfcp(sdev)->status); - r->u.trigger.fcp_lun = sdev ? zfcp_scsi_dev_lun(sdev) : - ZFCP_DBF_INVALID_LUN; - debug_event(dbf->rec, action ? 1 : 4, r, sizeof(*r)); + debug_event(dbf->rec, 1, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->rec_lock, flags); } + /** - * zfcp_dbf_rec_action - trace event showing progress of recovery action - * @id2: identifier - * @erp_action: error recovery action struct pointer + * zfcp_dbf_rec_run - trace event related to running recovery + * @tag: identifier for event + * @erp: erp_action running */ -void zfcp_dbf_rec_action(char *id2, struct zfcp_erp_action *erp_action) +void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp) { - struct zfcp_dbf *dbf = erp_action->adapter->dbf; - struct zfcp_dbf_rec_record *r = &dbf->rec_buf; + struct zfcp_dbf *dbf = erp->adapter->dbf; + struct zfcp_dbf_rec *rec = &dbf->rec_buf; unsigned long flags; spin_lock_irqsave(&dbf->rec_lock, flags); - memset(r, 0, sizeof(*r)); - r->id = ZFCP_REC_DBF_ID_ACTION; - memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE); - r->u.action.action = (unsigned long)erp_action; - r->u.action.status = erp_action->status; - r->u.action.step = erp_action->step; - r->u.action.fsf_req = erp_action->fsf_req_id; - debug_event(dbf->rec, 5, r, sizeof(*r)); - spin_unlock_irqrestore(&dbf->rec_lock, flags); -} + memset(rec, 0, sizeof(*rec)); -/** - * zfcp_dbf_san_ct_request - trace event for issued CT request - * @fsf_req: request containing issued CT data - * @d_id: destination id where ct request is sent to - */ -void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req, u32 d_id) -{ - struct zfcp_fsf_ct_els *ct = (struct zfcp_fsf_ct_els *)fsf_req->data; - struct zfcp_adapter *adapter = fsf_req->adapter; - struct zfcp_dbf *dbf = adapter->dbf; - struct fc_ct_hdr *hdr = sg_virt(ct->req); - struct zfcp_dbf_san_record *r = &dbf->san_buf; - struct zfcp_dbf_san_record_ct_request *oct = &r->u.ct_req; - int level = 3; - unsigned long flags; + rec->id = ZFCP_DBF_REC_RUN; + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + zfcp_dbf_set_common(rec, erp->adapter, erp->port, erp->sdev); - spin_lock_irqsave(&dbf->san_lock, flags); - memset(r, 0, sizeof(*r)); - strncpy(r->tag, "octc", ZFCP_DBF_TAG_SIZE); - r->fsf_reqid = fsf_req->req_id; - r->fsf_seqno = fsf_req->seq_no; - oct->d_id = d_id; - oct->cmd_req_code = hdr->ct_cmd; - oct->revision = hdr->ct_rev; - oct->gs_type = hdr->ct_fs_type; - oct->gs_subtype = hdr->ct_fs_subtype; - oct->options = hdr->ct_options; - oct->max_res_size = hdr->ct_mr_size; - oct->len = min((int)ct->req->length - (int)sizeof(struct fc_ct_hdr), - ZFCP_DBF_SAN_MAX_PAYLOAD); - debug_event(dbf->san, level, r, sizeof(*r)); - zfcp_dbf_hexdump(dbf->san, r, sizeof(*r), level, - (void *)hdr + sizeof(struct fc_ct_hdr), oct->len); - spin_unlock_irqrestore(&dbf->san_lock, flags); -} + rec->u.run.fsf_req_id = erp->fsf_req_id; + rec->u.run.rec_status = erp->status; + rec->u.run.rec_step = erp->step; + rec->u.run.rec_action = erp->action; -/** - * zfcp_dbf_san_ct_response - trace event for completion of CT request - * @fsf_req: request containing CT response - */ -void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *fsf_req) -{ - struct zfcp_fsf_ct_els *ct = (struct zfcp_fsf_ct_els *)fsf_req->data; - struct zfcp_adapter *adapter = fsf_req->adapter; - struct fc_ct_hdr *hdr = sg_virt(ct->resp); - struct zfcp_dbf *dbf = adapter->dbf; - struct zfcp_dbf_san_record *r = &dbf->san_buf; - struct zfcp_dbf_san_record_ct_response *rct = &r->u.ct_resp; - int level = 3; - unsigned long flags; + if (erp->sdev) + rec->u.run.rec_count = + atomic_read(&sdev_to_zfcp(erp->sdev)->erp_counter); + else if (erp->port) + rec->u.run.rec_count = atomic_read(&erp->port->erp_counter); + else + rec->u.run.rec_count = atomic_read(&erp->adapter->erp_counter); - spin_lock_irqsave(&dbf->san_lock, flags); - memset(r, 0, sizeof(*r)); - strncpy(r->tag, "rctc", ZFCP_DBF_TAG_SIZE); - r->fsf_reqid = fsf_req->req_id; - r->fsf_seqno = fsf_req->seq_no; - rct->cmd_rsp_code = hdr->ct_cmd; - rct->revision = hdr->ct_rev; - rct->reason_code = hdr->ct_reason; - rct->expl = hdr->ct_explan; - rct->vendor_unique = hdr->ct_vendor; - rct->max_res_size = hdr->ct_mr_size; - rct->len = min((int)ct->resp->length - (int)sizeof(struct fc_ct_hdr), - ZFCP_DBF_SAN_MAX_PAYLOAD); - debug_event(dbf->san, level, r, sizeof(*r)); - zfcp_dbf_hexdump(dbf->san, r, sizeof(*r), level, - (void *)hdr + sizeof(struct fc_ct_hdr), rct->len); - spin_unlock_irqrestore(&dbf->san_lock, flags); + debug_event(dbf->rec, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->rec_lock, flags); } -static void zfcp_dbf_san_els(const char *tag, int level, - struct zfcp_fsf_req *fsf_req, u32 d_id, - void *buffer, int buflen) +static inline +void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len, + u64 req_id, u32 d_id) { - struct zfcp_adapter *adapter = fsf_req->adapter; - struct zfcp_dbf *dbf = adapter->dbf; - struct zfcp_dbf_san_record *rec = &dbf->san_buf; + struct zfcp_dbf_san *rec = &dbf->san_buf; + u16 rec_len; unsigned long flags; spin_lock_irqsave(&dbf->san_lock, flags); memset(rec, 0, sizeof(*rec)); - strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE); - rec->fsf_reqid = fsf_req->req_id; - rec->fsf_seqno = fsf_req->seq_no; - rec->u.els.d_id = d_id; - debug_event(dbf->san, level, rec, sizeof(*rec)); - zfcp_dbf_hexdump(dbf->san, rec, sizeof(*rec), level, - buffer, min(buflen, ZFCP_DBF_SAN_MAX_PAYLOAD)); + + rec->id = id; + rec->fsf_req_id = req_id; + rec->d_id = d_id; + rec_len = min(len, (u16)ZFCP_DBF_SAN_MAX_PAYLOAD); + memcpy(rec->payload, data, rec_len); + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + + debug_event(dbf->san, 1, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->san_lock, flags); } /** - * zfcp_dbf_san_els_request - trace event for issued ELS - * @fsf_req: request containing issued ELS + * zfcp_dbf_san_req - trace event for issued SAN request + * @tag: indentifier for event + * @fsf_req: request containing issued CT data + * d_id: destination ID */ -void zfcp_dbf_san_els_request(struct zfcp_fsf_req *fsf_req) +void zfcp_dbf_san_req(char *tag, struct zfcp_fsf_req *fsf, u32 d_id) { - struct zfcp_fsf_ct_els *els = (struct zfcp_fsf_ct_els *)fsf_req->data; - u32 d_id = ntoh24(fsf_req->qtcb->bottom.support.d_id); + struct zfcp_dbf *dbf = fsf->adapter->dbf; + struct zfcp_fsf_ct_els *ct_els = fsf->data; + u16 length; - zfcp_dbf_san_els("oels", 2, fsf_req, d_id, - sg_virt(els->req), els->req->length); + length = (u16)(ct_els->req->length + FC_CT_HDR_LEN); + zfcp_dbf_san(tag, dbf, sg_virt(ct_els->req), ZFCP_DBF_SAN_REQ, length, + fsf->req_id, d_id); } /** - * zfcp_dbf_san_els_response - trace event for completed ELS - * @fsf_req: request containing ELS response + * zfcp_dbf_san_res - trace event for received SAN request + * @tag: indentifier for event + * @fsf_req: request containing issued CT data */ -void zfcp_dbf_san_els_response(struct zfcp_fsf_req *fsf_req) +void zfcp_dbf_san_res(char *tag, struct zfcp_fsf_req *fsf) { - struct zfcp_fsf_ct_els *els = (struct zfcp_fsf_ct_els *)fsf_req->data; - u32 d_id = ntoh24(fsf_req->qtcb->bottom.support.d_id); + struct zfcp_dbf *dbf = fsf->adapter->dbf; + struct zfcp_fsf_ct_els *ct_els = fsf->data; + u16 length; - zfcp_dbf_san_els("rels", 2, fsf_req, d_id, - sg_virt(els->resp), els->resp->length); + length = (u16)(ct_els->resp->length + FC_CT_HDR_LEN); + zfcp_dbf_san(tag, dbf, sg_virt(ct_els->resp), ZFCP_DBF_SAN_RES, length, + fsf->req_id, 0); } /** - * zfcp_dbf_san_incoming_els - trace event for incomig ELS - * @fsf_req: request containing unsolicited status buffer with incoming ELS + * zfcp_dbf_san_in_els - trace event for incoming ELS + * @tag: indentifier for event + * @fsf_req: request containing issued CT data */ -void zfcp_dbf_san_incoming_els(struct zfcp_fsf_req *fsf_req) +void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf) { - struct fsf_status_read_buffer *buf = - (struct fsf_status_read_buffer *)fsf_req->data; - int length = (int)buf->length - - (int)((void *)&buf->payload - (void *)buf); + struct zfcp_dbf *dbf = fsf->adapter->dbf; + struct fsf_status_read_buffer *srb = + (struct fsf_status_read_buffer *) fsf->data; + u16 length; - zfcp_dbf_san_els("iels", 1, fsf_req, ntoh24(buf->d_id), - (void *)buf->payload.data, length); -} - -static int zfcp_dbf_san_view_format(debug_info_t *id, struct debug_view *view, - char *out_buf, const char *in_buf) -{ - struct zfcp_dbf_san_record *r = (struct zfcp_dbf_san_record *)in_buf; - char *p = out_buf; - - if (strncmp(r->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0) - return 0; - - zfcp_dbf_tag(&p, "tag", r->tag); - zfcp_dbf_out(&p, "fsf_reqid", "0x%0Lx", r->fsf_reqid); - zfcp_dbf_out(&p, "fsf_seqno", "0x%08x", r->fsf_seqno); - - if (strncmp(r->tag, "octc", ZFCP_DBF_TAG_SIZE) == 0) { - struct zfcp_dbf_san_record_ct_request *ct = &r->u.ct_req; - zfcp_dbf_out(&p, "d_id", "0x%06x", ct->d_id); - zfcp_dbf_out(&p, "cmd_req_code", "0x%04x", ct->cmd_req_code); - zfcp_dbf_out(&p, "revision", "0x%02x", ct->revision); - zfcp_dbf_out(&p, "gs_type", "0x%02x", ct->gs_type); - zfcp_dbf_out(&p, "gs_subtype", "0x%02x", ct->gs_subtype); - zfcp_dbf_out(&p, "options", "0x%02x", ct->options); - zfcp_dbf_out(&p, "max_res_size", "0x%04x", ct->max_res_size); - } else if (strncmp(r->tag, "rctc", ZFCP_DBF_TAG_SIZE) == 0) { - struct zfcp_dbf_san_record_ct_response *ct = &r->u.ct_resp; - zfcp_dbf_out(&p, "cmd_rsp_code", "0x%04x", ct->cmd_rsp_code); - zfcp_dbf_out(&p, "revision", "0x%02x", ct->revision); - zfcp_dbf_out(&p, "reason_code", "0x%02x", ct->reason_code); - zfcp_dbf_out(&p, "reason_code_expl", "0x%02x", ct->expl); - zfcp_dbf_out(&p, "vendor_unique", "0x%02x", ct->vendor_unique); - zfcp_dbf_out(&p, "max_res_size", "0x%04x", ct->max_res_size); - } else if (strncmp(r->tag, "oels", ZFCP_DBF_TAG_SIZE) == 0 || - strncmp(r->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 || - strncmp(r->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) { - struct zfcp_dbf_san_record_els *els = &r->u.els; - zfcp_dbf_out(&p, "d_id", "0x%06x", els->d_id); - } - return p - out_buf; + length = (u16)(srb->length - + offsetof(struct fsf_status_read_buffer, payload)); + zfcp_dbf_san(tag, dbf, srb->payload.data, ZFCP_DBF_SAN_ELS, length, + fsf->req_id, ntoh24(srb->d_id)); } -static struct debug_view zfcp_dbf_san_view = { - .name = "structured", - .header_proc = zfcp_dbf_view_header, - .format_proc = zfcp_dbf_san_view_format, -}; - -void _zfcp_dbf_scsi(const char *tag, const char *tag2, int level, - struct zfcp_dbf *dbf, struct scsi_cmnd *scsi_cmnd, - struct zfcp_fsf_req *fsf_req, unsigned long old_req_id) +/** + * zfcp_dbf_scsi - trace event for scsi commands + * @tag: identifier for event + * @sc: pointer to struct scsi_cmnd + * @fsf: pointer to struct zfcp_fsf_req + */ +void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf) { - struct zfcp_dbf_scsi_record *rec = &dbf->scsi_buf; - struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)rec; - unsigned long flags; + struct zfcp_adapter *adapter = + (struct zfcp_adapter *) sc->device->host->hostdata[0]; + struct zfcp_dbf *dbf = adapter->dbf; + struct zfcp_dbf_scsi *rec = &dbf->scsi_buf; struct fcp_resp_with_ext *fcp_rsp; - struct fcp_resp_rsp_info *fcp_rsp_info = NULL; - char *fcp_sns_info = NULL; - int offset = 0, buflen = 0; + struct fcp_resp_rsp_info *fcp_rsp_info; + unsigned long flags; spin_lock_irqsave(&dbf->scsi_lock, flags); - do { - memset(rec, 0, sizeof(*rec)); - if (offset == 0) { - strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE); - strncpy(rec->tag2, tag2, ZFCP_DBF_TAG_SIZE); - if (scsi_cmnd != NULL) { - if (scsi_cmnd->device) { - rec->scsi_id = scsi_cmnd->device->id; - rec->scsi_lun = scsi_cmnd->device->lun; - } - rec->scsi_result = scsi_cmnd->result; - rec->scsi_cmnd = (unsigned long)scsi_cmnd; - memcpy(rec->scsi_opcode, scsi_cmnd->cmnd, - min((int)scsi_cmnd->cmd_len, - ZFCP_DBF_SCSI_OPCODE)); - rec->scsi_retries = scsi_cmnd->retries; - rec->scsi_allowed = scsi_cmnd->allowed; - } - if (fsf_req != NULL) { - fcp_rsp = (struct fcp_resp_with_ext *) - &(fsf_req->qtcb->bottom.io.fcp_rsp); - fcp_rsp_info = (struct fcp_resp_rsp_info *) - &fcp_rsp[1]; - fcp_sns_info = (char *) &fcp_rsp[1]; - if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL) - fcp_sns_info += fcp_rsp->ext.fr_sns_len; - - rec->rsp_validity = fcp_rsp->resp.fr_flags; - rec->rsp_scsi_status = fcp_rsp->resp.fr_status; - rec->rsp_resid = fcp_rsp->ext.fr_resid; - if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL) - rec->rsp_code = fcp_rsp_info->rsp_code; - if (fcp_rsp->resp.fr_flags & FCP_SNS_LEN_VAL) { - buflen = min(fcp_rsp->ext.fr_sns_len, - (u32)ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO); - rec->sns_info_len = buflen; - memcpy(rec->sns_info, fcp_sns_info, - min(buflen, - ZFCP_DBF_SCSI_FCP_SNS_INFO)); - offset += min(buflen, - ZFCP_DBF_SCSI_FCP_SNS_INFO); - } - - rec->fsf_reqid = fsf_req->req_id; - rec->fsf_seqno = fsf_req->seq_no; - rec->fsf_issued = fsf_req->issued; - } - rec->old_fsf_reqid = old_req_id; - } else { - strncpy(dump->tag, "dump", ZFCP_DBF_TAG_SIZE); - dump->total_size = buflen; - dump->offset = offset; - dump->size = min(buflen - offset, - (int)sizeof(struct - zfcp_dbf_scsi_record) - - (int)sizeof(struct zfcp_dbf_dump)); - memcpy(dump->data, fcp_sns_info + offset, dump->size); - offset += dump->size; - } - debug_event(dbf->scsi, level, rec, sizeof(*rec)); - } while (offset < buflen); - spin_unlock_irqrestore(&dbf->scsi_lock, flags); -} + memset(rec, 0, sizeof(*rec)); -static int zfcp_dbf_scsi_view_format(debug_info_t *id, struct debug_view *view, - char *out_buf, const char *in_buf) -{ - struct zfcp_dbf_scsi_record *r = (struct zfcp_dbf_scsi_record *)in_buf; - struct timespec t; - char *p = out_buf; - - if (strncmp(r->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0) - return 0; - - zfcp_dbf_tag(&p, "tag", r->tag); - zfcp_dbf_tag(&p, "tag2", r->tag2); - zfcp_dbf_out(&p, "scsi_id", "0x%08x", r->scsi_id); - zfcp_dbf_out(&p, "scsi_lun", "0x%08x", r->scsi_lun); - zfcp_dbf_out(&p, "scsi_result", "0x%08x", r->scsi_result); - zfcp_dbf_out(&p, "scsi_cmnd", "0x%0Lx", r->scsi_cmnd); - zfcp_dbf_outd(&p, "scsi_opcode", r->scsi_opcode, ZFCP_DBF_SCSI_OPCODE, - 0, ZFCP_DBF_SCSI_OPCODE); - zfcp_dbf_out(&p, "scsi_retries", "0x%02x", r->scsi_retries); - zfcp_dbf_out(&p, "scsi_allowed", "0x%02x", r->scsi_allowed); - if (strncmp(r->tag, "abrt", ZFCP_DBF_TAG_SIZE) == 0) - zfcp_dbf_out(&p, "old_fsf_reqid", "0x%0Lx", r->old_fsf_reqid); - zfcp_dbf_out(&p, "fsf_reqid", "0x%0Lx", r->fsf_reqid); - zfcp_dbf_out(&p, "fsf_seqno", "0x%08x", r->fsf_seqno); - stck_to_timespec(r->fsf_issued, &t); - zfcp_dbf_out(&p, "fsf_issued", "%011lu:%06lu", t.tv_sec, t.tv_nsec); - - if (strncmp(r->tag, "rslt", ZFCP_DBF_TAG_SIZE) == 0) { - zfcp_dbf_out(&p, "fcp_rsp_validity", "0x%02x", r->rsp_validity); - zfcp_dbf_out(&p, "fcp_rsp_scsi_status", "0x%02x", - r->rsp_scsi_status); - zfcp_dbf_out(&p, "fcp_rsp_resid", "0x%08x", r->rsp_resid); - zfcp_dbf_out(&p, "fcp_rsp_code", "0x%08x", r->rsp_code); - zfcp_dbf_out(&p, "fcp_sns_info_len", "0x%08x", r->sns_info_len); - zfcp_dbf_outd(&p, "fcp_sns_info", r->sns_info, - min((int)r->sns_info_len, - ZFCP_DBF_SCSI_FCP_SNS_INFO), 0, - r->sns_info_len); + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->id = ZFCP_DBF_SCSI_CMND; + rec->scsi_result = sc->result; + rec->scsi_retries = sc->retries; + rec->scsi_allowed = sc->allowed; + rec->scsi_id = sc->device->id; + rec->scsi_lun = sc->device->lun; + rec->host_scribble = (unsigned long)sc->host_scribble; + + memcpy(rec->scsi_opcode, sc->cmnd, + min((int)sc->cmd_len, ZFCP_DBF_SCSI_OPCODE)); + + if (fsf) { + rec->fsf_req_id = fsf->req_id; + fcp_rsp = (struct fcp_resp_with_ext *) + &(fsf->qtcb->bottom.io.fcp_rsp); + memcpy(&rec->fcp_rsp, fcp_rsp, FCP_RESP_WITH_EXT); + if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL) { + fcp_rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1]; + rec->fcp_rsp_info = fcp_rsp_info->rsp_code; + } + if (fcp_rsp->resp.fr_flags & FCP_SNS_LEN_VAL) { + rec->pl_len = min((u16)SCSI_SENSE_BUFFERSIZE, + (u16)ZFCP_DBF_PAY_MAX_REC); + zfcp_dbf_pl_write(dbf, sc->sense_buffer, rec->pl_len, + "fcp_sns", fsf->req_id); + } } - p += sprintf(p, "\n"); - return p - out_buf; -} -static struct debug_view zfcp_dbf_scsi_view = { - .name = "structured", - .header_proc = zfcp_dbf_view_header, - .format_proc = zfcp_dbf_scsi_view_format, -}; + debug_event(dbf->scsi, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->scsi_lock, flags); +} -static debug_info_t *zfcp_dbf_reg(const char *name, int level, - struct debug_view *view, int size) +static debug_info_t *zfcp_dbf_reg(const char *name, int size, int rec_size) { struct debug_info *d; - d = debug_register(name, dbfsize, level, size); + d = debug_register(name, size, 1, rec_size); if (!d) return NULL; debug_register_view(d, &debug_hex_ascii_view); - debug_register_view(d, view); - debug_set_level(d, level); + debug_set_level(d, 3); return d; } +static void zfcp_dbf_unregister(struct zfcp_dbf *dbf) +{ + if (!dbf) + return; + + debug_unregister(dbf->scsi); + debug_unregister(dbf->san); + debug_unregister(dbf->hba); + debug_unregister(dbf->pay); + debug_unregister(dbf->rec); + kfree(dbf); +} + /** * zfcp_adapter_debug_register - registers debug feature for an adapter * @adapter: pointer to adapter for which debug features should be registered @@ -1003,69 +415,66 @@ static debug_info_t *zfcp_dbf_reg(const char *name, int level, */ int zfcp_dbf_adapter_register(struct zfcp_adapter *adapter) { - char dbf_name[DEBUG_MAX_NAME_LEN]; + char name[DEBUG_MAX_NAME_LEN]; struct zfcp_dbf *dbf; dbf = kzalloc(sizeof(struct zfcp_dbf), GFP_KERNEL); if (!dbf) return -ENOMEM; - dbf->adapter = adapter; - + spin_lock_init(&dbf->pay_lock); spin_lock_init(&dbf->hba_lock); spin_lock_init(&dbf->san_lock); spin_lock_init(&dbf->scsi_lock); spin_lock_init(&dbf->rec_lock); /* debug feature area which records recovery activity */ - sprintf(dbf_name, "zfcp_%s_rec", dev_name(&adapter->ccw_device->dev)); - dbf->rec = zfcp_dbf_reg(dbf_name, 3, &zfcp_dbf_rec_view, - sizeof(struct zfcp_dbf_rec_record)); + sprintf(name, "zfcp_%s_rec", dev_name(&adapter->ccw_device->dev)); + dbf->rec = zfcp_dbf_reg(name, dbfsize, sizeof(struct zfcp_dbf_rec)); if (!dbf->rec) goto err_out; /* debug feature area which records HBA (FSF and QDIO) conditions */ - sprintf(dbf_name, "zfcp_%s_hba", dev_name(&adapter->ccw_device->dev)); - dbf->hba = zfcp_dbf_reg(dbf_name, 3, &zfcp_dbf_hba_view, - sizeof(struct zfcp_dbf_hba_record)); + sprintf(name, "zfcp_%s_hba", dev_name(&adapter->ccw_device->dev)); + dbf->hba = zfcp_dbf_reg(name, dbfsize, sizeof(struct zfcp_dbf_hba)); if (!dbf->hba) goto err_out; + /* debug feature area which records payload info */ + sprintf(name, "zfcp_%s_pay", dev_name(&adapter->ccw_device->dev)); + dbf->pay = zfcp_dbf_reg(name, dbfsize * 2, sizeof(struct zfcp_dbf_pay)); + if (!dbf->pay) + goto err_out; + /* debug feature area which records SAN command failures and recovery */ - sprintf(dbf_name, "zfcp_%s_san", dev_name(&adapter->ccw_device->dev)); - dbf->san = zfcp_dbf_reg(dbf_name, 6, &zfcp_dbf_san_view, - sizeof(struct zfcp_dbf_san_record)); + sprintf(name, "zfcp_%s_san", dev_name(&adapter->ccw_device->dev)); + dbf->san = zfcp_dbf_reg(name, dbfsize, sizeof(struct zfcp_dbf_san)); if (!dbf->san) goto err_out; /* debug feature area which records SCSI command failures and recovery */ - sprintf(dbf_name, "zfcp_%s_scsi", dev_name(&adapter->ccw_device->dev)); - dbf->scsi = zfcp_dbf_reg(dbf_name, 3, &zfcp_dbf_scsi_view, - sizeof(struct zfcp_dbf_scsi_record)); + sprintf(name, "zfcp_%s_scsi", dev_name(&adapter->ccw_device->dev)); + dbf->scsi = zfcp_dbf_reg(name, dbfsize, sizeof(struct zfcp_dbf_scsi)); if (!dbf->scsi) goto err_out; adapter->dbf = dbf; - return 0; + return 0; err_out: - zfcp_dbf_adapter_unregister(dbf); + zfcp_dbf_unregister(dbf); return -ENOMEM; } /** * zfcp_adapter_debug_unregister - unregisters debug feature for an adapter - * @dbf: pointer to dbf for which debug features should be unregistered + * @adapter: pointer to adapter for which debug features should be unregistered */ -void zfcp_dbf_adapter_unregister(struct zfcp_dbf *dbf) +void zfcp_dbf_adapter_unregister(struct zfcp_adapter *adapter) { - if (!dbf) - return; - debug_unregister(dbf->scsi); - debug_unregister(dbf->san); - debug_unregister(dbf->hba); - debug_unregister(dbf->rec); - dbf->adapter->dbf = NULL; - kfree(dbf); + struct zfcp_dbf *dbf = adapter->dbf; + + adapter->dbf = NULL; + zfcp_dbf_unregister(dbf); } diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 04081b1b62b..714f087eb7a 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -1,22 +1,8 @@ /* - * This file is part of the zfcp device driver for - * FCP adapters for IBM System z9 and zSeries. + * zfcp device driver + * debug feature declarations * - * Copyright IBM Corp. 2008, 2009 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * Copyright IBM Corp. 2008, 2010 */ #ifndef ZFCP_DBF_H @@ -27,322 +13,350 @@ #include "zfcp_fsf.h" #include "zfcp_def.h" -#define ZFCP_DBF_TAG_SIZE 4 -#define ZFCP_DBF_ID_SIZE 7 +#define ZFCP_DBF_TAG_LEN 7 #define ZFCP_DBF_INVALID_LUN 0xFFFFFFFFFFFFFFFFull -struct zfcp_dbf_dump { - u8 tag[ZFCP_DBF_TAG_SIZE]; - u32 total_size; /* size of total dump data */ - u32 offset; /* how much data has being already dumped */ - u32 size; /* how much data comes with this record */ - u8 data[]; /* dump data */ -} __attribute__ ((packed)); - -struct zfcp_dbf_rec_record_thread { - u32 total; +/** + * struct zfcp_dbf_rec_trigger - trace record for triggered recovery action + * @ready: number of ready recovery actions + * @running: number of running recovery actions + * @want: wanted recovery action + * @need: needed recovery action + */ +struct zfcp_dbf_rec_trigger { u32 ready; u32 running; -}; - -struct zfcp_dbf_rec_record_target { - u64 ref; - u32 status; - u32 d_id; - u64 wwpn; - u64 fcp_lun; - u32 erp_count; -}; - -struct zfcp_dbf_rec_record_trigger { u8 want; u8 need; - u32 as; - u32 ps; - u32 ls; - u64 ref; - u64 action; - u64 wwpn; - u64 fcp_lun; -}; +} __packed; -struct zfcp_dbf_rec_record_action { - u32 status; - u32 step; - u64 action; - u64 fsf_req; +/** + * struct zfcp_dbf_rec_running - trace record for running recovery + * @fsf_req_id: request id for fsf requests + * @rec_status: status of the fsf request + * @rec_step: current step of the recovery action + * rec_count: recovery counter + */ +struct zfcp_dbf_rec_running { + u64 fsf_req_id; + u32 rec_status; + u16 rec_step; + u8 rec_action; + u8 rec_count; +} __packed; + +/** + * enum zfcp_dbf_rec_id - recovery trace record id + * @ZFCP_DBF_REC_TRIG: triggered recovery identifier + * @ZFCP_DBF_REC_RUN: running recovery identifier + */ +enum zfcp_dbf_rec_id { + ZFCP_DBF_REC_TRIG = 1, + ZFCP_DBF_REC_RUN = 2, }; -struct zfcp_dbf_rec_record { +/** + * struct zfcp_dbf_rec - trace record for error recovery actions + * @id: unique number of recovery record type + * @tag: identifier string specifying the location of initiation + * @lun: logical unit number + * @wwpn: word wide port number + * @d_id: destination ID + * @adapter_status: current status of the adapter + * @port_status: current status of the port + * @lun_status: current status of the lun + * @u.trig: structure zfcp_dbf_rec_trigger + * @u.run: structure zfcp_dbf_rec_running + */ +struct zfcp_dbf_rec { u8 id; - char id2[7]; + char tag[ZFCP_DBF_TAG_LEN]; + u64 lun; + u64 wwpn; + u32 d_id; + u32 adapter_status; + u32 port_status; + u32 lun_status; union { - struct zfcp_dbf_rec_record_action action; - struct zfcp_dbf_rec_record_thread thread; - struct zfcp_dbf_rec_record_target target; - struct zfcp_dbf_rec_record_trigger trigger; + struct zfcp_dbf_rec_trigger trig; + struct zfcp_dbf_rec_running run; } u; -}; +} __packed; -enum { - ZFCP_REC_DBF_ID_ACTION, - ZFCP_REC_DBF_ID_THREAD, - ZFCP_REC_DBF_ID_TARGET, - ZFCP_REC_DBF_ID_TRIGGER, +/** + * enum zfcp_dbf_san_id - SAN trace record identifier + * @ZFCP_DBF_SAN_REQ: request trace record id + * @ZFCP_DBF_SAN_RES: response trace record id + * @ZFCP_DBF_SAN_ELS: extended link service record id + */ +enum zfcp_dbf_san_id { + ZFCP_DBF_SAN_REQ = 1, + ZFCP_DBF_SAN_RES = 2, + ZFCP_DBF_SAN_ELS = 3, }; -struct zfcp_dbf_hba_record_response { - u32 fsf_command; - u64 fsf_reqid; - u32 fsf_seqno; - u64 fsf_issued; - u32 fsf_prot_status; +/** struct zfcp_dbf_san - trace record for SAN requests and responses + * @id: unique number of recovery record type + * @tag: identifier string specifying the location of initiation + * @fsf_req_id: request id for fsf requests + * @payload: unformatted information related to request/response + * @d_id: destination id + */ +struct zfcp_dbf_san { + u8 id; + char tag[ZFCP_DBF_TAG_LEN]; + u64 fsf_req_id; + u32 d_id; +#define ZFCP_DBF_SAN_MAX_PAYLOAD (FC_CT_HDR_LEN + 32) + char payload[ZFCP_DBF_SAN_MAX_PAYLOAD]; +} __packed; + +/** + * struct zfcp_dbf_hba_res - trace record for hba responses + * @req_issued: timestamp when request was issued + * @prot_status: protocol status + * @prot_status_qual: protocol status qualifier + * @fsf_status: fsf status + * @fsf_status_qual: fsf status qualifier + */ +struct zfcp_dbf_hba_res { + u64 req_issued; + u32 prot_status; + u8 prot_status_qual[FSF_PROT_STATUS_QUAL_SIZE]; u32 fsf_status; - u8 fsf_prot_status_qual[FSF_PROT_STATUS_QUAL_SIZE]; - u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE]; - u32 fsf_req_status; - u8 sbal_first; - u8 sbal_last; - u8 sbal_response; - u8 pool; - u64 erp_action; - union { - struct { - u64 cmnd; - u32 data_dir; - } fcp; - struct { - u64 wwpn; - u32 d_id; - u32 port_handle; - } port; - struct { - u64 wwpn; - u64 fcp_lun; - u32 port_handle; - u32 lun_handle; - } unit; - struct { - u32 d_id; - } els; - } u; -} __attribute__ ((packed)); + u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE]; +} __packed; -struct zfcp_dbf_hba_record_status { - u8 failed; +/** + * struct zfcp_dbf_hba_uss - trace record for unsolicited status + * @status_type: type of unsolicited status + * @status_subtype: subtype of unsolicited status + * @d_id: destination ID + * @lun: logical unit number + * @queue_designator: queue designator + */ +struct zfcp_dbf_hba_uss { u32 status_type; u32 status_subtype; - struct fsf_queue_designator - queue_designator; - u32 payload_size; -#define ZFCP_DBF_UNSOL_PAYLOAD 80 -#define ZFCP_DBF_UNSOL_PAYLOAD_SENSE_DATA_AVAIL 32 -#define ZFCP_DBF_UNSOL_PAYLOAD_BIT_ERROR_THRESHOLD 56 -#define ZFCP_DBF_UNSOL_PAYLOAD_FEATURE_UPDATE_ALERT 2 * sizeof(u32) - u8 payload[ZFCP_DBF_UNSOL_PAYLOAD]; -} __attribute__ ((packed)); - -struct zfcp_dbf_hba_record_qdio { - u32 qdio_error; - u8 sbal_index; - u8 sbal_count; -} __attribute__ ((packed)); - -struct zfcp_dbf_hba_record { - u8 tag[ZFCP_DBF_TAG_SIZE]; - u8 tag2[ZFCP_DBF_TAG_SIZE]; - union { - struct zfcp_dbf_hba_record_response response; - struct zfcp_dbf_hba_record_status status; - struct zfcp_dbf_hba_record_qdio qdio; - struct fsf_bit_error_payload berr; - } u; -} __attribute__ ((packed)); - -struct zfcp_dbf_san_record_ct_request { - u16 cmd_req_code; - u8 revision; - u8 gs_type; - u8 gs_subtype; - u8 options; - u16 max_res_size; - u32 len; - u32 d_id; -} __attribute__ ((packed)); - -struct zfcp_dbf_san_record_ct_response { - u16 cmd_rsp_code; - u8 revision; - u8 reason_code; - u8 expl; - u8 vendor_unique; - u16 max_res_size; - u32 len; -} __attribute__ ((packed)); - -struct zfcp_dbf_san_record_els { u32 d_id; -} __attribute__ ((packed)); + u64 lun; + u64 queue_designator; +} __packed; -struct zfcp_dbf_san_record { - u8 tag[ZFCP_DBF_TAG_SIZE]; - u64 fsf_reqid; - u32 fsf_seqno; +/** + * enum zfcp_dbf_hba_id - HBA trace record identifier + * @ZFCP_DBF_HBA_RES: response trace record + * @ZFCP_DBF_HBA_USS: unsolicited status trace record + * @ZFCP_DBF_HBA_BIT: bit error trace record + */ +enum zfcp_dbf_hba_id { + ZFCP_DBF_HBA_RES = 1, + ZFCP_DBF_HBA_USS = 2, + ZFCP_DBF_HBA_BIT = 3, +}; + +/** + * struct zfcp_dbf_hba - common trace record for HBA records + * @id: unique number of recovery record type + * @tag: identifier string specifying the location of initiation + * @fsf_req_id: request id for fsf requests + * @fsf_req_status: status of fsf request + * @fsf_cmd: fsf command + * @fsf_seq_no: fsf sequence number + * @pl_len: length of payload stored as zfcp_dbf_pay + * @u: record type specific data + */ +struct zfcp_dbf_hba { + u8 id; + char tag[ZFCP_DBF_TAG_LEN]; + u64 fsf_req_id; + u32 fsf_req_status; + u32 fsf_cmd; + u32 fsf_seq_no; + u16 pl_len; union { - struct zfcp_dbf_san_record_ct_request ct_req; - struct zfcp_dbf_san_record_ct_response ct_resp; - struct zfcp_dbf_san_record_els els; + struct zfcp_dbf_hba_res res; + struct zfcp_dbf_hba_uss uss; + struct fsf_bit_error_payload be; } u; -} __attribute__ ((packed)); +} __packed; -#define ZFCP_DBF_SAN_MAX_PAYLOAD 1024 +/** + * enum zfcp_dbf_scsi_id - scsi trace record identifier + * @ZFCP_DBF_SCSI_CMND: scsi command trace record + */ +enum zfcp_dbf_scsi_id { + ZFCP_DBF_SCSI_CMND = 1, +}; -struct zfcp_dbf_scsi_record { - u8 tag[ZFCP_DBF_TAG_SIZE]; - u8 tag2[ZFCP_DBF_TAG_SIZE]; +/** + * struct zfcp_dbf_scsi - common trace record for SCSI records + * @id: unique number of recovery record type + * @tag: identifier string specifying the location of initiation + * @scsi_id: scsi device id + * @scsi_lun: scsi device logical unit number + * @scsi_result: scsi result + * @scsi_retries: current retry number of scsi request + * @scsi_allowed: allowed retries + * @fcp_rsp_info: FCP response info + * @scsi_opcode: scsi opcode + * @fsf_req_id: request id of fsf request + * @host_scribble: LLD specific data attached to SCSI request + * @pl_len: length of paload stored as zfcp_dbf_pay + * @fsf_rsp: response for fsf request + */ +struct zfcp_dbf_scsi { + u8 id; + char tag[ZFCP_DBF_TAG_LEN]; u32 scsi_id; u32 scsi_lun; u32 scsi_result; - u64 scsi_cmnd; -#define ZFCP_DBF_SCSI_OPCODE 16 - u8 scsi_opcode[ZFCP_DBF_SCSI_OPCODE]; u8 scsi_retries; u8 scsi_allowed; - u64 fsf_reqid; - u32 fsf_seqno; - u64 fsf_issued; - u64 old_fsf_reqid; - u8 rsp_validity; - u8 rsp_scsi_status; - u32 rsp_resid; - u8 rsp_code; -#define ZFCP_DBF_SCSI_FCP_SNS_INFO 16 -#define ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO 256 - u32 sns_info_len; - u8 sns_info[ZFCP_DBF_SCSI_FCP_SNS_INFO]; -} __attribute__ ((packed)); + u8 fcp_rsp_info; +#define ZFCP_DBF_SCSI_OPCODE 16 + u8 scsi_opcode[ZFCP_DBF_SCSI_OPCODE]; + u64 fsf_req_id; + u64 host_scribble; + u16 pl_len; + struct fcp_resp_with_ext fcp_rsp; +} __packed; +/** + * struct zfcp_dbf_pay - trace record for unformatted payload information + * @area: area this record is originated from + * @counter: ascending record number + * @fsf_req_id: request id of fsf request + * @data: unformatted data + */ +struct zfcp_dbf_pay { + u8 counter; + char area[ZFCP_DBF_TAG_LEN]; + u64 fsf_req_id; +#define ZFCP_DBF_PAY_MAX_REC 0x100 + char data[ZFCP_DBF_PAY_MAX_REC]; +} __packed; + +/** + * struct zfcp_dbf - main dbf trace structure + * @pay: reference to payload trace area + * @rec: reference to recovery trace area + * @hba: reference to hba trace area + * @san: reference to san trace area + * @scsi: reference to scsi trace area + * @pay_lock: lock protecting payload trace buffer + * @rec_lock: lock protecting recovery trace buffer + * @hba_lock: lock protecting hba trace buffer + * @san_lock: lock protecting san trace buffer + * @scsi_lock: lock protecting scsi trace buffer + * @pay_buf: pre-allocated buffer for payload + * @rec_buf: pre-allocated buffer for recovery + * @hba_buf: pre-allocated buffer for hba + * @san_buf: pre-allocated buffer for san + * @scsi_buf: pre-allocated buffer for scsi + */ struct zfcp_dbf { + debug_info_t *pay; debug_info_t *rec; debug_info_t *hba; debug_info_t *san; debug_info_t *scsi; + spinlock_t pay_lock; spinlock_t rec_lock; spinlock_t hba_lock; spinlock_t san_lock; spinlock_t scsi_lock; - struct zfcp_dbf_rec_record rec_buf; - struct zfcp_dbf_hba_record hba_buf; - struct zfcp_dbf_san_record san_buf; - struct zfcp_dbf_scsi_record scsi_buf; - struct zfcp_adapter *adapter; + struct zfcp_dbf_pay pay_buf; + struct zfcp_dbf_rec rec_buf; + struct zfcp_dbf_hba hba_buf; + struct zfcp_dbf_san san_buf; + struct zfcp_dbf_scsi scsi_buf; }; static inline -void zfcp_dbf_hba_fsf_resp(const char *tag2, int level, - struct zfcp_fsf_req *req, struct zfcp_dbf *dbf) +void zfcp_dbf_hba_fsf_resp(char *tag, int level, struct zfcp_fsf_req *req) { - if (level <= dbf->hba->level) - _zfcp_dbf_hba_fsf_response(tag2, level, req, dbf); + if (level <= req->adapter->dbf->hba->level) + zfcp_dbf_hba_fsf_res(tag, req); } /** * zfcp_dbf_hba_fsf_response - trace event for request completion - * @fsf_req: request that has been completed + * @req: request that has been completed */ -static inline void zfcp_dbf_hba_fsf_response(struct zfcp_fsf_req *req) +static inline +void zfcp_dbf_hba_fsf_response(struct zfcp_fsf_req *req) { - struct zfcp_dbf *dbf = req->adapter->dbf; struct fsf_qtcb *qtcb = req->qtcb; if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) && (qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) { - zfcp_dbf_hba_fsf_resp("perr", 1, req, dbf); + zfcp_dbf_hba_fsf_resp("fs_perr", 1, req); } else if (qtcb->header.fsf_status != FSF_GOOD) { - zfcp_dbf_hba_fsf_resp("ferr", 1, req, dbf); + zfcp_dbf_hba_fsf_resp("fs_ferr", 1, req); } else if ((req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) || (req->fsf_command == FSF_QTCB_OPEN_LUN)) { - zfcp_dbf_hba_fsf_resp("open", 4, req, dbf); + zfcp_dbf_hba_fsf_resp("fs_open", 4, req); } else if (qtcb->header.log_length) { - zfcp_dbf_hba_fsf_resp("qtcb", 5, req, dbf); + zfcp_dbf_hba_fsf_resp("fs_qtcb", 5, req); } else { - zfcp_dbf_hba_fsf_resp("norm", 6, req, dbf); + zfcp_dbf_hba_fsf_resp("fs_norm", 6, req); } - } - -/** - * zfcp_dbf_hba_fsf_unsol - trace event for an unsolicited status buffer - * @tag: tag indicating which kind of unsolicited status has been received - * @dbf: reference to dbf structure - * @status_buffer: buffer containing payload of unsolicited status - */ -static inline -void zfcp_dbf_hba_fsf_unsol(const char *tag, struct zfcp_dbf *dbf, - struct fsf_status_read_buffer *buf) -{ - int level = 2; - - if (level <= dbf->hba->level) - _zfcp_dbf_hba_fsf_unsol(tag, level, dbf, buf); } static inline -void zfcp_dbf_scsi(const char *tag, const char *tag2, int level, - struct zfcp_dbf *dbf, struct scsi_cmnd *scmd, - struct zfcp_fsf_req *req, unsigned long old_id) +void _zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *scmd, + struct zfcp_fsf_req *req) { - if (level <= dbf->scsi->level) - _zfcp_dbf_scsi(tag, tag2, level, dbf, scmd, req, old_id); + struct zfcp_adapter *adapter = (struct zfcp_adapter *) + scmd->device->host->hostdata[0]; + + if (level <= adapter->dbf->scsi->level) + zfcp_dbf_scsi(tag, scmd, req); } /** * zfcp_dbf_scsi_result - trace event for SCSI command completion - * @dbf: adapter dbf trace * @scmd: SCSI command pointer * @req: FSF request used to issue SCSI command */ static inline -void zfcp_dbf_scsi_result(struct zfcp_dbf *dbf, struct scsi_cmnd *scmd, - struct zfcp_fsf_req *req) +void zfcp_dbf_scsi_result(struct scsi_cmnd *scmd, struct zfcp_fsf_req *req) { if (scmd->result != 0) - zfcp_dbf_scsi("rslt", "erro", 3, dbf, scmd, req, 0); + _zfcp_dbf_scsi("rsl_err", 3, scmd, req); else if (scmd->retries > 0) - zfcp_dbf_scsi("rslt", "retr", 4, dbf, scmd, req, 0); + _zfcp_dbf_scsi("rsl_ret", 4, scmd, req); else - zfcp_dbf_scsi("rslt", "norm", 6, dbf, scmd, req, 0); + _zfcp_dbf_scsi("rsl_nor", 6, scmd, req); } /** * zfcp_dbf_scsi_fail_send - trace event for failure to send SCSI command - * @dbf: adapter dbf trace * @scmd: SCSI command pointer */ static inline -void zfcp_dbf_scsi_fail_send(struct zfcp_dbf *dbf, struct scsi_cmnd *scmd) +void zfcp_dbf_scsi_fail_send(struct scsi_cmnd *scmd) { - zfcp_dbf_scsi("rslt", "fail", 4, dbf, scmd, NULL, 0); + _zfcp_dbf_scsi("rsl_fai", 4, scmd, NULL); } /** * zfcp_dbf_scsi_abort - trace event for SCSI command abort * @tag: tag indicating success or failure of abort operation - * @adapter: adapter thas has been used to issue SCSI command to be aborted * @scmd: SCSI command to be aborted - * @new_req: request containing abort (might be NULL) - * @old_id: identifier of request containg SCSI command to be aborted + * @fsf_req: request containing abort (might be NULL) */ static inline -void zfcp_dbf_scsi_abort(const char *tag, struct zfcp_dbf *dbf, - struct scsi_cmnd *scmd, struct zfcp_fsf_req *new_req, - unsigned long old_id) +void zfcp_dbf_scsi_abort(char *tag, struct scsi_cmnd *scmd, + struct zfcp_fsf_req *fsf_req) { - zfcp_dbf_scsi("abrt", tag, 1, dbf, scmd, new_req, old_id); + _zfcp_dbf_scsi(tag, 1, scmd, fsf_req); } /** @@ -352,12 +366,17 @@ void zfcp_dbf_scsi_abort(const char *tag, struct zfcp_dbf *dbf, * @flag: indicates type of reset (Target Reset, Logical Unit Reset) */ static inline -void zfcp_dbf_scsi_devreset(const char *tag, struct scsi_cmnd *scmnd, u8 flag) +void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag) { - struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scmnd->device); + char tmp_tag[ZFCP_DBF_TAG_LEN]; + + if (flag == FCP_TMF_TGT_RESET) + memcpy(tmp_tag, "tr_", 3); + else + memcpy(tmp_tag, "lr_", 3); - zfcp_dbf_scsi(flag == FCP_TMF_TGT_RESET ? "trst" : "lrst", tag, 1, - zfcp_sdev->port->adapter->dbf, scmnd, NULL, 0); + memcpy(&tmp_tag[3], tag, 4); + _zfcp_dbf_scsi(tmp_tag, 1, scmnd, NULL); } #endif /* ZFCP_DBF_H */ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index d37c7331f24..e003e306f87 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -76,9 +76,9 @@ static void zfcp_erp_action_ready(struct zfcp_erp_action *act) struct zfcp_adapter *adapter = act->adapter; list_move(&act->list, &act->adapter->erp_ready_head); - zfcp_dbf_rec_action("erardy1", act); + zfcp_dbf_rec_run("erardy1", act); wake_up(&adapter->erp_ready_wq); - zfcp_dbf_rec_thread("erardy2", adapter->dbf); + zfcp_dbf_rec_run("erardy2", act); } static void zfcp_erp_action_dismiss(struct zfcp_erp_action *act) @@ -156,6 +156,8 @@ static int zfcp_erp_required_act(int want, struct zfcp_adapter *adapter, if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) || a_status & ZFCP_STATUS_COMMON_ERP_FAILED) return 0; + if (p_status & ZFCP_STATUS_COMMON_NOESC) + return need; if (!(a_status & ZFCP_STATUS_COMMON_UNBLOCKED)) need = ZFCP_ERP_ACTION_REOPEN_ADAPTER; /* fall through */ @@ -188,6 +190,9 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status, atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &zfcp_sdev->status); erp_action = &zfcp_sdev->erp_action; + memset(erp_action, 0, sizeof(struct zfcp_erp_action)); + erp_action->port = port; + erp_action->sdev = sdev; if (!(atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_RUNNING)) act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; @@ -200,6 +205,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status, zfcp_erp_action_dismiss_port(port); atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status); erp_action = &port->erp_action; + memset(erp_action, 0, sizeof(struct zfcp_erp_action)); + erp_action->port = port; if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING)) act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; break; @@ -209,6 +216,7 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status, zfcp_erp_action_dismiss_adapter(adapter); atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status); erp_action = &adapter->erp_action; + memset(erp_action, 0, sizeof(struct zfcp_erp_action)); if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; @@ -218,10 +226,7 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status, return NULL; } - memset(erp_action, 0, sizeof(struct zfcp_erp_action)); erp_action->adapter = adapter; - erp_action->port = port; - erp_action->sdev = sdev; erp_action->action = need; erp_action->status = act_status; @@ -231,10 +236,10 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status, static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter, struct zfcp_port *port, struct scsi_device *sdev, - char *id, void *ref, u32 act_status) + char *id, u32 act_status) { int retval = 1, need; - struct zfcp_erp_action *act = NULL; + struct zfcp_erp_action *act; if (!adapter->erp_thread) return -EIO; @@ -250,15 +255,14 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter, ++adapter->erp_total_count; list_add_tail(&act->list, &adapter->erp_ready_head); wake_up(&adapter->erp_ready_wq); - zfcp_dbf_rec_thread("eracte1", adapter->dbf); retval = 0; out: - zfcp_dbf_rec_trigger(id, ref, want, need, act, adapter, port, sdev); + zfcp_dbf_rec_trig(id, adapter, port, sdev, want, need); return retval; } static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, - int clear_mask, char *id, void *ref) + int clear_mask, char *id) { zfcp_erp_adapter_block(adapter, clear_mask); zfcp_scsi_schedule_rports_block(adapter); @@ -270,7 +274,7 @@ static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, return -EIO; } return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, - adapter, NULL, NULL, id, ref, 0); + adapter, NULL, NULL, id, 0); } /** @@ -278,10 +282,8 @@ static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, * @adapter: Adapter to reopen. * @clear: Status flags to clear. * @id: Id for debug trace event. - * @ref: Reference for debug trace event. */ -void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear, - char *id, void *ref) +void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear, char *id) { unsigned long flags; @@ -294,7 +296,7 @@ void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear, ZFCP_STATUS_COMMON_ERP_FAILED); else zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, adapter, - NULL, NULL, id, ref, 0); + NULL, NULL, id, 0); write_unlock_irqrestore(&adapter->erp_lock, flags); } @@ -303,13 +305,12 @@ void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear, * @adapter: Adapter to shut down. * @clear: Status flags to clear. * @id: Id for debug trace event. - * @ref: Reference for debug trace event. */ void zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear, - char *id, void *ref) + char *id) { int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; - zfcp_erp_adapter_reopen(adapter, clear | flags, id, ref); + zfcp_erp_adapter_reopen(adapter, clear | flags, id); } /** @@ -317,13 +318,11 @@ void zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear, * @port: Port to shut down. * @clear: Status flags to clear. * @id: Id for debug trace event. - * @ref: Reference for debug trace event. */ -void zfcp_erp_port_shutdown(struct zfcp_port *port, int clear, char *id, - void *ref) +void zfcp_erp_port_shutdown(struct zfcp_port *port, int clear, char *id) { int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; - zfcp_erp_port_reopen(port, clear | flags, id, ref); + zfcp_erp_port_reopen(port, clear | flags, id); } static void zfcp_erp_port_block(struct zfcp_port *port, int clear) @@ -332,8 +331,8 @@ static void zfcp_erp_port_block(struct zfcp_port *port, int clear) ZFCP_STATUS_COMMON_UNBLOCKED | clear); } -static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port, - int clear, char *id, void *ref) +static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, + char *id) { zfcp_erp_port_block(port, clear); zfcp_scsi_schedule_rport_block(port); @@ -342,28 +341,26 @@ static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port, return; zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED, - port->adapter, port, NULL, id, ref, 0); + port->adapter, port, NULL, id, 0); } /** * zfcp_erp_port_forced_reopen - Forced close of port and open again * @port: Port to force close and to reopen. + * @clear: Status flags to clear. * @id: Id for debug trace event. - * @ref: Reference for debug trace event. */ -void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, char *id, - void *ref) +void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, char *id) { unsigned long flags; struct zfcp_adapter *adapter = port->adapter; write_lock_irqsave(&adapter->erp_lock, flags); - _zfcp_erp_port_forced_reopen(port, clear, id, ref); + _zfcp_erp_port_forced_reopen(port, clear, id); write_unlock_irqrestore(&adapter->erp_lock, flags); } -static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id, - void *ref) +static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id) { zfcp_erp_port_block(port, clear); zfcp_scsi_schedule_rport_block(port); @@ -375,24 +372,25 @@ static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id, } return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT, - port->adapter, port, NULL, id, ref, 0); + port->adapter, port, NULL, id, 0); } /** * zfcp_erp_port_reopen - trigger remote port recovery * @port: port to recover * @clear_mask: flags in port status to be cleared + * @id: Id for debug trace event. * * Returns 0 if recovery has been triggered, < 0 if not. */ -int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id, void *ref) +int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id) { int retval; unsigned long flags; struct zfcp_adapter *adapter = port->adapter; write_lock_irqsave(&adapter->erp_lock, flags); - retval = _zfcp_erp_port_reopen(port, clear, id, ref); + retval = _zfcp_erp_port_reopen(port, clear, id); write_unlock_irqrestore(&adapter->erp_lock, flags); return retval; @@ -405,7 +403,7 @@ static void zfcp_erp_lun_block(struct scsi_device *sdev, int clear_mask) } static void _zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *id, - void *ref, u32 act_status) + u32 act_status) { struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; @@ -416,17 +414,18 @@ static void _zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *id, return; zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_LUN, adapter, - zfcp_sdev->port, sdev, id, ref, act_status); + zfcp_sdev->port, sdev, id, act_status); } /** * zfcp_erp_lun_reopen - initiate reopen of a LUN * @sdev: SCSI device / LUN to be reopened * @clear_mask: specifies flags in LUN status to be cleared + * @id: Id for debug trace event. + * * Return: 0 on success, < 0 on error */ -void zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *id, - void *ref) +void zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *id) { unsigned long flags; struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); @@ -434,7 +433,7 @@ void zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *id, struct zfcp_adapter *adapter = port->adapter; write_lock_irqsave(&adapter->erp_lock, flags); - _zfcp_erp_lun_reopen(sdev, clear, id, ref, 0); + _zfcp_erp_lun_reopen(sdev, clear, id, 0); write_unlock_irqrestore(&adapter->erp_lock, flags); } @@ -443,13 +442,11 @@ void zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *id, * @sdev: SCSI device / LUN to shut down. * @clear: Status flags to clear. * @id: Id for debug trace event. - * @ref: Reference for debug trace event. */ -void zfcp_erp_lun_shutdown(struct scsi_device *sdev, int clear, char *id, - void *ref) +void zfcp_erp_lun_shutdown(struct scsi_device *sdev, int clear, char *id) { int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; - zfcp_erp_lun_reopen(sdev, clear | flags, id, ref); + zfcp_erp_lun_reopen(sdev, clear | flags, id); } /** @@ -471,7 +468,7 @@ void zfcp_erp_lun_shutdown_wait(struct scsi_device *sdev, char *id) int clear = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED; write_lock_irqsave(&adapter->erp_lock, flags); - _zfcp_erp_lun_reopen(sdev, clear, id, NULL, ZFCP_STATUS_ERP_NO_REF); + _zfcp_erp_lun_reopen(sdev, clear, id, ZFCP_STATUS_ERP_NO_REF); write_unlock_irqrestore(&adapter->erp_lock, flags); zfcp_erp_wait(adapter); @@ -485,14 +482,14 @@ static int status_change_set(unsigned long mask, atomic_t *status) static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter) { if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status)) - zfcp_dbf_rec_adapter("eraubl1", NULL, adapter->dbf); + zfcp_dbf_rec_run("eraubl1", &adapter->erp_action); atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status); } static void zfcp_erp_port_unblock(struct zfcp_port *port) { if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status)) - zfcp_dbf_rec_port("erpubl1", NULL, port); + zfcp_dbf_rec_run("erpubl1", &port->erp_action); atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status); } @@ -501,14 +498,14 @@ static void zfcp_erp_lun_unblock(struct scsi_device *sdev) struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status)) - zfcp_dbf_rec_lun("erlubl1", NULL, sdev); + zfcp_dbf_rec_run("erlubl1", &sdev_to_zfcp(sdev)->erp_action); atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status); } static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action) { list_move(&erp_action->list, &erp_action->adapter->erp_running_head); - zfcp_dbf_rec_action("erator1", erp_action); + zfcp_dbf_rec_run("erator1", erp_action); } static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act) @@ -525,11 +522,11 @@ static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act) if (act->status & (ZFCP_STATUS_ERP_DISMISSED | ZFCP_STATUS_ERP_TIMEDOUT)) { req->status |= ZFCP_STATUS_FSFREQ_DISMISSED; - zfcp_dbf_rec_action("erscf_1", act); + zfcp_dbf_rec_run("erscf_1", act); req->erp_action = NULL; } if (act->status & ZFCP_STATUS_ERP_TIMEDOUT) - zfcp_dbf_rec_action("erscf_2", act); + zfcp_dbf_rec_run("erscf_2", act); if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) act->fsf_req_id = 0; } else @@ -580,40 +577,40 @@ static void zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action) } static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter, - int clear, char *id, void *ref) + int clear, char *id) { struct zfcp_port *port; read_lock(&adapter->port_list_lock); list_for_each_entry(port, &adapter->port_list, list) - _zfcp_erp_port_reopen(port, clear, id, ref); + _zfcp_erp_port_reopen(port, clear, id); read_unlock(&adapter->port_list_lock); } static void _zfcp_erp_lun_reopen_all(struct zfcp_port *port, int clear, - char *id, void *ref) + char *id) { struct scsi_device *sdev; shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) - _zfcp_erp_lun_reopen(sdev, clear, id, ref, 0); + _zfcp_erp_lun_reopen(sdev, clear, id, 0); } static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act) { switch (act->action) { case ZFCP_ERP_ACTION_REOPEN_ADAPTER: - _zfcp_erp_adapter_reopen(act->adapter, 0, "ersff_1", NULL); + _zfcp_erp_adapter_reopen(act->adapter, 0, "ersff_1"); break; case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: - _zfcp_erp_port_forced_reopen(act->port, 0, "ersff_2", NULL); + _zfcp_erp_port_forced_reopen(act->port, 0, "ersff_2"); break; case ZFCP_ERP_ACTION_REOPEN_PORT: - _zfcp_erp_port_reopen(act->port, 0, "ersff_3", NULL); + _zfcp_erp_port_reopen(act->port, 0, "ersff_3"); break; case ZFCP_ERP_ACTION_REOPEN_LUN: - _zfcp_erp_lun_reopen(act->sdev, 0, "ersff_4", NULL, 0); + _zfcp_erp_lun_reopen(act->sdev, 0, "ersff_4", 0); break; } } @@ -622,13 +619,13 @@ static void zfcp_erp_strategy_followup_success(struct zfcp_erp_action *act) { switch (act->action) { case ZFCP_ERP_ACTION_REOPEN_ADAPTER: - _zfcp_erp_port_reopen_all(act->adapter, 0, "ersfs_1", NULL); + _zfcp_erp_port_reopen_all(act->adapter, 0, "ersfs_1"); break; case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: - _zfcp_erp_port_reopen(act->port, 0, "ersfs_2", NULL); + _zfcp_erp_port_reopen(act->port, 0, "ersfs_2"); break; case ZFCP_ERP_ACTION_REOPEN_PORT: - _zfcp_erp_lun_reopen_all(act->port, 0, "ersfs_3", NULL); + _zfcp_erp_lun_reopen_all(act->port, 0, "ersfs_3"); break; } } @@ -647,17 +644,6 @@ static void zfcp_erp_wakeup(struct zfcp_adapter *adapter) read_unlock_irqrestore(&adapter->erp_lock, flags); } -static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *act) -{ - struct zfcp_qdio *qdio = act->adapter->qdio; - - if (zfcp_qdio_open(qdio)) - return ZFCP_ERP_FAILED; - init_waitqueue_head(&qdio->req_q_wq); - atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &act->adapter->status); - return ZFCP_ERP_SUCCEEDED; -} - static void zfcp_erp_enqueue_ptp_port(struct zfcp_adapter *adapter) { struct zfcp_port *port; @@ -665,7 +651,7 @@ static void zfcp_erp_enqueue_ptp_port(struct zfcp_adapter *adapter) adapter->peer_d_id); if (IS_ERR(port)) /* error or port already attached */ return; - _zfcp_erp_port_reopen(port, 0, "ereptp1", NULL); + _zfcp_erp_port_reopen(port, 0, "ereptp1"); } static int zfcp_erp_adapter_strat_fsf_xconf(struct zfcp_erp_action *erp_action) @@ -688,10 +674,8 @@ static int zfcp_erp_adapter_strat_fsf_xconf(struct zfcp_erp_action *erp_action) return ZFCP_ERP_FAILED; } - zfcp_dbf_rec_thread_lock("erasfx1", adapter->dbf); wait_event(adapter->erp_ready_wq, !list_empty(&adapter->erp_ready_head)); - zfcp_dbf_rec_thread_lock("erasfx2", adapter->dbf); if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) break; @@ -730,10 +714,10 @@ static int zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *act) if (ret) return ZFCP_ERP_FAILED; - zfcp_dbf_rec_thread_lock("erasox1", adapter->dbf); + zfcp_dbf_rec_run("erasox1", act); wait_event(adapter->erp_ready_wq, !list_empty(&adapter->erp_ready_head)); - zfcp_dbf_rec_thread_lock("erasox2", adapter->dbf); + zfcp_dbf_rec_run("erasox2", act); if (act->status & ZFCP_STATUS_ERP_TIMEDOUT) return ZFCP_ERP_FAILED; @@ -783,7 +767,7 @@ static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *act) { struct zfcp_adapter *adapter = act->adapter; - if (zfcp_erp_adapter_strategy_open_qdio(act)) { + if (zfcp_qdio_open(adapter->qdio)) { atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK | ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status); @@ -1161,7 +1145,7 @@ static int zfcp_erp_strategy_statechange(struct zfcp_erp_action *act, int ret) if (zfcp_erp_strat_change_det(&adapter->status, erp_status)) { _zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, - "ersscg1", NULL); + "ersscg1"); return ZFCP_ERP_EXIT; } break; @@ -1171,7 +1155,7 @@ static int zfcp_erp_strategy_statechange(struct zfcp_erp_action *act, int ret) if (zfcp_erp_strat_change_det(&port->status, erp_status)) { _zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, - "ersscg2", NULL); + "ersscg2"); return ZFCP_ERP_EXIT; } break; @@ -1181,7 +1165,7 @@ static int zfcp_erp_strategy_statechange(struct zfcp_erp_action *act, int ret) if (zfcp_erp_strat_change_det(&zfcp_sdev->status, erp_status)) { _zfcp_erp_lun_reopen(sdev, ZFCP_STATUS_COMMON_ERP_FAILED, - "ersscg3", NULL, 0); + "ersscg3", 0); return ZFCP_ERP_EXIT; } break; @@ -1201,7 +1185,7 @@ static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action) } list_del(&erp_action->list); - zfcp_dbf_rec_action("eractd1", erp_action); + zfcp_dbf_rec_run("eractd1", erp_action); switch (erp_action->action) { case ZFCP_ERP_ACTION_REOPEN_LUN: @@ -1308,7 +1292,7 @@ static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action) erp_action->status |= ZFCP_STATUS_ERP_LOWMEM; } if (adapter->erp_total_count == adapter->erp_low_mem_count) - _zfcp_erp_adapter_reopen(adapter, 0, "erstgy1", NULL); + _zfcp_erp_adapter_reopen(adapter, 0, "erstgy1"); else { zfcp_erp_strategy_memwait(erp_action); retval = ZFCP_ERP_CONTINUES; @@ -1352,11 +1336,9 @@ static int zfcp_erp_thread(void *data) unsigned long flags; for (;;) { - zfcp_dbf_rec_thread_lock("erthrd1", adapter->dbf); wait_event_interruptible(adapter->erp_ready_wq, !list_empty(&adapter->erp_ready_head) || kthread_should_stop()); - zfcp_dbf_rec_thread_lock("erthrd2", adapter->dbf); if (kthread_should_stop()) break; diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index bf8f3e51483..6e325284fbe 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -45,47 +45,33 @@ extern void zfcp_cfdc_adapter_access_changed(struct zfcp_adapter *); /* zfcp_dbf.c */ extern int zfcp_dbf_adapter_register(struct zfcp_adapter *); -extern void zfcp_dbf_adapter_unregister(struct zfcp_dbf *); -extern void zfcp_dbf_rec_thread(char *, struct zfcp_dbf *); -extern void zfcp_dbf_rec_thread_lock(char *, struct zfcp_dbf *); -extern void zfcp_dbf_rec_adapter(char *, void *, struct zfcp_dbf *); -extern void zfcp_dbf_rec_port(char *, void *, struct zfcp_port *); -extern void zfcp_dbf_rec_lun(char *, void *, struct scsi_device *); -extern void zfcp_dbf_rec_trigger(char *, void *, u8, u8, void *, - struct zfcp_adapter *, struct zfcp_port *, - struct scsi_device *); -extern void zfcp_dbf_rec_action(char *, struct zfcp_erp_action *); -extern void _zfcp_dbf_hba_fsf_response(const char *, int, struct zfcp_fsf_req *, - struct zfcp_dbf *); -extern void _zfcp_dbf_hba_fsf_unsol(const char *, int level, struct zfcp_dbf *, - struct fsf_status_read_buffer *); -extern void zfcp_dbf_hba_qdio(struct zfcp_dbf *, unsigned int, int, int); +extern void zfcp_dbf_adapter_unregister(struct zfcp_adapter *); +extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *, + struct zfcp_port *, struct scsi_device *, u8, u8); +extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *); +extern void zfcp_dbf_hba_fsf_uss(char *, struct zfcp_fsf_req *); +extern void zfcp_dbf_hba_fsf_res(char *, struct zfcp_fsf_req *); +extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *); -extern void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *, u32); -extern void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *); -extern void zfcp_dbf_san_els_request(struct zfcp_fsf_req *); -extern void zfcp_dbf_san_els_response(struct zfcp_fsf_req *); -extern void zfcp_dbf_san_incoming_els(struct zfcp_fsf_req *); -extern void _zfcp_dbf_scsi(const char *, const char *, int, struct zfcp_dbf *, - struct scsi_cmnd *, struct zfcp_fsf_req *, - unsigned long); +extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32); +extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *); +extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *); +extern void zfcp_dbf_scsi(char *, struct scsi_cmnd *, struct zfcp_fsf_req *); /* zfcp_erp.c */ extern void zfcp_erp_set_adapter_status(struct zfcp_adapter *, u32); extern void zfcp_erp_clear_adapter_status(struct zfcp_adapter *, u32); -extern void zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, char *, void *); -extern void zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, char *, - void *); +extern void zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, char *); +extern void zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, char *); extern void zfcp_erp_set_port_status(struct zfcp_port *, u32); extern void zfcp_erp_clear_port_status(struct zfcp_port *, u32); -extern int zfcp_erp_port_reopen(struct zfcp_port *, int, char *, void *); -extern void zfcp_erp_port_shutdown(struct zfcp_port *, int, char *, void *); -extern void zfcp_erp_port_forced_reopen(struct zfcp_port *, int, char *, - void *); +extern int zfcp_erp_port_reopen(struct zfcp_port *, int, char *); +extern void zfcp_erp_port_shutdown(struct zfcp_port *, int, char *); +extern void zfcp_erp_port_forced_reopen(struct zfcp_port *, int, char *); extern void zfcp_erp_set_lun_status(struct scsi_device *, u32); extern void zfcp_erp_clear_lun_status(struct scsi_device *, u32); -extern void zfcp_erp_lun_reopen(struct scsi_device *, int, char *, void *); -extern void zfcp_erp_lun_shutdown(struct scsi_device *, int, char *, void *); +extern void zfcp_erp_lun_reopen(struct scsi_device *, int, char *); +extern void zfcp_erp_lun_shutdown(struct scsi_device *, int, char *); extern void zfcp_erp_lun_shutdown_wait(struct scsi_device *, char *); extern int zfcp_erp_thread_setup(struct zfcp_adapter *); extern void zfcp_erp_thread_kill(struct zfcp_adapter *); @@ -149,6 +135,8 @@ extern int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *, struct zfcp_qdio_req *, extern int zfcp_qdio_open(struct zfcp_qdio *); extern void zfcp_qdio_close(struct zfcp_qdio *); extern void zfcp_qdio_siosl(struct zfcp_adapter *); +extern struct zfcp_fsf_req *zfcp_fsf_get_req(struct zfcp_qdio *, + struct qdio_buffer *); /* zfcp_scsi.c */ extern struct zfcp_data zfcp_data; diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 86fd905df48..30cf91a787a 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -174,7 +174,7 @@ static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range, if (!port->d_id) zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, - "fcrscn1", NULL); + "fcrscn1"); } read_unlock_irqrestore(&adapter->port_list_lock, flags); } @@ -215,7 +215,7 @@ static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, u64 wwpn) read_lock_irqsave(&adapter->port_list_lock, flags); list_for_each_entry(port, &adapter->port_list, list) if (port->wwpn == wwpn) { - zfcp_erp_port_forced_reopen(port, 0, "fciwwp1", req); + zfcp_erp_port_forced_reopen(port, 0, "fciwwp1"); break; } read_unlock_irqrestore(&adapter->port_list_lock, flags); @@ -251,7 +251,7 @@ void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req) (struct fsf_status_read_buffer *) fsf_req->data; unsigned int els_type = status_buffer->payload.data[0]; - zfcp_dbf_san_incoming_els(fsf_req); + zfcp_dbf_san_in_els("fciels1", fsf_req); if (els_type == ELS_PLOGI) zfcp_fc_incoming_plogi(fsf_req); else if (els_type == ELS_LOGO) @@ -360,7 +360,7 @@ void zfcp_fc_port_did_lookup(struct work_struct *work) ret = zfcp_fc_ns_gid_pn(port); if (ret) { /* could not issue gid_pn for some reason */ - zfcp_erp_adapter_reopen(port->adapter, 0, "fcgpn_1", NULL); + zfcp_erp_adapter_reopen(port->adapter, 0, "fcgpn_1"); goto out; } @@ -369,7 +369,7 @@ void zfcp_fc_port_did_lookup(struct work_struct *work) goto out; } - zfcp_erp_port_reopen(port, 0, "fcgpn_3", NULL); + zfcp_erp_port_reopen(port, 0, "fcgpn_3"); out: put_device(&port->dev); } @@ -426,7 +426,7 @@ static void zfcp_fc_adisc_handler(void *data) if (adisc->els.status) { /* request rejected or timed out */ zfcp_erp_port_forced_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, - "fcadh_1", NULL); + "fcadh_1"); goto out; } @@ -436,7 +436,7 @@ static void zfcp_fc_adisc_handler(void *data) if ((port->wwpn != adisc_resp->adisc_wwpn) || !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)) { zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, - "fcadh_2", NULL); + "fcadh_2"); goto out; } @@ -507,7 +507,7 @@ void zfcp_fc_link_test_work(struct work_struct *work) /* send of ADISC was not possible */ atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status); - zfcp_erp_port_forced_reopen(port, 0, "fcltwk1", NULL); + zfcp_erp_port_forced_reopen(port, 0, "fcltwk1"); out: put_device(&port->dev); @@ -659,7 +659,7 @@ static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft, port = zfcp_port_enqueue(adapter, acc->fp_wwpn, ZFCP_STATUS_COMMON_NOESC, d_id); if (!IS_ERR(port)) - zfcp_erp_port_reopen(port, 0, "fcegpf1", NULL); + zfcp_erp_port_reopen(port, 0, "fcegpf1"); else if (PTR_ERR(port) != -EEXIST) ret = PTR_ERR(port); } @@ -671,7 +671,7 @@ static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft, write_unlock_irqrestore(&adapter->port_list_lock, flags); list_for_each_entry_safe(port, tmp, &remove_lh, list) { - zfcp_erp_port_shutdown(port, 0, "fcegpf2", NULL); + zfcp_erp_port_shutdown(port, 0, "fcegpf2"); zfcp_device_unregister(&port->dev, &zfcp_sysfs_port_attrs); } diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index be031745714..60ff9d172c7 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -23,7 +23,7 @@ static void zfcp_fsf_request_timeout_handler(unsigned long data) struct zfcp_adapter *adapter = (struct zfcp_adapter *) data; zfcp_qdio_siosl(adapter); zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, - "fsrth_1", NULL); + "fsrth_1"); } static void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req, @@ -65,7 +65,7 @@ static void zfcp_fsf_class_not_supp(struct zfcp_fsf_req *req) { dev_err(&req->adapter->ccw_device->dev, "FCP device not " "operational because of an unsupported FC class\n"); - zfcp_erp_adapter_shutdown(req->adapter, 0, "fscns_1", req); + zfcp_erp_adapter_shutdown(req->adapter, 0, "fscns_1"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; } @@ -98,7 +98,7 @@ static void zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *req) read_lock_irqsave(&adapter->port_list_lock, flags); list_for_each_entry(port, &adapter->port_list, list) if (port->d_id == d_id) { - zfcp_erp_port_reopen(port, 0, "fssrpc1", req); + zfcp_erp_port_reopen(port, 0, "fssrpc1"); break; } read_unlock_irqrestore(&adapter->port_list_lock, flags); @@ -211,13 +211,13 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req) struct fsf_status_read_buffer *sr_buf = req->data; if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) { - zfcp_dbf_hba_fsf_unsol("dism", adapter->dbf, sr_buf); + zfcp_dbf_hba_fsf_uss("fssrh_1", req); mempool_free(sr_buf, adapter->pool.status_read_data); zfcp_fsf_req_free(req); return; } - zfcp_dbf_hba_fsf_unsol("read", adapter->dbf, sr_buf); + zfcp_dbf_hba_fsf_uss("fssrh_2", req); switch (sr_buf->status_type) { case FSF_STATUS_READ_PORT_CLOSED: @@ -232,7 +232,7 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req) dev_warn(&adapter->ccw_device->dev, "The error threshold for checksum statistics " "has been exceeded\n"); - zfcp_dbf_hba_berr(adapter->dbf, req); + zfcp_dbf_hba_bit_err("fssrh_3", req); break; case FSF_STATUS_READ_LINK_DOWN: zfcp_fsf_status_read_link_down(req); @@ -247,7 +247,7 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req) zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | ZFCP_STATUS_COMMON_ERP_FAILED, - "fssrh_2", req); + "fssrh_2"); zfcp_fc_enqueue_event(adapter, FCH_EVT_LINKUP, 0); break; @@ -287,7 +287,7 @@ static void zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *req) "The FCP adapter reported a problem " "that cannot be recovered\n"); zfcp_qdio_siosl(req->adapter); - zfcp_erp_adapter_shutdown(req->adapter, 0, "fsfsqe1", req); + zfcp_erp_adapter_shutdown(req->adapter, 0, "fsfsqe1"); break; } /* all non-return stats set FSFREQ_ERROR*/ @@ -304,7 +304,7 @@ static void zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *req) dev_err(&req->adapter->ccw_device->dev, "The FCP adapter does not recognize the command 0x%x\n", req->qtcb->header.fsf_command); - zfcp_erp_adapter_shutdown(req->adapter, 0, "fsfse_1", req); + zfcp_erp_adapter_shutdown(req->adapter, 0, "fsfse_1"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_ADAPTER_STATUS_AVAILABLE: @@ -335,17 +335,17 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req) "QTCB version 0x%x not supported by FCP adapter " "(0x%x to 0x%x)\n", FSF_QTCB_CURRENT_VERSION, psq->word[0], psq->word[1]); - zfcp_erp_adapter_shutdown(adapter, 0, "fspse_1", req); + zfcp_erp_adapter_shutdown(adapter, 0, "fspse_1"); break; case FSF_PROT_ERROR_STATE: case FSF_PROT_SEQ_NUMB_ERROR: - zfcp_erp_adapter_reopen(adapter, 0, "fspse_2", req); + zfcp_erp_adapter_reopen(adapter, 0, "fspse_2"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_PROT_UNSUPP_QTCB_TYPE: dev_err(&adapter->ccw_device->dev, "The QTCB type is not supported by the FCP adapter\n"); - zfcp_erp_adapter_shutdown(adapter, 0, "fspse_3", req); + zfcp_erp_adapter_shutdown(adapter, 0, "fspse_3"); break; case FSF_PROT_HOST_CONNECTION_INITIALIZING: atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, @@ -355,12 +355,12 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req) dev_err(&adapter->ccw_device->dev, "0x%Lx is an ambiguous request identifier\n", (unsigned long long)qtcb->bottom.support.req_handle); - zfcp_erp_adapter_shutdown(adapter, 0, "fspse_4", req); + zfcp_erp_adapter_shutdown(adapter, 0, "fspse_4"); break; case FSF_PROT_LINK_DOWN: zfcp_fsf_link_down_info_eval(req, &psq->link_down_info); /* go through reopen to flush pending requests */ - zfcp_erp_adapter_reopen(adapter, 0, "fspse_6", req); + zfcp_erp_adapter_reopen(adapter, 0, "fspse_6"); break; case FSF_PROT_REEST_QUEUE: /* All ports should be marked as ready to run again */ @@ -369,14 +369,14 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req) zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | ZFCP_STATUS_COMMON_ERP_FAILED, - "fspse_8", req); + "fspse_8"); break; default: dev_err(&adapter->ccw_device->dev, "0x%x is not a valid transfer protocol status\n", qtcb->prefix.prot_status); zfcp_qdio_siosl(adapter); - zfcp_erp_adapter_shutdown(adapter, 0, "fspse_9", req); + zfcp_erp_adapter_shutdown(adapter, 0, "fspse_9"); } req->status |= ZFCP_STATUS_FSFREQ_ERROR; } @@ -482,7 +482,7 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req) dev_err(&adapter->ccw_device->dev, "Unknown or unsupported arbitrated loop " "fibre channel topology detected\n"); - zfcp_erp_adapter_shutdown(adapter, 0, "fsece_1", req); + zfcp_erp_adapter_shutdown(adapter, 0, "fsece_1"); return -EIO; } @@ -518,7 +518,7 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req) "FCP adapter maximum QTCB size (%d bytes) " "is too small\n", bottom->max_qtcb_size); - zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh1", req); + zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh1"); return; } atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, @@ -536,7 +536,7 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req) &qtcb->header.fsf_status_qual.link_down_info); break; default: - zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh3", req); + zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh3"); return; } @@ -552,14 +552,14 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req) dev_err(&adapter->ccw_device->dev, "The FCP adapter only supports newer " "control block versions\n"); - zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh4", req); + zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh4"); return; } if (FSF_QTCB_CURRENT_VERSION > bottom->high_qtcb_version) { dev_err(&adapter->ccw_device->dev, "The FCP adapter only supports older " "control block versions\n"); - zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh5", req); + zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh5"); } } @@ -700,7 +700,7 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *req) del_timer(&req->timer); /* lookup request again, list might have changed */ zfcp_reqlist_find_rm(adapter->req_list, req_id); - zfcp_erp_adapter_reopen(adapter, 0, "fsrs__1", req); + zfcp_erp_adapter_reopen(adapter, 0, "fsrs__1"); return -EIO; } @@ -754,10 +754,11 @@ int zfcp_fsf_status_read(struct zfcp_qdio *qdio) goto out; failed_req_send: + req->data = NULL; mempool_free(sr_buf, adapter->pool.status_read_data); failed_buf: + zfcp_dbf_hba_fsf_uss("fssr__1", req); zfcp_fsf_req_free(req); - zfcp_dbf_hba_fsf_unsol("fail", adapter->dbf, NULL); out: spin_unlock_irq(&qdio->req_q_lock); return retval; @@ -776,14 +777,13 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req) case FSF_PORT_HANDLE_NOT_VALID: if (fsq->word[0] == fsq->word[1]) { zfcp_erp_adapter_reopen(zfcp_sdev->port->adapter, 0, - "fsafch1", req); + "fsafch1"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; } break; case FSF_LUN_HANDLE_NOT_VALID: if (fsq->word[0] == fsq->word[1]) { - zfcp_erp_port_reopen(zfcp_sdev->port, 0, "fsafch2", - req); + zfcp_erp_port_reopen(zfcp_sdev->port, 0, "fsafch2"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; } break; @@ -794,14 +794,13 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req) zfcp_erp_set_port_status(zfcp_sdev->port, ZFCP_STATUS_COMMON_ACCESS_BOXED); zfcp_erp_port_reopen(zfcp_sdev->port, - ZFCP_STATUS_COMMON_ERP_FAILED, "fsafch3", - req); + ZFCP_STATUS_COMMON_ERP_FAILED, "fsafch3"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_LUN_BOXED: zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_ACCESS_BOXED); zfcp_erp_lun_reopen(sdev, ZFCP_STATUS_COMMON_ERP_FAILED, - "fsafch4", req); + "fsafch4"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_ADAPTER_STATUS_AVAILABLE: @@ -851,7 +850,7 @@ struct zfcp_fsf_req *zfcp_fsf_abort_fcp_cmnd(struct scsi_cmnd *scmnd) zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); - req->data = zfcp_sdev; + req->data = sdev; req->handler = zfcp_fsf_abort_fcp_command_handler; req->qtcb->header.lun_handle = zfcp_sdev->lun_handle; req->qtcb->header.port_handle = zfcp_sdev->port->handle; @@ -882,7 +881,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req) switch (header->fsf_status) { case FSF_GOOD: - zfcp_dbf_san_ct_response(req); + zfcp_dbf_san_res("fsscth1", req); ct->status = 0; break; case FSF_SERVICE_CLASS_NOT_SUPPORTED: @@ -902,7 +901,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req) req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_PORT_HANDLE_NOT_VALID: - zfcp_erp_adapter_reopen(adapter, 0, "fsscth1", req); + zfcp_erp_adapter_reopen(adapter, 0, "fsscth1"); /* fall through */ case FSF_GENERIC_COMMAND_REJECTED: case FSF_PAYLOAD_SIZE_MISMATCH: @@ -1025,7 +1024,7 @@ int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port, req->qtcb->header.port_handle = wka_port->handle; req->data = ct; - zfcp_dbf_san_ct_request(req, wka_port->d_id); + zfcp_dbf_san_req("fssct_1", req, wka_port->d_id); ret = zfcp_fsf_req_send(req); if (ret) @@ -1053,7 +1052,7 @@ static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req) switch (header->fsf_status) { case FSF_GOOD: - zfcp_dbf_san_els_response(req); + zfcp_dbf_san_res("fsselh1", req); send_els->status = 0; break; case FSF_SERVICE_CLASS_NOT_SUPPORTED: @@ -1127,7 +1126,7 @@ int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id, req->handler = zfcp_fsf_send_els_handler; req->data = els; - zfcp_dbf_san_els_request(req); + zfcp_dbf_san_req("fssels1", req, d_id); ret = zfcp_fsf_req_send(req); if (ret) @@ -1448,7 +1447,7 @@ static void zfcp_fsf_close_port_handler(struct zfcp_fsf_req *req) switch (req->qtcb->header.fsf_status) { case FSF_PORT_HANDLE_NOT_VALID: - zfcp_erp_adapter_reopen(port->adapter, 0, "fscph_1", req); + zfcp_erp_adapter_reopen(port->adapter, 0, "fscph_1"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_ADAPTER_STATUS_AVAILABLE: @@ -1580,7 +1579,7 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req) if (req->qtcb->header.fsf_status == FSF_PORT_HANDLE_NOT_VALID) { req->status |= ZFCP_STATUS_FSFREQ_ERROR; - zfcp_erp_adapter_reopen(wka_port->adapter, 0, "fscwph1", req); + zfcp_erp_adapter_reopen(wka_port->adapter, 0, "fscwph1"); } wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; @@ -1638,7 +1637,7 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req) switch (header->fsf_status) { case FSF_PORT_HANDLE_NOT_VALID: - zfcp_erp_adapter_reopen(port->adapter, 0, "fscpph1", req); + zfcp_erp_adapter_reopen(port->adapter, 0, "fscpph1"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_ACCESS_DENIED: @@ -1654,7 +1653,7 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req) &sdev_to_zfcp(sdev)->status); zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ACCESS_BOXED); zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, - "fscpph2", req); + "fscpph2"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_ADAPTER_STATUS_AVAILABLE: @@ -1743,7 +1742,7 @@ static void zfcp_fsf_open_lun_handler(struct zfcp_fsf_req *req) switch (header->fsf_status) { case FSF_PORT_HANDLE_NOT_VALID: - zfcp_erp_adapter_reopen(adapter, 0, "fsouh_1", req); + zfcp_erp_adapter_reopen(adapter, 0, "fsouh_1"); /* fall through */ case FSF_LUN_ALREADY_OPEN: break; @@ -1755,8 +1754,7 @@ static void zfcp_fsf_open_lun_handler(struct zfcp_fsf_req *req) zfcp_erp_set_port_status(zfcp_sdev->port, ZFCP_STATUS_COMMON_ACCESS_BOXED); zfcp_erp_port_reopen(zfcp_sdev->port, - ZFCP_STATUS_COMMON_ERP_FAILED, "fsouh_2", - req); + ZFCP_STATUS_COMMON_ERP_FAILED, "fsouh_2"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_LUN_SHARING_VIOLATION: @@ -1852,20 +1850,18 @@ static void zfcp_fsf_close_lun_handler(struct zfcp_fsf_req *req) switch (req->qtcb->header.fsf_status) { case FSF_PORT_HANDLE_NOT_VALID: - zfcp_erp_adapter_reopen(zfcp_sdev->port->adapter, 0, "fscuh_1", - req); + zfcp_erp_adapter_reopen(zfcp_sdev->port->adapter, 0, "fscuh_1"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_LUN_HANDLE_NOT_VALID: - zfcp_erp_port_reopen(zfcp_sdev->port, 0, "fscuh_2", req); + zfcp_erp_port_reopen(zfcp_sdev->port, 0, "fscuh_2"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_PORT_BOXED: zfcp_erp_set_port_status(zfcp_sdev->port, ZFCP_STATUS_COMMON_ACCESS_BOXED); zfcp_erp_port_reopen(zfcp_sdev->port, - ZFCP_STATUS_COMMON_ERP_FAILED, "fscuh_3", - req); + ZFCP_STATUS_COMMON_ERP_FAILED, "fscuh_3"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_ADAPTER_STATUS_AVAILABLE: @@ -2002,13 +1998,12 @@ static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req) switch (header->fsf_status) { case FSF_HANDLE_MISMATCH: case FSF_PORT_HANDLE_NOT_VALID: - zfcp_erp_adapter_reopen(zfcp_sdev->port->adapter, 0, "fssfch1", - req); + zfcp_erp_adapter_reopen(zfcp_sdev->port->adapter, 0, "fssfch1"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_FCPLUN_NOT_VALID: case FSF_LUN_HANDLE_NOT_VALID: - zfcp_erp_port_reopen(zfcp_sdev->port, 0, "fssfch2", req); + zfcp_erp_port_reopen(zfcp_sdev->port, 0, "fssfch2"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_SERVICE_CLASS_NOT_SUPPORTED: @@ -2026,7 +2021,7 @@ static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req) (unsigned long long)zfcp_scsi_dev_lun(sdev), (unsigned long long)zfcp_sdev->port->wwpn); zfcp_erp_adapter_shutdown(zfcp_sdev->port->adapter, 0, - "fssfch3", req); + "fssfch3"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_CMND_LENGTH_NOT_VALID: @@ -2037,21 +2032,20 @@ static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req) (unsigned long long)zfcp_scsi_dev_lun(sdev), (unsigned long long)zfcp_sdev->port->wwpn); zfcp_erp_adapter_shutdown(zfcp_sdev->port->adapter, 0, - "fssfch4", req); + "fssfch4"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_PORT_BOXED: zfcp_erp_set_port_status(zfcp_sdev->port, ZFCP_STATUS_COMMON_ACCESS_BOXED); zfcp_erp_port_reopen(zfcp_sdev->port, - ZFCP_STATUS_COMMON_ERP_FAILED, "fssfch5", - req); + ZFCP_STATUS_COMMON_ERP_FAILED, "fssfch5"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_LUN_BOXED: zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_ACCESS_BOXED); zfcp_erp_lun_reopen(sdev, ZFCP_STATUS_COMMON_ERP_FAILED, - "fssfch6", req); + "fssfch6"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_ADAPTER_STATUS_AVAILABLE: @@ -2069,8 +2063,6 @@ static void zfcp_fsf_fcp_cmnd_handler(struct zfcp_fsf_req *req) struct fcp_resp_with_ext *fcp_rsp; unsigned long flags; - zfcp_fsf_fcp_handler_common(req); - read_lock_irqsave(&req->adapter->abort_lock, flags); scpnt = req->data; @@ -2079,6 +2071,8 @@ static void zfcp_fsf_fcp_cmnd_handler(struct zfcp_fsf_req *req) return; } + zfcp_fsf_fcp_handler_common(req); + if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR)) { set_host_byte(scpnt, DID_TRANSPORT_DISRUPTED); goto skip_fsfstatus; @@ -2104,7 +2098,7 @@ static void zfcp_fsf_fcp_cmnd_handler(struct zfcp_fsf_req *req) skip_fsfstatus: zfcp_fsf_req_trace(req, scpnt); - zfcp_dbf_scsi_result(req->adapter->dbf, scpnt, req); + zfcp_dbf_scsi_result(scpnt, req); scpnt->host_scribble = NULL; (scpnt->scsi_done) (scpnt); @@ -2170,12 +2164,13 @@ int zfcp_fsf_fcp_cmnd(struct scsi_cmnd *scsi_cmnd) struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; struct zfcp_qdio *qdio = adapter->qdio; struct fsf_qtcb_bottom_io *io; + unsigned long flags; if (unlikely(!(atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_UNBLOCKED))) return -EBUSY; - spin_lock(&qdio->req_q_lock); + spin_lock_irqsave(&qdio->req_q_lock, flags); if (atomic_read(&qdio->req_q_free) <= 0) { atomic_inc(&qdio->req_q_full); goto out; @@ -2239,7 +2234,7 @@ failed_scsi_cmnd: zfcp_fsf_req_free(req); scsi_cmnd->host_scribble = NULL; out: - spin_unlock(&qdio->req_q_lock); + spin_unlock_irqrestore(&qdio->req_q_lock, flags); return retval; } @@ -2419,3 +2414,12 @@ void zfcp_fsf_reqid_check(struct zfcp_qdio *qdio, int sbal_idx) break; } } + +struct zfcp_fsf_req *zfcp_fsf_get_req(struct zfcp_qdio *qdio, + struct qdio_buffer *sbal) +{ + struct qdio_buffer_element *sbale = &sbal->element[0]; + u64 req_id = (unsigned long) sbale->addr; + + return zfcp_reqlist_find(qdio->adapter->req_list, req_id); +} diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index a0554beb417..8da5ed644c2 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -41,7 +41,7 @@ static void zfcp_qdio_handler_error(struct zfcp_qdio *qdio, char *id, zfcp_qdio_siosl(adapter); zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | - ZFCP_STATUS_COMMON_ERP_FAILED, id, NULL); + ZFCP_STATUS_COMMON_ERP_FAILED, id); } static void zfcp_qdio_zero_sbals(struct qdio_buffer *sbal[], int first, int cnt) @@ -74,7 +74,6 @@ static void zfcp_qdio_int_req(struct ccw_device *cdev, unsigned int qdio_err, struct zfcp_qdio *qdio = (struct zfcp_qdio *) parm; if (unlikely(qdio_err)) { - zfcp_dbf_hba_qdio(qdio->adapter->dbf, qdio_err, idx, count); zfcp_qdio_handler_error(qdio, "qdireq1", qdio_err); return; } @@ -97,7 +96,6 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err, int sbal_idx, sbal_no; if (unlikely(qdio_err)) { - zfcp_dbf_hba_qdio(qdio->adapter->dbf, qdio_err, idx, count); zfcp_qdio_handler_error(qdio, "qdires1", qdio_err); return; } @@ -116,7 +114,7 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err, * put SBALs back to response queue */ if (do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, idx, count)) - zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdires2", NULL); + zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdires2"); } static struct qdio_buffer_element * @@ -236,7 +234,7 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) if (!ret) { atomic_inc(&qdio->req_q_full); /* assume hanging outbound queue, try queue recovery */ - zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1", NULL); + zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1"); } spin_lock_irq(&qdio->req_q_lock); @@ -292,6 +290,8 @@ static void zfcp_qdio_setup_init_data(struct qdio_initialize *id, id->int_parm = (unsigned long) qdio; id->input_sbal_addr_array = (void **) (qdio->res_q); id->output_sbal_addr_array = (void **) (qdio->req_q); + id->scan_threshold = + QDIO_MAX_BUFFERS_PER_Q - ZFCP_QDIO_MAX_SBALS_PER_REQ * 2; } /** @@ -309,6 +309,7 @@ static int zfcp_qdio_allocate(struct zfcp_qdio *qdio) return -ENOMEM; zfcp_qdio_setup_init_data(&init_data, qdio); + init_waitqueue_head(&qdio->req_q_wq); return qdio_allocate(&init_data); } @@ -393,6 +394,7 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio) /* set index of first avalable SBALS / number of available SBALS */ qdio->req_q_idx = 0; atomic_set(&qdio->req_q_free, QDIO_MAX_BUFFERS_PER_Q); + atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &qdio->adapter->status); return 0; diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 50286d8707f..ddb5800823a 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -30,6 +30,10 @@ module_param_named(dif, enable_dif, bool, 0600); MODULE_PARM_DESC(dif, "Enable DIF/DIX data integrity support"); #endif +static bool allow_lun_scan = 1; +module_param(allow_lun_scan, bool, 0600); +MODULE_PARM_DESC(allow_lun_scan, "For NPIV, scan and attach all storage LUNs"); + static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth, int reason) { @@ -68,31 +72,26 @@ static int zfcp_scsi_slave_configure(struct scsi_device *sdp) static void zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result) { - struct zfcp_adapter *adapter = - (struct zfcp_adapter *) scpnt->device->host->hostdata[0]; - set_host_byte(scpnt, result); - zfcp_dbf_scsi_fail_send(adapter->dbf, scpnt); + zfcp_dbf_scsi_fail_send(scpnt); scpnt->scsi_done(scpnt); } -static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt, - void (*done) (struct scsi_cmnd *)) +static +int zfcp_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scpnt) { struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device); - struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; struct fc_rport *rport = starget_to_rport(scsi_target(scpnt->device)); int status, scsi_result, ret; /* reset the status for this request */ scpnt->result = 0; scpnt->host_scribble = NULL; - scpnt->scsi_done = done; scsi_result = fc_remote_port_chkready(rport); if (unlikely(scsi_result)) { scpnt->result = scsi_result; - zfcp_dbf_scsi_fail_send(adapter->dbf, scpnt); + zfcp_dbf_scsi_fail_send(scpnt); scpnt->scsi_done(scpnt); return 0; } @@ -135,6 +134,7 @@ static int zfcp_scsi_slave_alloc(struct scsi_device *sdev) struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_port *port; struct zfcp_unit *unit; + int npiv = adapter->connection_features & FSF_FEATURE_NPIV_MODE; port = zfcp_get_port_by_wwpn(adapter, rport->port_name); if (!port) @@ -144,7 +144,7 @@ static int zfcp_scsi_slave_alloc(struct scsi_device *sdev) if (unit) put_device(&unit->dev); - if (!unit && !(adapter->connection_features & FSF_FEATURE_NPIV_MODE)) { + if (!unit && !(allow_lun_scan && npiv)) { put_device(&port->dev); return -ENXIO; } @@ -159,7 +159,7 @@ static int zfcp_scsi_slave_alloc(struct scsi_device *sdev) spin_lock_init(&zfcp_sdev->latencies.lock); zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_RUNNING); - zfcp_erp_lun_reopen(sdev, 0, "scsla_1", NULL); + zfcp_erp_lun_reopen(sdev, 0, "scsla_1"); zfcp_erp_wait(port->adapter); return 0; @@ -183,8 +183,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) old_req = zfcp_reqlist_find(adapter->req_list, old_reqid); if (!old_req) { write_unlock_irqrestore(&adapter->abort_lock, flags); - zfcp_dbf_scsi_abort("lte1", adapter->dbf, scpnt, NULL, - old_reqid); + zfcp_dbf_scsi_abort("abrt_or", scpnt, NULL); return FAILED; /* completion could be in progress */ } old_req->data = NULL; @@ -199,29 +198,32 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) zfcp_erp_wait(adapter); ret = fc_block_scsi_eh(scpnt); - if (ret) + if (ret) { + zfcp_dbf_scsi_abort("abrt_bl", scpnt, NULL); return ret; + } if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) { - zfcp_dbf_scsi_abort("nres", adapter->dbf, scpnt, NULL, - old_reqid); + zfcp_dbf_scsi_abort("abrt_ru", scpnt, NULL); return SUCCESS; } } - if (!abrt_req) + if (!abrt_req) { + zfcp_dbf_scsi_abort("abrt_ar", scpnt, NULL); return FAILED; + } wait_for_completion(&abrt_req->completion); if (abrt_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) - dbf_tag = "okay"; + dbf_tag = "abrt_ok"; else if (abrt_req->status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) - dbf_tag = "lte2"; + dbf_tag = "abrt_nn"; else { - dbf_tag = "fail"; + dbf_tag = "abrt_fa"; retval = FAILED; } - zfcp_dbf_scsi_abort(dbf_tag, adapter->dbf, scpnt, abrt_req, old_reqid); + zfcp_dbf_scsi_abort(dbf_tag, scpnt, abrt_req); zfcp_fsf_req_free(abrt_req); return retval; } @@ -281,7 +283,7 @@ static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt) struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; int ret; - zfcp_erp_adapter_reopen(adapter, 0, "schrh_1", scpnt); + zfcp_erp_adapter_reopen(adapter, 0, "schrh_1"); zfcp_erp_wait(adapter); ret = fc_block_scsi_eh(scpnt); if (ret) @@ -519,7 +521,7 @@ static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport) port = zfcp_get_port_by_wwpn(adapter, rport->port_name); if (port) { - zfcp_erp_port_forced_reopen(port, 0, "sctrpi1", NULL); + zfcp_erp_port_forced_reopen(port, 0, "sctrpi1"); put_device(&port->dev); } } diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 2f2c54f4718..cdc4ff78a7b 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -105,8 +105,7 @@ static ssize_t zfcp_sysfs_port_failed_store(struct device *dev, return -EINVAL; zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_RUNNING); - zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, "sypfai2", - NULL); + zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, "sypfai2"); zfcp_erp_wait(port->adapter); return count; @@ -148,7 +147,7 @@ static ssize_t zfcp_sysfs_unit_failed_store(struct device *dev, if (sdev) { zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_RUNNING); zfcp_erp_lun_reopen(sdev, ZFCP_STATUS_COMMON_ERP_FAILED, - "syufai2", NULL); + "syufai2"); zfcp_erp_wait(unit->port->adapter); } else zfcp_unit_scsi_scan(unit); @@ -198,7 +197,7 @@ static ssize_t zfcp_sysfs_adapter_failed_store(struct device *dev, zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, - "syafai2", NULL); + "syafai2"); zfcp_erp_wait(adapter); out: zfcp_ccw_adapter_put(adapter); @@ -256,7 +255,7 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, put_device(&port->dev); - zfcp_erp_port_shutdown(port, 0, "syprs_1", NULL); + zfcp_erp_port_shutdown(port, 0, "syprs_1"); zfcp_device_unregister(&port->dev, &zfcp_sysfs_port_attrs); out: zfcp_ccw_adapter_put(adapter); |