summaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2008-10-15 13:46:29 +0200
committerIngo Molnar <mingo@elte.hu>2008-10-15 13:46:29 +0200
commitb2aaf8f74cdc84a9182f6cabf198b7763bcb9d40 (patch)
tree53ccb1c2c14751fe69cf93102e76e97021f6df07 /drivers/s390
parent4f962d4d65923d7b722192e729840cfb79af0a5a (diff)
parent278429cff8809958d25415ba0ed32b59866ab1a8 (diff)
Merge branch 'linus' into stackprotector
Conflicts: arch/x86/kernel/Makefile include/asm-x86/pda.h
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/block/dasd.c53
-rw-r--r--drivers/s390/block/dasd_3990_erp.c17
-rw-r--r--drivers/s390/block/dasd_alias.c4
-rw-r--r--drivers/s390/block/dasd_devmap.c44
-rw-r--r--drivers/s390/block/dasd_diag.c25
-rw-r--r--drivers/s390/block/dasd_eckd.c295
-rw-r--r--drivers/s390/block/dasd_eckd.h186
-rw-r--r--drivers/s390/block/dasd_eer.c15
-rw-r--r--drivers/s390/block/dasd_fba.c23
-rw-r--r--drivers/s390/block/dasd_int.h5
-rw-r--r--drivers/s390/block/dasd_proc.c5
-rw-r--r--drivers/s390/block/dcssblk.c541
-rw-r--r--drivers/s390/block/xpram.c55
-rw-r--r--drivers/s390/char/con3215.c91
-rw-r--r--drivers/s390/char/con3270.c33
-rw-r--r--drivers/s390/char/fs3270.c43
-rw-r--r--drivers/s390/char/monreader.c78
-rw-r--r--drivers/s390/char/monwriter.c3
-rw-r--r--drivers/s390/char/raw3270.c42
-rw-r--r--drivers/s390/char/sclp.c18
-rw-r--r--drivers/s390/char/sclp_cmd.c348
-rw-r--r--drivers/s390/char/sclp_con.c29
-rw-r--r--drivers/s390/char/sclp_config.c4
-rw-r--r--drivers/s390/char/sclp_cpi_sys.c57
-rw-r--r--drivers/s390/char/sclp_quiesce.c8
-rw-r--r--drivers/s390/char/sclp_rw.c2
-rw-r--r--drivers/s390/char/sclp_sdias.c4
-rw-r--r--drivers/s390/char/sclp_tty.c261
-rw-r--r--drivers/s390/char/sclp_tty.h53
-rw-r--r--drivers/s390/char/sclp_vt220.c88
-rw-r--r--drivers/s390/char/tape_34xx.c12
-rw-r--r--drivers/s390/char/tape_3590.c153
-rw-r--r--drivers/s390/char/tape_block.c2
-rw-r--r--drivers/s390/char/tape_char.c14
-rw-r--r--drivers/s390/char/tape_class.c7
-rw-r--r--drivers/s390/char/tape_core.c36
-rw-r--r--drivers/s390/char/tape_proc.c2
-rw-r--r--drivers/s390/char/tape_std.c15
-rw-r--r--drivers/s390/char/tty3270.c9
-rw-r--r--drivers/s390/char/vmcp.c41
-rw-r--r--drivers/s390/char/vmlogrdr.c42
-rw-r--r--drivers/s390/char/vmur.c38
-rw-r--r--drivers/s390/char/vmwatchdog.c23
-rw-r--r--drivers/s390/char/zcore.c132
-rw-r--r--drivers/s390/cio/Makefile6
-rw-r--r--drivers/s390/cio/airq.c45
-rw-r--r--drivers/s390/cio/blacklist.c11
-rw-r--r--drivers/s390/cio/ccwgroup.c25
-rw-r--r--drivers/s390/cio/chp.c126
-rw-r--r--drivers/s390/cio/chp.h15
-rw-r--r--drivers/s390/cio/chsc.c388
-rw-r--r--drivers/s390/cio/chsc.h28
-rw-r--r--drivers/s390/cio/chsc_sch.c820
-rw-r--r--drivers/s390/cio/chsc_sch.h13
-rw-r--r--drivers/s390/cio/cio.c341
-rw-r--r--drivers/s390/cio/cio.h18
-rw-r--r--drivers/s390/cio/cmf.c20
-rw-r--r--drivers/s390/cio/css.c320
-rw-r--r--drivers/s390/cio/css.h49
-rw-r--r--drivers/s390/cio/device.c612
-rw-r--r--drivers/s390/cio/device.h10
-rw-r--r--drivers/s390/cio/device_fsm.c248
-rw-r--r--drivers/s390/cio/device_id.c16
-rw-r--r--drivers/s390/cio/device_ops.c136
-rw-r--r--drivers/s390/cio/device_pgid.c26
-rw-r--r--drivers/s390/cio/device_status.c133
-rw-r--r--drivers/s390/cio/fcx.c350
-rw-r--r--drivers/s390/cio/idset.c8
-rw-r--r--drivers/s390/cio/idset.h2
-rw-r--r--drivers/s390/cio/io_sch.h66
-rw-r--r--drivers/s390/cio/ioasm.h51
-rw-r--r--drivers/s390/cio/isc.c68
-rw-r--r--drivers/s390/cio/itcw.c327
-rw-r--r--drivers/s390/cio/qdio.c3934
-rw-r--r--drivers/s390/cio/qdio.h842
-rw-r--r--drivers/s390/cio/qdio_debug.c240
-rw-r--r--drivers/s390/cio/qdio_debug.h91
-rw-r--r--drivers/s390/cio/qdio_main.c1769
-rw-r--r--drivers/s390/cio/qdio_perf.c151
-rw-r--r--drivers/s390/cio/qdio_perf.h54
-rw-r--r--drivers/s390/cio/qdio_setup.c506
-rw-r--r--drivers/s390/cio/qdio_thinint.c384
-rw-r--r--drivers/s390/cio/schid.h26
-rw-r--r--drivers/s390/cio/scsw.c843
-rw-r--r--drivers/s390/crypto/ap_bus.c67
-rw-r--r--drivers/s390/crypto/ap_bus.h2
-rw-r--r--drivers/s390/crypto/zcrypt_api.c27
-rw-r--r--drivers/s390/crypto/zcrypt_api.h28
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c4
-rw-r--r--drivers/s390/crypto/zcrypt_error.h6
-rw-r--r--drivers/s390/crypto/zcrypt_pcica.c3
-rw-r--r--drivers/s390/crypto/zcrypt_pcicc.c15
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c69
-rw-r--r--drivers/s390/kvm/Makefile2
-rw-r--r--drivers/s390/kvm/kvm_virtio.c49
-rw-r--r--drivers/s390/net/claw.c2241
-rw-r--r--drivers/s390/net/claw.h2
-rw-r--r--drivers/s390/net/ctcm_dbug.c29
-rw-r--r--drivers/s390/net/ctcm_dbug.h39
-rw-r--r--drivers/s390/net/ctcm_fsms.c466
-rw-r--r--drivers/s390/net/ctcm_main.c562
-rw-r--r--drivers/s390/net/ctcm_main.h42
-rw-r--r--drivers/s390/net/ctcm_mpc.c1131
-rw-r--r--drivers/s390/net/ctcm_mpc.h2
-rw-r--r--drivers/s390/net/cu3088.c2
-rw-r--r--drivers/s390/net/cu3088.h3
-rw-r--r--drivers/s390/net/lcs.c79
-rw-r--r--drivers/s390/net/netiucv.c63
-rw-r--r--drivers/s390/net/qeth_core.h28
-rw-r--r--drivers/s390/net/qeth_core_main.c195
-rw-r--r--drivers/s390/net/qeth_l2_main.c143
-rw-r--r--drivers/s390/net/qeth_l3_main.c125
-rw-r--r--drivers/s390/net/qeth_l3_sys.c2
-rw-r--r--drivers/s390/net/smsgiucv.c10
-rw-r--r--drivers/s390/s390_rdev.c2
-rw-r--r--drivers/s390/s390mach.c106
-rw-r--r--drivers/s390/s390mach.h10
-rw-r--r--drivers/s390/scsi/Makefile3
-rw-r--r--drivers/s390/scsi/zfcp_aux.c1700
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c187
-rw-r--r--drivers/s390/scsi/zfcp_cfdc.c259
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c175
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h15
-rw-r--r--drivers/s390/scsi/zfcp_def.h514
-rw-r--r--drivers/s390/scsi/zfcp_erp.c3785
-rw-r--r--drivers/s390/scsi/zfcp_ext.h309
-rw-r--r--drivers/s390/scsi/zfcp_fc.c647
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c5675
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h145
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c806
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c796
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c498
-rw-r--r--drivers/s390/scsi/zfcp_sysfs_adapter.c270
-rw-r--r--drivers/s390/scsi/zfcp_sysfs_driver.c106
-rw-r--r--drivers/s390/scsi/zfcp_sysfs_port.c295
-rw-r--r--drivers/s390/scsi/zfcp_sysfs_unit.c167
136 files changed, 16838 insertions, 21142 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 1a402568336..0a225ccda02 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -215,7 +215,7 @@ static int dasd_state_known_to_basic(struct dasd_device *device)
return rc;
}
/* register 'device' debug area, used for all DBF_DEV_XXX calls */
- device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 1,
+ device->debug_area = debug_register(dev_name(&device->cdev->dev), 1, 1,
8 * sizeof(long));
debug_register_view(device->debug_area, &debug_sprintf_view);
debug_set_level(device->debug_area, DBF_WARNING);
@@ -933,7 +933,7 @@ static void dasd_handle_killed_request(struct ccw_device *cdev,
MESSAGE(KERN_DEBUG,
"invalid status in handle_killed_request: "
"bus_id %s, status %02x",
- cdev->dev.bus_id, cqr->status);
+ dev_name(&cdev->dev), cqr->status);
return;
}
@@ -942,7 +942,7 @@ static void dasd_handle_killed_request(struct ccw_device *cdev,
device != dasd_device_from_cdev_locked(cdev) ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
return;
}
@@ -982,11 +982,11 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
break;
case -ETIMEDOUT:
printk(KERN_WARNING"%s(%s): request timed out\n",
- __func__, cdev->dev.bus_id);
+ __func__, dev_name(&cdev->dev));
break;
default:
printk(KERN_WARNING"%s(%s): unknown error %ld\n",
- __func__, cdev->dev.bus_id, PTR_ERR(irb));
+ __func__, dev_name(&cdev->dev), PTR_ERR(irb));
}
dasd_handle_killed_request(cdev, intparm);
return;
@@ -995,14 +995,14 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
now = get_clock();
DBF_EVENT(DBF_ERR, "Interrupt: bus_id %s CS/DS %04x ip %08x",
- cdev->dev.bus_id, ((irb->scsw.cstat<<8)|irb->scsw.dstat),
- (unsigned int) intparm);
+ dev_name(&cdev->dev), ((irb->scsw.cmd.cstat << 8) |
+ irb->scsw.cmd.dstat), (unsigned int) intparm);
/* check for unsolicited interrupts */
cqr = (struct dasd_ccw_req *) intparm;
- if (!cqr || ((irb->scsw.cc == 1) &&
- (irb->scsw.fctl & SCSW_FCTL_START_FUNC) &&
- (irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) ) {
+ if (!cqr || ((irb->scsw.cmd.cc == 1) &&
+ (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) &&
+ (irb->scsw.cmd.stctl & SCSW_STCTL_STATUS_PEND))) {
if (cqr && cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_QUEUED;
device = dasd_device_from_cdev_locked(cdev);
@@ -1019,13 +1019,13 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
if (!device ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
return;
}
/* Check for clear pending */
if (cqr->status == DASD_CQR_CLEAR_PENDING &&
- irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
+ irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
cqr->status = DASD_CQR_CLEARED;
dasd_device_clear_timer(device);
wake_up(&dasd_flush_wq);
@@ -1037,15 +1037,15 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
if (cqr->status != DASD_CQR_IN_IO) {
MESSAGE(KERN_DEBUG,
"invalid status: bus_id %s, status %02x",
- cdev->dev.bus_id, cqr->status);
+ dev_name(&cdev->dev), cqr->status);
return;
}
DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x for cqr %p",
- ((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr);
+ ((irb->scsw.cmd.cstat << 8) | irb->scsw.cmd.dstat), cqr);
next = NULL;
expires = 0;
- if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
- irb->scsw.cstat == 0 && !irb->esw.esw0.erw.cons) {
+ if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
+ irb->scsw.cmd.cstat == 0 && !irb->esw.esw0.erw.cons) {
/* request was completed successfully */
cqr->status = DASD_CQR_SUCCESS;
cqr->stopclk = now;
@@ -2134,14 +2134,14 @@ int dasd_generic_probe(struct ccw_device *cdev,
if (ret) {
printk(KERN_WARNING
"dasd_generic_probe: could not set ccw-device options "
- "for %s\n", cdev->dev.bus_id);
+ "for %s\n", dev_name(&cdev->dev));
return ret;
}
ret = dasd_add_sysfs_files(cdev);
if (ret) {
printk(KERN_WARNING
"dasd_generic_probe: could not add sysfs entries "
- "for %s\n", cdev->dev.bus_id);
+ "for %s\n", dev_name(&cdev->dev));
return ret;
}
cdev->handler = &dasd_int_handler;
@@ -2152,13 +2152,13 @@ int dasd_generic_probe(struct ccw_device *cdev,
* initial probe.
*/
if ((dasd_get_feature(cdev, DASD_FEATURE_INITIAL_ONLINE) > 0 ) ||
- (dasd_autodetect && dasd_busid_known(cdev->dev.bus_id) != 0))
+ (dasd_autodetect && dasd_busid_known(dev_name(&cdev->dev)) != 0))
ret = ccw_device_set_online(cdev);
if (ret)
printk(KERN_WARNING
"dasd_generic_probe: could not initially "
"online ccw-device %s; return code: %d\n",
- cdev->dev.bus_id, ret);
+ dev_name(&cdev->dev), ret);
return 0;
}
@@ -2224,7 +2224,7 @@ int dasd_generic_set_online(struct ccw_device *cdev,
printk (KERN_WARNING
"dasd_generic couldn't online device %s "
"- discipline DIAG not available\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
dasd_delete_device(device);
return -ENODEV;
}
@@ -2248,7 +2248,7 @@ int dasd_generic_set_online(struct ccw_device *cdev,
printk (KERN_WARNING
"dasd_generic couldn't online device %s "
"with discipline %s rc=%i\n",
- cdev->dev.bus_id, discipline->name, rc);
+ dev_name(&cdev->dev), discipline->name, rc);
module_put(discipline->owner);
module_put(base_discipline->owner);
dasd_delete_device(device);
@@ -2259,7 +2259,7 @@ int dasd_generic_set_online(struct ccw_device *cdev,
if (device->state <= DASD_STATE_KNOWN) {
printk (KERN_WARNING
"dasd_generic discipline not found for %s\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
rc = -ENODEV;
dasd_set_target_state(device, DASD_STATE_NEW);
if (device->block)
@@ -2267,7 +2267,7 @@ int dasd_generic_set_online(struct ccw_device *cdev,
dasd_delete_device(device);
} else
pr_debug("dasd_generic device %s found\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
/* FIXME: we have to wait for the root device but we don't want
* to wait for each single device but for all at once. */
@@ -2333,13 +2333,11 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
{
struct dasd_device *device;
struct dasd_ccw_req *cqr;
- unsigned long flags;
int ret;
- device = dasd_device_from_cdev(cdev);
+ device = dasd_device_from_cdev_locked(cdev);
if (IS_ERR(device))
return 0;
- spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
ret = 0;
switch (event) {
case CIO_GONE:
@@ -2369,7 +2367,6 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
ret = 1;
break;
}
- spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
dasd_put_device(device);
return ret;
}
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index e6700df52df..b8f9c00633f 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -1397,7 +1397,7 @@ static struct dasd_ccw_req *dasd_3990_erp_inspect_alias(
DEV_MESSAGE(KERN_ERR, cqr->startdev,
"ERP on alias device for request %p,"
" recover on base device %s", cqr,
- cqr->block->base->cdev->dev.bus_id);
+ dev_name(&cqr->block->base->cdev->dev));
}
dasd_eckd_reset_ccw_to_base_io(cqr);
erp->startdev = cqr->block->base;
@@ -1572,7 +1572,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
/* determine the address of the CCW to be restarted */
/* Imprecise ending is not set -> addr from IRB-SCSW */
- cpa = default_erp->refers->irb.scsw.cpa;
+ cpa = default_erp->refers->irb.scsw.cmd.cpa;
if (cpa == 0) {
@@ -1725,7 +1725,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
/* determine the address of the CCW to be restarted */
/* Imprecise ending is not set -> addr from IRB-SCSW */
- cpa = previous_erp->irb.scsw.cpa;
+ cpa = previous_erp->irb.scsw.cmd.cpa;
if (cpa == 0) {
@@ -2171,7 +2171,7 @@ dasd_3990_erp_control_check(struct dasd_ccw_req *erp)
{
struct dasd_device *device = erp->startdev;
- if (erp->refers->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK
+ if (erp->refers->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK
| SCHN_STAT_CHN_CTRL_CHK)) {
DEV_MESSAGE(KERN_DEBUG, device, "%s",
"channel or interface control check");
@@ -2352,9 +2352,9 @@ dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1, struct dasd_ccw_req *cqr2)
if ((cqr1->irb.esw.esw0.erw.cons == 0) &&
(cqr2->irb.esw.esw0.erw.cons == 0)) {
- if ((cqr1->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK |
+ if ((cqr1->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK |
SCHN_STAT_CHN_CTRL_CHK)) ==
- (cqr2->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK |
+ (cqr2->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK |
SCHN_STAT_CHN_CTRL_CHK)))
return 1; /* match with ifcc*/
}
@@ -2622,8 +2622,9 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
}
/* double-check if current erp/cqr was successfull */
- if ((cqr->irb.scsw.cstat == 0x00) &&
- (cqr->irb.scsw.dstat == (DEV_STAT_CHN_END|DEV_STAT_DEV_END))) {
+ if ((cqr->irb.scsw.cmd.cstat == 0x00) &&
+ (cqr->irb.scsw.cmd.dstat ==
+ (DEV_STAT_CHN_END | DEV_STAT_DEV_END))) {
DEV_MESSAGE(KERN_DEBUG, device,
"ERP called for successful request %p"
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
index 2d8df0b3053..20676cdef4a 100644
--- a/drivers/s390/block/dasd_alias.c
+++ b/drivers/s390/block/dasd_alias.c
@@ -91,7 +91,8 @@ static struct alias_pav_group *_find_group(struct alias_lcu *lcu,
else
search_unit_addr = uid->base_unit_addr;
list_for_each_entry(pos, &lcu->grouplist, group) {
- if (pos->uid.base_unit_addr == search_unit_addr)
+ if (pos->uid.base_unit_addr == search_unit_addr &&
+ !strncmp(pos->uid.vduit, uid->vduit, sizeof(uid->vduit)))
return pos;
};
return NULL;
@@ -332,6 +333,7 @@ static int _add_device_to_lcu(struct alias_lcu *lcu,
group->uid.base_unit_addr = uid->real_unit_addr;
else
group->uid.base_unit_addr = uid->base_unit_addr;
+ memcpy(group->uid.vduit, uid->vduit, sizeof(uid->vduit));
INIT_LIST_HEAD(&group->group);
INIT_LIST_HEAD(&group->baselist);
INIT_LIST_HEAD(&group->aliaslist);
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index d774e79476f..921443b01d1 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -515,9 +515,9 @@ dasd_devmap_from_cdev(struct ccw_device *cdev)
{
struct dasd_devmap *devmap;
- devmap = dasd_find_busid(cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&cdev->dev));
if (IS_ERR(devmap))
- devmap = dasd_add_busid(cdev->dev.bus_id,
+ devmap = dasd_add_busid(dev_name(&cdev->dev),
DASD_FEATURE_DEFAULT);
return devmap;
}
@@ -584,7 +584,7 @@ dasd_delete_device(struct dasd_device *device)
unsigned long flags;
/* First remove device pointer from devmap. */
- devmap = dasd_find_busid(device->cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&device->cdev->dev));
BUG_ON(IS_ERR(devmap));
spin_lock(&dasd_devmap_lock);
if (devmap->device != device) {
@@ -674,7 +674,7 @@ dasd_ro_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
int ro_flag;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
if (!IS_ERR(devmap))
ro_flag = (devmap->features & DASD_FEATURE_READONLY) != 0;
else
@@ -723,7 +723,7 @@ dasd_erplog_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
int erplog;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
if (!IS_ERR(devmap))
erplog = (devmap->features & DASD_FEATURE_ERPLOG) != 0;
else
@@ -770,7 +770,7 @@ dasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
int use_diag;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
if (!IS_ERR(devmap))
use_diag = (devmap->features & DASD_FEATURE_USEDIAG) != 0;
else
@@ -876,7 +876,7 @@ dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
int alias;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
spin_lock(&dasd_devmap_lock);
if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
spin_unlock(&dasd_devmap_lock);
@@ -899,7 +899,7 @@ dasd_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
char *vendor;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
spin_lock(&dasd_devmap_lock);
if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0)
vendor = devmap->uid.vendor;
@@ -913,7 +913,8 @@ dasd_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR(vendor, 0444, dasd_vendor_show, NULL);
#define UID_STRLEN ( /* vendor */ 3 + 1 + /* serial */ 14 + 1 +\
- /* SSID */ 4 + 1 + /* unit addr */ 2 + 1)
+ /* SSID */ 4 + 1 + /* unit addr */ 2 + 1 +\
+ /* vduit */ 32 + 1)
static ssize_t
dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -923,7 +924,7 @@ dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
char ua_string[3];
struct dasd_uid *uid;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
spin_lock(&dasd_devmap_lock);
if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
spin_unlock(&dasd_devmap_lock);
@@ -945,8 +946,17 @@ dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
sprintf(ua_string, "%02x", uid->real_unit_addr);
break;
}
- snprintf(uid_string, sizeof(uid_string), "%s.%s.%04x.%s",
- uid->vendor, uid->serial, uid->ssid, ua_string);
+ if (strlen(uid->vduit) > 0)
+ snprintf(uid_string, sizeof(uid_string),
+ "%s.%s.%04x.%s.%s",
+ uid->vendor, uid->serial,
+ uid->ssid, ua_string,
+ uid->vduit);
+ else
+ snprintf(uid_string, sizeof(uid_string),
+ "%s.%s.%04x.%s",
+ uid->vendor, uid->serial,
+ uid->ssid, ua_string);
spin_unlock(&dasd_devmap_lock);
return snprintf(buf, PAGE_SIZE, "%s\n", uid_string);
}
@@ -962,7 +972,7 @@ dasd_eer_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
int eer_flag;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
if (!IS_ERR(devmap) && devmap->device)
eer_flag = dasd_eer_enabled(devmap->device);
else
@@ -1024,7 +1034,7 @@ dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid)
{
struct dasd_devmap *devmap;
- devmap = dasd_find_busid(cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&cdev->dev));
if (IS_ERR(devmap))
return PTR_ERR(devmap);
spin_lock(&dasd_devmap_lock);
@@ -1047,7 +1057,7 @@ dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid)
{
struct dasd_devmap *devmap;
- devmap = dasd_find_busid(cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&cdev->dev));
if (IS_ERR(devmap))
return PTR_ERR(devmap);
@@ -1067,7 +1077,7 @@ dasd_get_feature(struct ccw_device *cdev, int feature)
{
struct dasd_devmap *devmap;
- devmap = dasd_find_busid(cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&cdev->dev));
if (IS_ERR(devmap))
return PTR_ERR(devmap);
@@ -1083,7 +1093,7 @@ dasd_set_feature(struct ccw_device *cdev, int feature, int flag)
{
struct dasd_devmap *devmap;
- devmap = dasd_find_busid(cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&cdev->dev));
if (IS_ERR(devmap))
return PTR_ERR(devmap);
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index d91df38ee4f..85fcb437105 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -333,7 +333,8 @@ dasd_diag_check_device(struct dasd_device *device)
if (IS_ERR(block)) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
"could not allocate dasd block structure");
- kfree(device->private);
+ device->private = NULL;
+ kfree(private);
return PTR_ERR(block);
}
device->block = block;
@@ -348,7 +349,8 @@ dasd_diag_check_device(struct dasd_device *device)
if (rc) {
DEV_MESSAGE(KERN_WARNING, device, "failed to retrieve device "
"information (rc=%d)", rc);
- return -ENOTSUPP;
+ rc = -EOPNOTSUPP;
+ goto out;
}
/* Figure out position of label block */
@@ -362,7 +364,8 @@ dasd_diag_check_device(struct dasd_device *device)
default:
DEV_MESSAGE(KERN_WARNING, device, "unsupported device class "
"(class=%d)", private->rdc_data.vdev_class);
- return -ENOTSUPP;
+ rc = -EOPNOTSUPP;
+ goto out;
}
DBF_DEV_EVENT(DBF_INFO, device,
@@ -379,7 +382,8 @@ dasd_diag_check_device(struct dasd_device *device)
if (label == NULL) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
"No memory to allocate initialization request");
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto out;
}
rc = 0;
end_block = 0;
@@ -403,7 +407,7 @@ dasd_diag_check_device(struct dasd_device *device)
DEV_MESSAGE(KERN_WARNING, device, "%s",
"DIAG call failed");
rc = -EOPNOTSUPP;
- goto out;
+ goto out_label;
}
mdsk_term_io(device);
if (rc == 0)
@@ -413,7 +417,7 @@ dasd_diag_check_device(struct dasd_device *device)
DEV_MESSAGE(KERN_WARNING, device, "device access failed "
"(rc=%d)", rc);
rc = -EIO;
- goto out;
+ goto out_label;
}
/* check for label block */
if (memcmp(label->label_id, DASD_DIAG_CMS1,
@@ -439,8 +443,15 @@ dasd_diag_check_device(struct dasd_device *device)
(unsigned long) (block->blocks <<
block->s2b_shift) >> 1);
}
-out:
+out_label:
free_page((long) label);
+out:
+ if (rc) {
+ device->block = NULL;
+ dasd_free_block(block);
+ device->private = NULL;
+ kfree(private);
+ }
return rc;
}
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index a0edae091b5..49f9d221e23 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -6,6 +6,8 @@
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
+ * EMC Symmetrix ioctl Copyright EMC Corporation, 2008
+ * Author.........: Nigel Hislop <hislop_nigel@emc.com>
*
*/
@@ -84,7 +86,7 @@ dasd_eckd_probe (struct ccw_device *cdev)
if (ret) {
printk(KERN_WARNING
"dasd_eckd_probe: could not set ccw-device options "
- "for %s\n", cdev->dev.bus_id);
+ "for %s\n", dev_name(&cdev->dev));
return ret;
}
ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
@@ -313,8 +315,8 @@ static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk,
memset(pfxdata, 0, sizeof(*pfxdata));
/* prefix data */
pfxdata->format = 0;
- pfxdata->base_address = basepriv->conf_data.ned1.unit_addr;
- pfxdata->base_lss = basepriv->conf_data.ned1.ID;
+ pfxdata->base_address = basepriv->ned->unit_addr;
+ pfxdata->base_lss = basepriv->ned->ID;
pfxdata->validity.define_extend = 1;
/* private uid is kept up to date, conf_data may be outdated */
@@ -536,36 +538,40 @@ dasd_eckd_cdl_reclen(int recid)
/*
* Generate device unique id that specifies the physical device.
*/
-static int
-dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid)
+static int dasd_eckd_generate_uid(struct dasd_device *device,
+ struct dasd_uid *uid)
{
struct dasd_eckd_private *private;
- struct dasd_eckd_confdata *confdata;
+ int count;
private = (struct dasd_eckd_private *) device->private;
if (!private)
return -ENODEV;
- confdata = &private->conf_data;
- if (!confdata)
+ if (!private->ned || !private->gneq)
return -ENODEV;
memset(uid, 0, sizeof(struct dasd_uid));
- memcpy(uid->vendor, confdata->ned1.HDA_manufacturer,
+ memcpy(uid->vendor, private->ned->HDA_manufacturer,
sizeof(uid->vendor) - 1);
EBCASC(uid->vendor, sizeof(uid->vendor) - 1);
- memcpy(uid->serial, confdata->ned1.HDA_location,
+ memcpy(uid->serial, private->ned->HDA_location,
sizeof(uid->serial) - 1);
EBCASC(uid->serial, sizeof(uid->serial) - 1);
- uid->ssid = confdata->neq.subsystemID;
- uid->real_unit_addr = confdata->ned1.unit_addr;
- if (confdata->ned2.sneq.flags == 0x40 &&
- confdata->ned2.sneq.format == 0x0001) {
- uid->type = confdata->ned2.sneq.sua_flags;
+ uid->ssid = private->gneq->subsystemID;
+ uid->real_unit_addr = private->ned->unit_addr;;
+ if (private->sneq) {
+ uid->type = private->sneq->sua_flags;
if (uid->type == UA_BASE_PAV_ALIAS)
- uid->base_unit_addr = confdata->ned2.sneq.base_unit_addr;
+ uid->base_unit_addr = private->sneq->base_unit_addr;
} else {
uid->type = UA_BASE_DEVICE;
}
+ if (private->vdsneq) {
+ for (count = 0; count < 16; count++) {
+ sprintf(uid->vduit+2*count, "%02x",
+ private->vdsneq->uit[count]);
+ }
+ }
return 0;
}
@@ -623,6 +629,15 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
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);
if (IS_ERR(cqr)) {
ret = PTR_ERR(cqr);
@@ -646,8 +661,62 @@ out_error:
return ret;
}
-static int
-dasd_eckd_read_conf(struct dasd_device *device)
+static int dasd_eckd_identify_conf_parts(struct dasd_eckd_private *private)
+{
+
+ struct dasd_sneq *sneq;
+ int i, count;
+
+ private->ned = NULL;
+ private->sneq = NULL;
+ private->vdsneq = NULL;
+ private->gneq = NULL;
+ count = private->conf_len / sizeof(struct dasd_sneq);
+ sneq = (struct dasd_sneq *)private->conf_data;
+ for (i = 0; i < count; ++i) {
+ if (sneq->flags.identifier == 1 && sneq->format == 1)
+ private->sneq = sneq;
+ else if (sneq->flags.identifier == 1 && sneq->format == 4)
+ private->vdsneq = (struct vd_sneq *)sneq;
+ else if (sneq->flags.identifier == 2)
+ private->gneq = (struct dasd_gneq *)sneq;
+ else if (sneq->flags.identifier == 3 && sneq->res1 == 1)
+ private->ned = (struct dasd_ned *)sneq;
+ sneq++;
+ }
+ if (!private->ned || !private->gneq) {
+ private->ned = NULL;
+ private->sneq = NULL;
+ private->vdsneq = NULL;
+ private->gneq = NULL;
+ return -EINVAL;
+ }
+ return 0;
+
+};
+
+static unsigned char dasd_eckd_path_access(void *conf_data, int conf_len)
+{
+ struct dasd_gneq *gneq;
+ int i, count, found;
+
+ count = conf_len / sizeof(*gneq);
+ gneq = (struct dasd_gneq *)conf_data;
+ found = 0;
+ for (i = 0; i < count; ++i) {
+ if (gneq->flags.identifier == 2) {
+ found = 1;
+ break;
+ }
+ gneq++;
+ }
+ if (found)
+ return ((char *)gneq)[18] & 0x07;
+ else
+ return 0;
+}
+
+static int dasd_eckd_read_conf(struct dasd_device *device)
{
void *conf_data;
int conf_len, conf_data_saved;
@@ -661,7 +730,6 @@ dasd_eckd_read_conf(struct dasd_device *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){
@@ -678,22 +746,20 @@ dasd_eckd_read_conf(struct dasd_device *device)
"data retrieved");
continue; /* no error */
}
- if (conf_len != sizeof(struct dasd_eckd_confdata)) {
- MESSAGE(KERN_WARNING,
- "sizes of configuration data mismatch"
- "%d (read) vs %ld (expected)",
- conf_len,
- sizeof(struct dasd_eckd_confdata));
- kfree(conf_data);
- continue; /* no error */
- }
/* save first valid configuration data */
- if (!conf_data_saved){
- memcpy(&private->conf_data, conf_data,
- sizeof(struct dasd_eckd_confdata));
+ if (!conf_data_saved) {
+ kfree(private->conf_data);
+ private->conf_data = conf_data;
+ private->conf_len = conf_len;
+ if (dasd_eckd_identify_conf_parts(private)) {
+ private->conf_data = NULL;
+ private->conf_len = 0;
+ kfree(conf_data);
+ continue;
+ }
conf_data_saved++;
}
- switch (((char *)conf_data)[242] & 0x07){
+ switch (dasd_eckd_path_access(conf_data, conf_len)) {
case 0x02:
path_data->npm |= lpm;
break;
@@ -701,7 +767,8 @@ dasd_eckd_read_conf(struct dasd_device *device)
path_data->ppm |= lpm;
break;
}
- kfree(conf_data);
+ if (conf_data != private->conf_data)
+ kfree(conf_data);
}
}
return 0;
@@ -952,6 +1019,7 @@ out_err2:
dasd_free_block(device->block);
device->block = NULL;
out_err1:
+ kfree(private->conf_data);
kfree(device->private);
device->private = NULL;
return rc;
@@ -959,7 +1027,17 @@ out_err1:
static void dasd_eckd_uncheck_device(struct dasd_device *device)
{
+ struct dasd_eckd_private *private;
+
+ private = (struct dasd_eckd_private *) device->private;
dasd_alias_disconnect_device_from_lcu(device);
+ private->ned = NULL;
+ private->sneq = NULL;
+ private->vdsneq = NULL;
+ private->gneq = NULL;
+ private->conf_len = 0;
+ kfree(private->conf_data);
+ private->conf_data = NULL;
}
static struct dasd_ccw_req *
@@ -1404,30 +1482,48 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
/* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
- if ((irb->scsw.dstat & mask) == mask) {
+ if ((irb->scsw.cmd.dstat & mask) == mask) {
dasd_generic_handle_state_change(device);
return;
}
/* summary unit check */
- if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && irb->ecw[7] == 0x0D) {
+ if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
+ (irb->ecw[7] == 0x0D)) {
dasd_alias_handle_summary_unit_check(device, irb);
return;
}
/* service information message SIM */
- if ((irb->ecw[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE) {
+ if (irb->esw.esw0.erw.cons && (irb->ecw[27] & DASD_SENSE_BIT_0) &&
+ ((irb->ecw[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) {
dasd_3990_erp_handle_sim(device, irb->ecw);
+ dasd_schedule_device_bh(device);
return;
}
- /* just report other unsolicited interrupts */
- DEV_MESSAGE(KERN_DEBUG, device, "%s",
- "unsolicited interrupt received");
- device->discipline->dump_sense(device, NULL, irb);
- dasd_schedule_device_bh(device);
+ if ((irb->scsw.cmd.cc == 1) &&
+ (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) &&
+ (irb->scsw.cmd.actl & SCSW_ACTL_START_PEND) &&
+ (irb->scsw.cmd.stctl & SCSW_STCTL_STATUS_PEND)) {
+ /* fake irb do nothing, they are handled elsewhere */
+ dasd_schedule_device_bh(device);
+ return;
+ }
+
+ if (!(irb->esw.esw0.erw.cons)) {
+ /* just report other unsolicited interrupts */
+ DEV_MESSAGE(KERN_ERR, device, "%s",
+ "unsolicited interrupt received");
+ } else {
+ DEV_MESSAGE(KERN_ERR, device, "%s",
+ "unsolicited interrupt received "
+ "(sense available)");
+ device->discipline->dump_sense(device, NULL, irb);
+ }
+ dasd_schedule_device_bh(device);
return;
};
@@ -1743,9 +1839,10 @@ dasd_eckd_fill_info(struct dasd_device * device,
info->characteristics_size = sizeof(struct dasd_eckd_characteristics);
memcpy(info->characteristics, &private->rdc_data,
sizeof(struct dasd_eckd_characteristics));
- info->confdata_size = sizeof(struct dasd_eckd_confdata);
- memcpy(info->configuration_data, &private->conf_data,
- sizeof(struct dasd_eckd_confdata));
+ info->confdata_size = min((unsigned long)private->conf_len,
+ sizeof(info->configuration_data));
+ memcpy(info->configuration_data, private->conf_data,
+ info->confdata_size);
return 0;
}
@@ -1988,6 +2085,103 @@ dasd_eckd_set_attrib(struct dasd_device *device, void __user *argp)
return 0;
}
+/*
+ * Issue syscall I/O to EMC Symmetrix array.
+ * CCWs are PSF and RSSD
+ */
+static int dasd_symm_io(struct dasd_device *device, void __user *argp)
+{
+ struct dasd_symmio_parms usrparm;
+ char *psf_data, *rssd_result;
+ struct dasd_ccw_req *cqr;
+ struct ccw1 *ccw;
+ int rc;
+
+ /* Copy parms from caller */
+ rc = -EFAULT;
+ if (copy_from_user(&usrparm, argp, sizeof(usrparm)))
+ goto out;
+#ifndef CONFIG_64BIT
+ /* Make sure pointers are sane even on 31 bit. */
+ if ((usrparm.psf_data >> 32) != 0 || (usrparm.rssd_result >> 32) != 0) {
+ rc = -EINVAL;
+ goto out;
+ }
+#endif
+ /* alloc I/O data area */
+ psf_data = kzalloc(usrparm.psf_data_len, GFP_KERNEL | GFP_DMA);
+ rssd_result = kzalloc(usrparm.rssd_result_len, GFP_KERNEL | GFP_DMA);
+ if (!psf_data || !rssd_result) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+
+ /* get syscall header from user space */
+ rc = -EFAULT;
+ if (copy_from_user(psf_data,
+ (void __user *)(unsigned long) usrparm.psf_data,
+ usrparm.psf_data_len))
+ goto out_free;
+
+ /* sanity check on syscall header */
+ if (psf_data[0] != 0x17 && psf_data[1] != 0xce) {
+ rc = -EINVAL;
+ goto out_free;
+ }
+
+ /* setup CCWs for PSF + RSSD */
+ cqr = dasd_smalloc_request("ECKD", 2 , 0, device);
+ if (IS_ERR(cqr)) {
+ DEV_MESSAGE(KERN_WARNING, device, "%s",
+ "Could not allocate initialization request");
+ rc = PTR_ERR(cqr);
+ goto out_free;
+ }
+
+ cqr->startdev = device;
+ cqr->memdev = device;
+ cqr->retries = 3;
+ cqr->expires = 10 * HZ;
+ cqr->buildclk = get_clock();
+ cqr->status = DASD_CQR_FILLED;
+
+ /* Build the ccws */
+ ccw = cqr->cpaddr;
+
+ /* PSF ccw */
+ ccw->cmd_code = DASD_ECKD_CCW_PSF;
+ ccw->count = usrparm.psf_data_len;
+ ccw->flags |= CCW_FLAG_CC;
+ ccw->cda = (__u32)(addr_t) psf_data;
+
+ ccw++;
+
+ /* RSSD ccw */
+ ccw->cmd_code = DASD_ECKD_CCW_RSSD;
+ ccw->count = usrparm.rssd_result_len;
+ ccw->flags = CCW_FLAG_SLI ;
+ ccw->cda = (__u32)(addr_t) rssd_result;
+
+ rc = dasd_sleep_on(cqr);
+ if (rc)
+ goto out_sfree;
+
+ rc = -EFAULT;
+ if (copy_to_user((void __user *)(unsigned long) usrparm.rssd_result,
+ rssd_result, usrparm.rssd_result_len))
+ goto out_sfree;
+ rc = 0;
+
+out_sfree:
+ dasd_sfree_request(cqr, cqr->memdev);
+out_free:
+ kfree(rssd_result);
+ kfree(psf_data);
+out:
+ DBF_DEV_EVENT(DBF_WARNING, device, "Symmetrix ioctl: rc=%d", rc);
+ return rc;
+}
+
static int
dasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp)
{
@@ -2006,6 +2200,8 @@ dasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp)
return dasd_eckd_reserve(device);
case BIODASDSLCK:
return dasd_eckd_steal_lock(device);
+ case BIODASDSYMMIO:
+ return dasd_symm_io(device, argp);
default:
return -ENOIOCTLCMD;
}
@@ -2065,14 +2261,14 @@ static void dasd_eckd_dump_sense(struct dasd_device *device,
/* dump the sense data */
len = sprintf(page, KERN_ERR PRINTK_HEADER
" I/O status report for device %s:\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" in req: %p CS: 0x%02X DS: 0x%02X\n", req,
- irb->scsw.cstat, irb->scsw.dstat);
+ irb->scsw.cmd.cstat, irb->scsw.cmd.dstat);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" device %s: Failing CCW: %p\n",
- device->cdev->dev.bus_id,
- (void *) (addr_t) irb->scsw.cpa);
+ dev_name(&device->cdev->dev),
+ (void *) (addr_t) irb->scsw.cmd.cpa);
if (irb->esw.esw0.erw.cons) {
for (sl = 0; sl < 4; sl++) {
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
@@ -2122,7 +2318,8 @@ static void dasd_eckd_dump_sense(struct dasd_device *device,
/* scsw->cda is either valid or zero */
len = 0;
from = ++to;
- fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
+ fail = (struct ccw1 *)(addr_t)
+ irb->scsw.cmd.cpa; /* failing CCW */
if (from < fail - 2) {
from = fail - 2; /* there is a gap - print header */
len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index fc2509c939b..2476f87d21d 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -231,133 +231,62 @@ struct dasd_eckd_characteristics {
__u8 reserved3[10];
} __attribute__ ((packed));
-struct dasd_eckd_confdata {
+/* elements of the configuration data */
+struct dasd_ned {
struct {
- struct {
- unsigned char identifier:2;
- unsigned char token_id:1;
- unsigned char sno_valid:1;
- unsigned char subst_sno:1;
- unsigned char recNED:1;
- unsigned char emuNED:1;
- unsigned char reserved:1;
- } __attribute__ ((packed)) flags;
- __u8 descriptor;
- __u8 dev_class;
- __u8 reserved;
- unsigned char dev_type[6];
- unsigned char dev_model[3];
- unsigned char HDA_manufacturer[3];
- unsigned char HDA_location[2];
- unsigned char HDA_seqno[12];
- __u8 ID;
- __u8 unit_addr;
- } __attribute__ ((packed)) ned1;
- union {
- struct {
- struct {
- unsigned char identifier:2;
- unsigned char token_id:1;
- unsigned char sno_valid:1;
- unsigned char subst_sno:1;
- unsigned char recNED:1;
- unsigned char emuNED:1;
- unsigned char reserved:1;
- } __attribute__ ((packed)) flags;
- __u8 descriptor;
- __u8 reserved[2];
- unsigned char dev_type[6];
- unsigned char dev_model[3];
- unsigned char DASD_manufacturer[3];
- unsigned char DASD_location[2];
- unsigned char DASD_seqno[12];
- __u16 ID;
- } __attribute__ ((packed)) ned;
- struct {
- unsigned char flags; /* byte 0 */
- unsigned char res1; /* byte 1 */
- __u16 format; /* byte 2-3 */
- unsigned char res2[4]; /* byte 4-7 */
- unsigned char sua_flags; /* byte 8 */
- __u8 base_unit_addr; /* byte 9 */
- unsigned char res3[22]; /* byte 10-31 */
- } __attribute__ ((packed)) sneq;
- } __attribute__ ((packed)) ned2;
+ __u8 identifier:2;
+ __u8 token_id:1;
+ __u8 sno_valid:1;
+ __u8 subst_sno:1;
+ __u8 recNED:1;
+ __u8 emuNED:1;
+ __u8 reserved:1;
+ } __attribute__ ((packed)) flags;
+ __u8 descriptor;
+ __u8 dev_class;
+ __u8 reserved;
+ __u8 dev_type[6];
+ __u8 dev_model[3];
+ __u8 HDA_manufacturer[3];
+ __u8 HDA_location[2];
+ __u8 HDA_seqno[12];
+ __u8 ID;
+ __u8 unit_addr;
+} __attribute__ ((packed));
+
+struct dasd_sneq {
struct {
- struct {
- unsigned char identifier:2;
- unsigned char token_id:1;
- unsigned char sno_valid:1;
- unsigned char subst_sno:1;
- unsigned char recNED:1;
- unsigned char emuNED:1;
- unsigned char reserved:1;
- } __attribute__ ((packed)) flags;
- __u8 descriptor;
- __u8 reserved[2];
- unsigned char cont_type[6];
- unsigned char cont_model[3];
- unsigned char cont_manufacturer[3];
- unsigned char cont_location[2];
- unsigned char cont_seqno[12];
- __u16 ID;
- } __attribute__ ((packed)) ned3;
+ __u8 identifier:2;
+ __u8 reserved:6;
+ } __attribute__ ((packed)) flags;
+ __u8 res1;
+ __u16 format;
+ __u8 res2[4]; /* byte 4- 7 */
+ __u8 sua_flags; /* byte 8 */
+ __u8 base_unit_addr; /* byte 9 */
+ __u8 res3[22]; /* byte 10-31 */
+} __attribute__ ((packed));
+
+struct vd_sneq {
struct {
- struct {
- unsigned char identifier:2;
- unsigned char token_id:1;
- unsigned char sno_valid:1;
- unsigned char subst_sno:1;
- unsigned char recNED:1;
- unsigned char emuNED:1;
- unsigned char reserved:1;
- } __attribute__ ((packed)) flags;
- __u8 descriptor;
- __u8 reserved[2];
- unsigned char cont_type[6];
- unsigned char empty[3];
- unsigned char cont_manufacturer[3];
- unsigned char cont_location[2];
- unsigned char cont_seqno[12];
- __u16 ID;
- } __attribute__ ((packed)) ned4;
- unsigned char ned5[32];
- unsigned char ned6[32];
- unsigned char ned7[32];
+ __u8 identifier:2;
+ __u8 reserved:6;
+ } __attribute__ ((packed)) flags;
+ __u8 res1;
+ __u16 format;
+ __u8 res2[4]; /* byte 4- 7 */
+ __u8 uit[16]; /* byte 8-23 */
+ __u8 res3[8]; /* byte 24-31 */
+} __attribute__ ((packed));
+
+struct dasd_gneq {
struct {
- struct {
- unsigned char identifier:2;
- unsigned char reserved:6;
- } __attribute__ ((packed)) flags;
- __u8 selector;
- __u16 interfaceID;
- __u32 reserved;
- __u16 subsystemID;
- struct {
- unsigned char sp0:1;
- unsigned char sp1:1;
- unsigned char reserved:5;
- unsigned char scluster:1;
- } __attribute__ ((packed)) spathID;
- __u8 unit_address;
- __u8 dev_ID;
- __u8 dev_address;
- __u8 adapterID;
- __u16 link_address;
- struct {
- unsigned char parallel:1;
- unsigned char escon:1;
- unsigned char reserved:1;
- unsigned char ficon:1;
- unsigned char reserved2:4;
- } __attribute__ ((packed)) protocol_type;
- struct {
- unsigned char PID_in_236:1;
- unsigned char reserved:7;
- } __attribute__ ((packed)) format_flags;
- __u8 log_dev_address;
- unsigned char reserved2[12];
- } __attribute__ ((packed)) neq;
+ __u8 identifier:2;
+ __u8 reserved:6;
+ } __attribute__ ((packed)) flags;
+ __u8 reserved[7];
+ __u16 subsystemID;
+ __u8 reserved2[22];
} __attribute__ ((packed));
struct dasd_eckd_path {
@@ -379,7 +308,7 @@ struct dasd_psf_prssd_data {
unsigned char flags;
unsigned char reserved[4];
unsigned char suborder;
- unsigned char varies[9];
+ unsigned char varies[5];
} __attribute__ ((packed));
/*
@@ -463,7 +392,14 @@ struct alias_pav_group {
struct dasd_eckd_private {
struct dasd_eckd_characteristics rdc_data;
- struct dasd_eckd_confdata conf_data;
+ u8 *conf_data;
+ int conf_len;
+ /* pointers to specific parts in the conf_data */
+ struct dasd_ned *ned;
+ struct dasd_sneq *sneq;
+ struct vd_sneq *vdsneq;
+ struct dasd_gneq *gneq;
+
struct dasd_eckd_path path_data;
struct eckd_count count_area[5];
int init_cqr_status;
diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c
index 6e53ab606e9..892e2878d61 100644
--- a/drivers/s390/block/dasd_eer.c
+++ b/drivers/s390/block/dasd_eer.c
@@ -15,6 +15,8 @@
#include <linux/device.h>
#include <linux/poll.h>
#include <linux/mutex.h>
+#include <linux/smp_lock.h>
+#include <linux/err.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
@@ -307,7 +309,8 @@ static void dasd_eer_write_standard_trigger(struct dasd_device *device,
do_gettimeofday(&tv);
header.tv_sec = tv.tv_sec;
header.tv_usec = tv.tv_usec;
- strncpy(header.busid, device->cdev->dev.bus_id, DASD_EER_BUSID_SIZE);
+ strncpy(header.busid, dev_name(&device->cdev->dev),
+ DASD_EER_BUSID_SIZE);
spin_lock_irqsave(&bufferlock, flags);
list_for_each_entry(eerb, &bufferlist, list) {
@@ -347,7 +350,8 @@ static void dasd_eer_write_snss_trigger(struct dasd_device *device,
do_gettimeofday(&tv);
header.tv_sec = tv.tv_sec;
header.tv_usec = tv.tv_usec;
- strncpy(header.busid, device->cdev->dev.bus_id, DASD_EER_BUSID_SIZE);
+ strncpy(header.busid, dev_name(&device->cdev->dev),
+ DASD_EER_BUSID_SIZE);
spin_lock_irqsave(&bufferlock, flags);
list_for_each_entry(eerb, &bufferlist, list) {
@@ -456,7 +460,7 @@ int dasd_eer_enable(struct dasd_device *device)
cqr = dasd_kmalloc_request("ECKD", 1 /* SNSS */,
SNSS_DATA_SIZE, device);
- if (!cqr)
+ if (IS_ERR(cqr))
return -ENOMEM;
cqr->startdev = device;
@@ -525,6 +529,7 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL);
if (!eerb)
return -ENOMEM;
+ lock_kernel();
eerb->buffer_page_count = eer_pages;
if (eerb->buffer_page_count < 1 ||
eerb->buffer_page_count > INT_MAX / PAGE_SIZE) {
@@ -532,6 +537,7 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
MESSAGE(KERN_WARNING, "can't open device since module "
"parameter eer_pages is smaller then 1 or"
" bigger then %d", (int)(INT_MAX / PAGE_SIZE));
+ unlock_kernel();
return -EINVAL;
}
eerb->buffersize = eerb->buffer_page_count * PAGE_SIZE;
@@ -539,12 +545,14 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
GFP_KERNEL);
if (!eerb->buffer) {
kfree(eerb);
+ unlock_kernel();
return -ENOMEM;
}
if (dasd_eer_allocate_buffer_pages(eerb->buffer,
eerb->buffer_page_count)) {
kfree(eerb->buffer);
kfree(eerb);
+ unlock_kernel();
return -ENOMEM;
}
filp->private_data = eerb;
@@ -552,6 +560,7 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
list_add(&eerb->list, &bufferlist);
spin_unlock_irqrestore(&bufferlock, flags);
+ unlock_kernel();
return nonseekable_open(inp,filp);
}
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 116611583df..93d9b6452a9 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -139,7 +139,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
if (IS_ERR(block)) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
"could not allocate dasd block structure");
- kfree(device->private);
+ device->private = NULL;
+ kfree(private);
return PTR_ERR(block);
}
device->block = block;
@@ -152,6 +153,10 @@ dasd_fba_check_characteristics(struct dasd_device *device)
DEV_MESSAGE(KERN_WARNING, device,
"Read device characteristics returned error %d",
rc);
+ device->block = NULL;
+ dasd_free_block(block);
+ device->private = NULL;
+ kfree(private);
return rc;
}
@@ -222,7 +227,7 @@ static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device,
/* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
- if ((irb->scsw.dstat & mask) == mask) {
+ if ((irb->scsw.cmd.dstat & mask) == mask) {
dasd_generic_handle_state_change(device);
return;
}
@@ -446,14 +451,14 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
}
len = sprintf(page, KERN_ERR PRINTK_HEADER
" I/O status report for device %s:\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" in req: %p CS: 0x%02X DS: 0x%02X\n", req,
- irb->scsw.cstat, irb->scsw.dstat);
+ irb->scsw.cmd.cstat, irb->scsw.cmd.dstat);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" device %s: Failing CCW: %p\n",
- device->cdev->dev.bus_id,
- (void *) (addr_t) irb->scsw.cpa);
+ dev_name(&device->cdev->dev),
+ (void *) (addr_t) irb->scsw.cmd.cpa);
if (irb->esw.esw0.erw.cons) {
for (sl = 0; sl < 4; sl++) {
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
@@ -498,11 +503,11 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
/* print failing CCW area */
len = 0;
- if (act < ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) {
- act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2;
+ if (act < ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2) {
+ act = ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2;
len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
}
- end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last);
+ end = min((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa + 2, last);
while (act <= end) {
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" CCW %p: %08X %08X DAT:",
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index fb2f931cf84..489d5fe488f 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -126,7 +126,7 @@ do { \
#define DEV_MESSAGE(d_loglevel,d_device,d_string,d_args...)\
do { \
printk(d_loglevel PRINTK_HEADER " %s: " d_string "\n", \
- d_device->cdev->dev.bus_id, d_args); \
+ dev_name(&d_device->cdev->dev), d_args); \
DBF_DEV_EVENT(DBF_ALERT, d_device, d_string, d_args); \
} while(0)
@@ -140,7 +140,7 @@ do { \
#define DEV_MESSAGE_LOG(d_loglevel,d_device,d_string,d_args...)\
do { \
printk(d_loglevel PRINTK_HEADER " %s: " d_string "\n", \
- d_device->cdev->dev.bus_id, d_args); \
+ dev_name(&d_device->cdev->dev), d_args); \
} while(0)
#define MESSAGE_LOG(d_loglevel,d_string,d_args...)\
@@ -307,6 +307,7 @@ struct dasd_uid {
__u16 ssid;
__u8 real_unit_addr;
__u8 base_unit_addr;
+ char vduit[33];
};
/*
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 03c0e40a92f..9088de84b45 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -67,7 +67,7 @@ dasd_devices_show(struct seq_file *m, void *v)
return 0;
}
/* Print device number. */
- seq_printf(m, "%s", device->cdev->dev.bus_id);
+ seq_printf(m, "%s", dev_name(&device->cdev->dev));
/* Print discipline string. */
if (device != NULL && device->discipline != NULL)
seq_printf(m, "(%s)", device->discipline->name);
@@ -76,7 +76,8 @@ dasd_devices_show(struct seq_file *m, void *v)
/* Print kdev. */
if (block->gdp)
seq_printf(m, " at (%3d:%6d)",
- block->gdp->major, block->gdp->first_minor);
+ MAJOR(disk_devt(block->gdp)),
+ MINOR(disk_devt(block->gdp)));
else
seq_printf(m, " at (???:??????)");
/* Print device name. */
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index bb52d2fbac1..a7ff167d5b8 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -31,7 +31,6 @@
#define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x)
#define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x)
-
static int dcssblk_open(struct inode *inode, struct file *filp);
static int dcssblk_release(struct inode *inode, struct file *filp);
static int dcssblk_make_request(struct request_queue *q, struct bio *bio);
@@ -48,6 +47,30 @@ static struct block_device_operations dcssblk_devops = {
.direct_access = dcssblk_direct_access,
};
+struct dcssblk_dev_info {
+ struct list_head lh;
+ struct device dev;
+ char segment_name[BUS_ID_SIZE];
+ atomic_t use_count;
+ struct gendisk *gd;
+ unsigned long start;
+ unsigned long end;
+ int segment_type;
+ unsigned char save_pending;
+ unsigned char is_shared;
+ struct request_queue *dcssblk_queue;
+ int num_of_segments;
+ struct list_head seg_list;
+};
+
+struct segment_info {
+ struct list_head lh;
+ char segment_name[BUS_ID_SIZE];
+ unsigned long start;
+ unsigned long end;
+ int segment_type;
+};
+
static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf,
size_t count);
static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf,
@@ -58,30 +81,20 @@ static ssize_t dcssblk_save_show(struct device *dev, struct device_attribute *at
static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf,
size_t count);
static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t dcssblk_seglist_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store);
static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store);
-static DEVICE_ATTR(save, S_IWUSR | S_IRUGO, dcssblk_save_show,
+static DEVICE_ATTR(save, S_IWUSR | S_IRUSR, dcssblk_save_show,
dcssblk_save_store);
-static DEVICE_ATTR(shared, S_IWUSR | S_IRUGO, dcssblk_shared_show,
+static DEVICE_ATTR(shared, S_IWUSR | S_IRUSR, dcssblk_shared_show,
dcssblk_shared_store);
+static DEVICE_ATTR(seglist, S_IRUSR, dcssblk_seglist_show, NULL);
static struct device *dcssblk_root_dev;
-struct dcssblk_dev_info {
- struct list_head lh;
- struct device dev;
- char segment_name[BUS_ID_SIZE];
- atomic_t use_count;
- struct gendisk *gd;
- unsigned long start;
- unsigned long end;
- int segment_type;
- unsigned char save_pending;
- unsigned char is_shared;
- struct request_queue *dcssblk_queue;
-};
-
static LIST_HEAD(dcssblk_devices);
static struct rw_semaphore dcssblk_devices_sem;
@@ -91,8 +104,15 @@ static struct rw_semaphore dcssblk_devices_sem;
static void
dcssblk_release_segment(struct device *dev)
{
- PRINT_DEBUG("segment release fn called for %s\n", dev->bus_id);
- kfree(container_of(dev, struct dcssblk_dev_info, dev));
+ struct dcssblk_dev_info *dev_info;
+ struct segment_info *entry, *temp;
+
+ dev_info = container_of(dev, struct dcssblk_dev_info, dev);
+ list_for_each_entry_safe(entry, temp, &dev_info->seg_list, lh) {
+ list_del(&entry->lh);
+ kfree(entry);
+ }
+ kfree(dev_info);
module_put(THIS_MODULE);
}
@@ -114,7 +134,7 @@ dcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info)
found = 0;
// test if minor available
list_for_each_entry(entry, &dcssblk_devices, lh)
- if (minor == entry->gd->first_minor)
+ if (minor == MINOR(disk_devt(entry->gd)))
found++;
if (!found) break; // got unused minor
}
@@ -142,6 +162,169 @@ dcssblk_get_device_by_name(char *name)
return NULL;
}
+/*
+ * get the struct segment_info from seg_list
+ * for the given name.
+ * down_read(&dcssblk_devices_sem) must be held.
+ */
+static struct segment_info *
+dcssblk_get_segment_by_name(char *name)
+{
+ struct dcssblk_dev_info *dev_info;
+ struct segment_info *entry;
+
+ list_for_each_entry(dev_info, &dcssblk_devices, lh) {
+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
+ if (!strcmp(name, entry->segment_name))
+ return entry;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * get the highest address of the multi-segment block.
+ */
+static unsigned long
+dcssblk_find_highest_addr(struct dcssblk_dev_info *dev_info)
+{
+ unsigned long highest_addr;
+ struct segment_info *entry;
+
+ highest_addr = 0;
+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
+ if (highest_addr < entry->end)
+ highest_addr = entry->end;
+ }
+ return highest_addr;
+}
+
+/*
+ * get the lowest address of the multi-segment block.
+ */
+static unsigned long
+dcssblk_find_lowest_addr(struct dcssblk_dev_info *dev_info)
+{
+ int set_first;
+ unsigned long lowest_addr;
+ struct segment_info *entry;
+
+ set_first = 0;
+ lowest_addr = 0;
+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
+ if (set_first == 0) {
+ lowest_addr = entry->start;
+ set_first = 1;
+ } else {
+ if (lowest_addr > entry->start)
+ lowest_addr = entry->start;
+ }
+ }
+ return lowest_addr;
+}
+
+/*
+ * Check continuity of segments.
+ */
+static int
+dcssblk_is_continuous(struct dcssblk_dev_info *dev_info)
+{
+ int i, j, rc;
+ struct segment_info *sort_list, *entry, temp;
+
+ if (dev_info->num_of_segments <= 1)
+ return 0;
+
+ sort_list = kzalloc(
+ sizeof(struct segment_info) * dev_info->num_of_segments,
+ GFP_KERNEL);
+ if (sort_list == NULL)
+ return -ENOMEM;
+ i = 0;
+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
+ memcpy(&sort_list[i], entry, sizeof(struct segment_info));
+ i++;
+ }
+
+ /* sort segments */
+ for (i = 0; i < dev_info->num_of_segments; i++)
+ for (j = 0; j < dev_info->num_of_segments; j++)
+ if (sort_list[j].start > sort_list[i].start) {
+ memcpy(&temp, &sort_list[i],
+ sizeof(struct segment_info));
+ memcpy(&sort_list[i], &sort_list[j],
+ sizeof(struct segment_info));
+ memcpy(&sort_list[j], &temp,
+ sizeof(struct segment_info));
+ }
+
+ /* check continuity */
+ for (i = 0; i < dev_info->num_of_segments - 1; i++) {
+ if ((sort_list[i].end + 1) != sort_list[i+1].start) {
+ PRINT_ERR("Segment %s is not contiguous with "
+ "segment %s\n",
+ sort_list[i].segment_name,
+ sort_list[i+1].segment_name);
+ rc = -EINVAL;
+ goto out;
+ }
+ /* EN and EW are allowed in a block device */
+ if (sort_list[i].segment_type != sort_list[i+1].segment_type) {
+ if (!(sort_list[i].segment_type & SEGMENT_EXCLUSIVE) ||
+ (sort_list[i].segment_type == SEG_TYPE_ER) ||
+ !(sort_list[i+1].segment_type &
+ SEGMENT_EXCLUSIVE) ||
+ (sort_list[i+1].segment_type == SEG_TYPE_ER)) {
+ PRINT_ERR("Segment %s has different type from "
+ "segment %s\n",
+ sort_list[i].segment_name,
+ sort_list[i+1].segment_name);
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+ }
+ rc = 0;
+out:
+ kfree(sort_list);
+ return rc;
+}
+
+/*
+ * Load a segment
+ */
+static int
+dcssblk_load_segment(char *name, struct segment_info **seg_info)
+{
+ int rc;
+
+ /* already loaded? */
+ down_read(&dcssblk_devices_sem);
+ *seg_info = dcssblk_get_segment_by_name(name);
+ up_read(&dcssblk_devices_sem);
+ if (*seg_info != NULL)
+ return -EEXIST;
+
+ /* get a struct segment_info */
+ *seg_info = kzalloc(sizeof(struct segment_info), GFP_KERNEL);
+ if (*seg_info == NULL)
+ return -ENOMEM;
+
+ strcpy((*seg_info)->segment_name, name);
+
+ /* load the segment */
+ rc = segment_load(name, SEGMENT_SHARED,
+ &(*seg_info)->start, &(*seg_info)->end);
+ if (rc < 0) {
+ segment_warning(rc, (*seg_info)->segment_name);
+ kfree(*seg_info);
+ } else {
+ INIT_LIST_HEAD(&(*seg_info)->lh);
+ (*seg_info)->segment_type = rc;
+ }
+ return rc;
+}
+
static void dcssblk_unregister_callback(struct device *dev)
{
device_unregister(dev);
@@ -165,57 +348,55 @@ static ssize_t
dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
{
struct dcssblk_dev_info *dev_info;
+ struct segment_info *entry, *temp;
int rc;
- if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) {
- PRINT_WARN("Invalid value, must be 0 or 1\n");
+ if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
return -EINVAL;
- }
down_write(&dcssblk_devices_sem);
dev_info = container_of(dev, struct dcssblk_dev_info, dev);
if (atomic_read(&dev_info->use_count)) {
- PRINT_ERR("share: segment %s is busy!\n",
- dev_info->segment_name);
rc = -EBUSY;
goto out;
}
if (inbuf[0] == '1') {
- // reload segment in shared mode
- rc = segment_modify_shared(dev_info->segment_name,
- SEGMENT_SHARED);
- if (rc < 0) {
- BUG_ON(rc == -EINVAL);
- if (rc != -EAGAIN)
- goto removeseg;
- } else {
- dev_info->is_shared = 1;
- switch (dev_info->segment_type) {
- case SEG_TYPE_SR:
- case SEG_TYPE_ER:
- case SEG_TYPE_SC:
- set_disk_ro(dev_info->gd,1);
+ /* reload segments in shared mode */
+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
+ rc = segment_modify_shared(entry->segment_name,
+ SEGMENT_SHARED);
+ if (rc < 0) {
+ BUG_ON(rc == -EINVAL);
+ if (rc != -EAGAIN)
+ goto removeseg;
}
}
+ dev_info->is_shared = 1;
+ switch (dev_info->segment_type) {
+ case SEG_TYPE_SR:
+ case SEG_TYPE_ER:
+ case SEG_TYPE_SC:
+ set_disk_ro(dev_info->gd, 1);
+ }
} else if (inbuf[0] == '0') {
- // reload segment in exclusive mode
+ /* reload segments in exclusive mode */
if (dev_info->segment_type == SEG_TYPE_SC) {
PRINT_ERR("Segment type SC (%s) cannot be loaded in "
- "non-shared mode\n", dev_info->segment_name);
+ "non-shared mode\n", dev_info->segment_name);
rc = -EINVAL;
goto out;
}
- rc = segment_modify_shared(dev_info->segment_name,
- SEGMENT_EXCLUSIVE);
- if (rc < 0) {
- BUG_ON(rc == -EINVAL);
- if (rc != -EAGAIN)
- goto removeseg;
- } else {
- dev_info->is_shared = 0;
- set_disk_ro(dev_info->gd, 0);
+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
+ rc = segment_modify_shared(entry->segment_name,
+ SEGMENT_EXCLUSIVE);
+ if (rc < 0) {
+ BUG_ON(rc == -EINVAL);
+ if (rc != -EAGAIN)
+ goto removeseg;
+ }
}
+ dev_info->is_shared = 0;
+ set_disk_ro(dev_info->gd, 0);
} else {
- PRINT_WARN("Invalid value, must be 0 or 1\n");
rc = -EINVAL;
goto out;
}
@@ -223,8 +404,14 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch
goto out;
removeseg:
- PRINT_ERR("Could not reload segment %s, removing it now!\n",
- dev_info->segment_name);
+ PRINT_ERR("Could not reload segment(s) of the device %s, removing "
+ "segment(s) now!\n",
+ dev_info->segment_name);
+ temp = entry;
+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
+ if (entry != temp)
+ segment_unload(entry->segment_name);
+ }
list_del(&dev_info->lh);
del_gendisk(dev_info->gd);
@@ -257,25 +444,26 @@ static ssize_t
dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
{
struct dcssblk_dev_info *dev_info;
+ struct segment_info *entry;
- if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) {
- PRINT_WARN("Invalid value, must be 0 or 1\n");
+ if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
return -EINVAL;
- }
dev_info = container_of(dev, struct dcssblk_dev_info, dev);
down_write(&dcssblk_devices_sem);
if (inbuf[0] == '1') {
if (atomic_read(&dev_info->use_count) == 0) {
// device is idle => we save immediately
- PRINT_INFO("Saving segment %s\n",
+ PRINT_INFO("Saving segment(s) of the device %s\n",
dev_info->segment_name);
- segment_save(dev_info->segment_name);
+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
+ segment_save(entry->segment_name);
+ }
} else {
// device is busy => we save it when it becomes
// idle in dcssblk_release
- PRINT_INFO("Segment %s is currently busy, it will "
- "be saved when it becomes idle...\n",
+ PRINT_INFO("Device %s is currently busy, segment(s) "
+ "will be saved when it becomes idle...\n",
dev_info->segment_name);
dev_info->save_pending = 1;
}
@@ -284,12 +472,12 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
// device is busy & the user wants to undo his save
// request
dev_info->save_pending = 0;
- PRINT_INFO("Pending save for segment %s deactivated\n",
+ PRINT_INFO("Pending save for segment(s) of the device "
+ "%s deactivated\n",
dev_info->segment_name);
}
} else {
up_write(&dcssblk_devices_sem);
- PRINT_WARN("Invalid value, must be 0 or 1\n");
return -EINVAL;
}
up_write(&dcssblk_devices_sem);
@@ -297,66 +485,123 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
}
/*
+ * device attribute for showing all segments in a device
+ */
+static ssize_t
+dcssblk_seglist_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int i;
+
+ struct dcssblk_dev_info *dev_info;
+ struct segment_info *entry;
+
+ down_read(&dcssblk_devices_sem);
+ dev_info = container_of(dev, struct dcssblk_dev_info, dev);
+ i = 0;
+ buf[0] = '\0';
+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
+ strcpy(&buf[i], entry->segment_name);
+ i += strlen(entry->segment_name);
+ buf[i] = '\n';
+ i++;
+ }
+ up_read(&dcssblk_devices_sem);
+ return i;
+}
+
+/*
* device attribute for adding devices
*/
static ssize_t
dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
- int rc, i;
+ int rc, i, j, num_of_segments;
struct dcssblk_dev_info *dev_info;
+ struct segment_info *seg_info, *temp;
char *local_buf;
unsigned long seg_byte_size;
dev_info = NULL;
+ seg_info = NULL;
if (dev != dcssblk_root_dev) {
rc = -EINVAL;
goto out_nobuf;
}
+ if ((count < 1) || (buf[0] == '\0') || (buf[0] == '\n')) {
+ rc = -ENAMETOOLONG;
+ goto out_nobuf;
+ }
+
local_buf = kmalloc(count + 1, GFP_KERNEL);
if (local_buf == NULL) {
rc = -ENOMEM;
goto out_nobuf;
}
+
/*
* parse input
*/
+ num_of_segments = 0;
for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) {
- local_buf[i] = toupper(buf[i]);
+ for (j = i; (buf[j] != ':') &&
+ (buf[j] != '\0') &&
+ (buf[j] != '\n') &&
+ j < count; j++) {
+ local_buf[j-i] = toupper(buf[j]);
+ }
+ local_buf[j-i] = '\0';
+ if (((j - i) == 0) || ((j - i) > 8)) {
+ rc = -ENAMETOOLONG;
+ goto seg_list_del;
+ }
+
+ rc = dcssblk_load_segment(local_buf, &seg_info);
+ if (rc < 0)
+ goto seg_list_del;
+ /*
+ * get a struct dcssblk_dev_info
+ */
+ if (num_of_segments == 0) {
+ dev_info = kzalloc(sizeof(struct dcssblk_dev_info),
+ GFP_KERNEL);
+ if (dev_info == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ strcpy(dev_info->segment_name, local_buf);
+ dev_info->segment_type = seg_info->segment_type;
+ INIT_LIST_HEAD(&dev_info->seg_list);
+ }
+ list_add_tail(&seg_info->lh, &dev_info->seg_list);
+ num_of_segments++;
+ i = j;
+
+ if ((buf[j] == '\0') || (buf[j] == '\n'))
+ break;
}
- local_buf[i] = '\0';
- if ((i == 0) || (i > 8)) {
+
+ /* no trailing colon at the end of the input */
+ if ((i > 0) && (buf[i-1] == ':')) {
rc = -ENAMETOOLONG;
- goto out;
- }
- /*
- * already loaded?
- */
- down_read(&dcssblk_devices_sem);
- dev_info = dcssblk_get_device_by_name(local_buf);
- up_read(&dcssblk_devices_sem);
- if (dev_info != NULL) {
- PRINT_WARN("Segment %s already loaded!\n", local_buf);
- rc = -EEXIST;
- goto out;
- }
- /*
- * get a struct dcssblk_dev_info
- */
- dev_info = kzalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL);
- if (dev_info == NULL) {
- rc = -ENOMEM;
- goto out;
+ goto seg_list_del;
}
+ strlcpy(local_buf, buf, i + 1);
+ dev_info->num_of_segments = num_of_segments;
+ rc = dcssblk_is_continuous(dev_info);
+ if (rc < 0)
+ goto seg_list_del;
- strcpy(dev_info->segment_name, local_buf);
- strlcpy(dev_info->dev.bus_id, local_buf, BUS_ID_SIZE);
+ dev_info->start = dcssblk_find_lowest_addr(dev_info);
+ dev_info->end = dcssblk_find_highest_addr(dev_info);
+
+ dev_set_name(&dev_info->dev, dev_info->segment_name);
dev_info->dev.release = dcssblk_release_segment;
INIT_LIST_HEAD(&dev_info->lh);
-
dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK);
if (dev_info->gd == NULL) {
rc = -ENOMEM;
- goto free_dev_info;
+ goto seg_list_del;
}
dev_info->gd->major = dcssblk_major;
dev_info->gd->fops = &dcssblk_devops;
@@ -366,54 +611,43 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
dev_info->gd->driverfs_dev = &dev_info->dev;
blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096);
- /*
- * load the segment
- */
- rc = segment_load(local_buf, SEGMENT_SHARED,
- &dev_info->start, &dev_info->end);
- if (rc < 0) {
- segment_warning(rc, dev_info->segment_name);
- goto dealloc_gendisk;
- }
+
seg_byte_size = (dev_info->end - dev_info->start + 1);
set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
- PRINT_INFO("Loaded segment %s, size = %lu Byte, "
+ PRINT_INFO("Loaded segment(s) %s, size = %lu Byte, "
"capacity = %lu (512 Byte) sectors\n", local_buf,
seg_byte_size, seg_byte_size >> 9);
- dev_info->segment_type = rc;
dev_info->save_pending = 0;
dev_info->is_shared = 1;
dev_info->dev.parent = dcssblk_root_dev;
/*
- * get minor, add to list
+ *get minor, add to list
*/
down_write(&dcssblk_devices_sem);
- rc = dcssblk_assign_free_minor(dev_info);
- if (rc) {
- up_write(&dcssblk_devices_sem);
- PRINT_ERR("No free minor number available! "
- "Unloading segment...\n");
- goto unload_seg;
+ if (dcssblk_get_segment_by_name(local_buf)) {
+ rc = -EEXIST;
+ goto release_gd;
}
+ rc = dcssblk_assign_free_minor(dev_info);
+ if (rc)
+ goto release_gd;
sprintf(dev_info->gd->disk_name, "dcssblk%d",
- dev_info->gd->first_minor);
+ MINOR(disk_devt(dev_info->gd)));
list_add_tail(&dev_info->lh, &dcssblk_devices);
if (!try_module_get(THIS_MODULE)) {
rc = -ENODEV;
- goto list_del;
+ goto dev_list_del;
}
/*
* register the device
*/
rc = device_register(&dev_info->dev);
if (rc) {
- PRINT_ERR("Segment %s could not be registered RC=%d\n",
- local_buf, rc);
module_put(THIS_MODULE);
- goto list_del;
+ goto dev_list_del;
}
get_device(&dev_info->dev);
rc = device_create_file(&dev_info->dev, &dev_attr_shared);
@@ -422,6 +656,9 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
rc = device_create_file(&dev_info->dev, &dev_attr_save);
if (rc)
goto unregister_dev;
+ rc = device_create_file(&dev_info->dev, &dev_attr_seglist);
+ if (rc)
+ goto unregister_dev;
add_disk(dev_info->gd);
@@ -435,32 +672,37 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
set_disk_ro(dev_info->gd,0);
break;
}
- PRINT_DEBUG("Segment %s loaded successfully\n", local_buf);
up_write(&dcssblk_devices_sem);
rc = count;
goto out;
unregister_dev:
- PRINT_ERR("device_create_file() failed!\n");
list_del(&dev_info->lh);
blk_cleanup_queue(dev_info->dcssblk_queue);
dev_info->gd->queue = NULL;
put_disk(dev_info->gd);
device_unregister(&dev_info->dev);
- segment_unload(dev_info->segment_name);
+ list_for_each_entry(seg_info, &dev_info->seg_list, lh) {
+ segment_unload(seg_info->segment_name);
+ }
put_device(&dev_info->dev);
up_write(&dcssblk_devices_sem);
goto out;
-list_del:
+dev_list_del:
list_del(&dev_info->lh);
- up_write(&dcssblk_devices_sem);
-unload_seg:
- segment_unload(local_buf);
-dealloc_gendisk:
+release_gd:
blk_cleanup_queue(dev_info->dcssblk_queue);
dev_info->gd->queue = NULL;
put_disk(dev_info->gd);
-free_dev_info:
+ up_write(&dcssblk_devices_sem);
+seg_list_del:
+ if (dev_info == NULL)
+ goto out;
+ list_for_each_entry_safe(seg_info, temp, &dev_info->seg_list, lh) {
+ list_del(&seg_info->lh);
+ segment_unload(seg_info->segment_name);
+ kfree(seg_info);
+ }
kfree(dev_info);
out:
kfree(local_buf);
@@ -475,6 +717,7 @@ static ssize_t
dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct dcssblk_dev_info *dev_info;
+ struct segment_info *entry;
int rc, i;
char *local_buf;
@@ -501,26 +744,28 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch
dev_info = dcssblk_get_device_by_name(local_buf);
if (dev_info == NULL) {
up_write(&dcssblk_devices_sem);
- PRINT_WARN("Segment %s is not loaded!\n", local_buf);
+ PRINT_WARN("Device %s is not loaded!\n", local_buf);
rc = -ENODEV;
goto out_buf;
}
if (atomic_read(&dev_info->use_count) != 0) {
up_write(&dcssblk_devices_sem);
- PRINT_WARN("Segment %s is in use!\n", local_buf);
+ PRINT_WARN("Device %s is in use!\n", local_buf);
rc = -EBUSY;
goto out_buf;
}
- list_del(&dev_info->lh);
+ list_del(&dev_info->lh);
del_gendisk(dev_info->gd);
blk_cleanup_queue(dev_info->dcssblk_queue);
dev_info->gd->queue = NULL;
put_disk(dev_info->gd);
device_unregister(&dev_info->dev);
- segment_unload(dev_info->segment_name);
- PRINT_DEBUG("Segment %s unloaded successfully\n",
- dev_info->segment_name);
+
+ /* unload all related segments */
+ list_for_each_entry(entry, &dev_info->seg_list, lh)
+ segment_unload(entry->segment_name);
+
put_device(&dev_info->dev);
up_write(&dcssblk_devices_sem);
@@ -552,6 +797,7 @@ static int
dcssblk_release(struct inode *inode, struct file *filp)
{
struct dcssblk_dev_info *dev_info;
+ struct segment_info *entry;
int rc;
dev_info = inode->i_bdev->bd_disk->private_data;
@@ -562,9 +808,11 @@ dcssblk_release(struct inode *inode, struct file *filp)
down_write(&dcssblk_devices_sem);
if (atomic_dec_and_test(&dev_info->use_count)
&& (dev_info->save_pending)) {
- PRINT_INFO("Segment %s became idle and is being saved now\n",
+ PRINT_INFO("Device %s became idle and is being saved now\n",
dev_info->segment_name);
- segment_save(dev_info->segment_name);
+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
+ segment_save(entry->segment_name);
+ }
dev_info->save_pending = 0;
}
up_write(&dcssblk_devices_sem);
@@ -604,7 +852,8 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
case SEG_TYPE_SC:
/* cannot write to these segments */
if (bio_data_dir(bio) == WRITE) {
- PRINT_WARN("rejecting write to ro segment %s\n", dev_info->dev.bus_id);
+ PRINT_WARN("rejecting write to ro device %s\n",
+ dev_name(&dev_info->dev));
goto fail;
}
}
@@ -659,7 +908,7 @@ static void
dcssblk_check_params(void)
{
int rc, i, j, k;
- char buf[9];
+ char buf[DCSSBLK_PARM_LEN + 1];
struct dcssblk_dev_info *dev_info;
for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0');
@@ -667,15 +916,16 @@ dcssblk_check_params(void)
for (j = i; (dcssblk_segments[j] != ',') &&
(dcssblk_segments[j] != '\0') &&
(dcssblk_segments[j] != '(') &&
- (j - i) < 8; j++)
+ (j < DCSSBLK_PARM_LEN); j++)
{
buf[j-i] = dcssblk_segments[j];
}
buf[j-i] = '\0';
rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i);
if ((rc >= 0) && (dcssblk_segments[j] == '(')) {
- for (k = 0; buf[k] != '\0'; k++)
+ for (k = 0; (buf[k] != ':') && (buf[k] != '\0'); k++)
buf[k] = toupper(buf[k]);
+ buf[k] = '\0';
if (!strncmp(&dcssblk_segments[j], "(local)", 7)) {
down_read(&dcssblk_devices_sem);
dev_info = dcssblk_get_device_by_name(buf);
@@ -702,10 +952,8 @@ dcssblk_check_params(void)
static void __exit
dcssblk_exit(void)
{
- PRINT_DEBUG("DCSSBLOCK EXIT...\n");
s390_root_dev_unregister(dcssblk_root_dev);
unregister_blkdev(dcssblk_major, DCSSBLK_NAME);
- PRINT_DEBUG("...finished!\n");
}
static int __init
@@ -713,27 +961,21 @@ dcssblk_init(void)
{
int rc;
- PRINT_DEBUG("DCSSBLOCK INIT...\n");
dcssblk_root_dev = s390_root_dev_register("dcssblk");
- if (IS_ERR(dcssblk_root_dev)) {
- PRINT_ERR("device_register() failed!\n");
+ if (IS_ERR(dcssblk_root_dev))
return PTR_ERR(dcssblk_root_dev);
- }
rc = device_create_file(dcssblk_root_dev, &dev_attr_add);
if (rc) {
- PRINT_ERR("device_create_file(add) failed!\n");
s390_root_dev_unregister(dcssblk_root_dev);
return rc;
}
rc = device_create_file(dcssblk_root_dev, &dev_attr_remove);
if (rc) {
- PRINT_ERR("device_create_file(remove) failed!\n");
s390_root_dev_unregister(dcssblk_root_dev);
return rc;
}
rc = register_blkdev(0, DCSSBLK_NAME);
if (rc < 0) {
- PRINT_ERR("Can't get dynamic major!\n");
s390_root_dev_unregister(dcssblk_root_dev);
return rc;
}
@@ -742,7 +984,6 @@ dcssblk_init(void)
dcssblk_check_params();
- PRINT_DEBUG("...finished!\n");
return 0;
}
@@ -751,10 +992,12 @@ module_exit(dcssblk_exit);
module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444);
MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, "
- "comma-separated list, each name max. 8 chars.\n"
- "Adding \"(local)\" to segment name equals echoing 0 to "
- "/sys/devices/dcssblk/<segment name>/shared after loading "
- "the segment - \n"
- "e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\"");
+ "comma-separated list, names in each set separated "
+ "by commas are separated by colons, each set contains "
+ "names of contiguous segments and each name max. 8 chars.\n"
+ "Adding \"(local)\" to the end of each set equals echoing 0 "
+ "to /sys/devices/dcssblk/<device name>/shared after loading "
+ "the contiguous segments - \n"
+ "e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\"");
MODULE_LICENSE("GPL");
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index f231bc21b1c..03916989ed2 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -56,6 +56,7 @@ typedef struct {
static xpram_device_t xpram_devices[XPRAM_MAX_DEVS];
static unsigned int xpram_sizes[XPRAM_MAX_DEVS];
static struct gendisk *xpram_disks[XPRAM_MAX_DEVS];
+static struct request_queue *xpram_queues[XPRAM_MAX_DEVS];
static unsigned int xpram_pages;
static int xpram_devs;
@@ -100,15 +101,10 @@ static int xpram_page_in (unsigned long page_addr, unsigned int xpage_index)
: "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
if (cc == 3)
return -ENXIO;
- if (cc == 2) {
- PRINT_ERR("expanded storage lost!\n");
+ if (cc == 2)
return -ENXIO;
- }
- if (cc == 1) {
- PRINT_ERR("page in failed for page index %u.\n",
- xpage_index);
+ if (cc == 1)
return -EIO;
- }
return 0;
}
@@ -135,15 +131,10 @@ static long xpram_page_out (unsigned long page_addr, unsigned int xpage_index)
: "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
if (cc == 3)
return -ENXIO;
- if (cc == 2) {
- PRINT_ERR("expanded storage lost!\n");
+ if (cc == 2)
return -ENXIO;
- }
- if (cc == 1) {
- PRINT_ERR("page out failed for page index %u.\n",
- xpage_index);
+ if (cc == 1)
return -EIO;
- }
return 0;
}
@@ -340,18 +331,22 @@ static int __init xpram_setup_sizes(unsigned long pages)
return 0;
}
-static struct request_queue *xpram_queue;
-
static int __init xpram_setup_blkdev(void)
{
unsigned long offset;
int i, rc = -ENOMEM;
for (i = 0; i < xpram_devs; i++) {
- struct gendisk *disk = alloc_disk(1);
- if (!disk)
+ xpram_disks[i] = alloc_disk(1);
+ if (!xpram_disks[i])
+ goto out;
+ xpram_queues[i] = blk_alloc_queue(GFP_KERNEL);
+ if (!xpram_queues[i]) {
+ put_disk(xpram_disks[i]);
goto out;
- xpram_disks[i] = disk;
+ }
+ blk_queue_make_request(xpram_queues[i], xpram_make_request);
+ blk_queue_hardsect_size(xpram_queues[i], 4096);
}
/*
@@ -362,18 +357,6 @@ static int __init xpram_setup_blkdev(void)
goto out;
/*
- * Assign the other needed values: make request function, sizes and
- * hardsect size. All the minor devices feature the same value.
- */
- xpram_queue = blk_alloc_queue(GFP_KERNEL);
- if (!xpram_queue) {
- rc = -ENOMEM;
- goto out_unreg;
- }
- blk_queue_make_request(xpram_queue, xpram_make_request);
- blk_queue_hardsect_size(xpram_queue, 4096);
-
- /*
* Setup device structures.
*/
offset = 0;
@@ -387,18 +370,18 @@ static int __init xpram_setup_blkdev(void)
disk->first_minor = i;
disk->fops = &xpram_devops;
disk->private_data = &xpram_devices[i];
- disk->queue = xpram_queue;
+ disk->queue = xpram_queues[i];
sprintf(disk->disk_name, "slram%d", i);
set_capacity(disk, xpram_sizes[i] << 1);
add_disk(disk);
}
return 0;
-out_unreg:
- unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME);
out:
- while (i--)
+ while (i--) {
+ blk_cleanup_queue(xpram_queues[i]);
put_disk(xpram_disks[i]);
+ }
return rc;
}
@@ -410,10 +393,10 @@ static void __exit xpram_exit(void)
int i;
for (i = 0; i < xpram_devs; i++) {
del_gendisk(xpram_disks[i]);
+ blk_cleanup_queue(xpram_queues[i]);
put_disk(xpram_disks[i]);
}
unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME);
- blk_cleanup_queue(xpram_queue);
}
static int __init xpram_init(void)
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 3e5653c92f4..9ab06e0dad4 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -21,6 +21,7 @@
#include <linux/console.h>
#include <linux/interrupt.h>
#include <linux/err.h>
+#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/bootmem.h>
@@ -88,14 +89,10 @@ struct raw3215_info {
int count; /* number of bytes in output buffer */
int written; /* number of bytes in write requests */
struct tty_struct *tty; /* pointer to tty structure if present */
- struct tasklet_struct tasklet;
struct raw3215_req *queued_read; /* pointer to queued read requests */
struct raw3215_req *queued_write;/* pointer to queued write requests */
wait_queue_head_t empty_wait; /* wait queue for flushing */
struct timer_list timer; /* timer for delayed output */
- char *message; /* pending message from raw3215_irq */
- int msg_dstat; /* dstat for pending message */
- int msg_cstat; /* cstat for pending message */
int line_pos; /* position on the line (for tabs) */
char ubuffer[80]; /* copy_from_user buffer */
};
@@ -344,26 +341,14 @@ raw3215_try_io(struct raw3215_info *raw)
}
/*
- * The bottom half handler routine for 3215 devices. It tries to start
- * the next IO and wakes up processes waiting on the tty.
+ * Try to start the next IO and wake up processes waiting on the tty.
*/
-static void
-raw3215_tasklet(void *data)
+static void raw3215_next_io(struct raw3215_info *raw)
{
- struct raw3215_info *raw;
struct tty_struct *tty;
- unsigned long flags;
- raw = (struct raw3215_info *) data;
- spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);
raw3215_mk_write_req(raw);
raw3215_try_io(raw);
- spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
- /* Check for pending message from raw3215_irq */
- if (raw->message != NULL) {
- printk(raw->message, raw->msg_dstat, raw->msg_cstat);
- raw->message = NULL;
- }
tty = raw->tty;
if (tty != NULL &&
RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) {
@@ -381,20 +366,14 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
struct raw3215_req *req;
struct tty_struct *tty;
int cstat, dstat;
- int count, slen;
+ int count;
raw = cdev->dev.driver_data;
req = (struct raw3215_req *) intparm;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
- if (cstat != 0) {
- raw->message = KERN_WARNING
- "Got nonzero channel status in raw3215_irq "
- "(dev sts 0x%2x, sch sts 0x%2x)";
- raw->msg_dstat = dstat;
- raw->msg_cstat = cstat;
- tasklet_schedule(&raw->tasklet);
- }
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
+ if (cstat != 0)
+ raw3215_next_io(raw);
if (dstat & 0x01) { /* we got a unit exception */
dstat &= ~0x01; /* we can ignore it */
}
@@ -404,9 +383,7 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
break;
/* Attention interrupt, someone hit the enter key */
raw3215_mk_read_req(raw);
- if (MACHINE_IS_P390)
- memset(raw->inbuf, 0, RAW3215_INBUF_SIZE);
- tasklet_schedule(&raw->tasklet);
+ raw3215_next_io(raw);
break;
case 0x08:
case 0x0C:
@@ -415,7 +392,7 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
return; /* That shouldn't happen ... */
if (req->type == RAW3215_READ) {
/* store residual count, then wait for device end */
- req->residual = irb->scsw.count;
+ req->residual = irb->scsw.cmd.count;
}
if (dstat == 0x08)
break;
@@ -428,11 +405,6 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
tty = raw->tty;
count = 160 - req->residual;
- if (MACHINE_IS_P390) {
- slen = strnlen(raw->inbuf, RAW3215_INBUF_SIZE);
- if (count > slen)
- count = slen;
- } else
EBCASC(raw->inbuf, count);
cchar = ctrlchar_handle(raw->inbuf, count, tty);
switch (cchar & CTRLCHAR_MASK) {
@@ -469,7 +441,7 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
raw->queued_read == NULL) {
wake_up_interruptible(&raw->empty_wait);
}
- tasklet_schedule(&raw->tasklet);
+ raw3215_next_io(raw);
break;
default:
/* Strange interrupt, I'll do my best to clean up */
@@ -481,12 +453,7 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
raw->flags &= ~RAW3215_WORKING;
raw3215_free_req(req);
}
- raw->message = KERN_WARNING
- "Spurious interrupt in in raw3215_irq "
- "(dev sts 0x%2x, sch sts 0x%2x)";
- raw->msg_dstat = dstat;
- raw->msg_cstat = cstat;
- tasklet_schedule(&raw->tasklet);
+ raw3215_next_io(raw);
}
return;
}
@@ -700,9 +667,6 @@ raw3215_probe (struct ccw_device *cdev)
kfree(raw);
return -ENOMEM;
}
- tasklet_init(&raw->tasklet,
- (void (*)(unsigned long)) raw3215_tasklet,
- (unsigned long) raw);
init_waitqueue_head(&raw->empty_wait);
cdev->dev.driver_data = raw;
@@ -801,11 +765,11 @@ static struct tty_driver *con3215_device(struct console *c, int *index)
}
/*
- * panic() calls console_unblank before the system enters a
- * disabled, endless loop.
+ * panic() calls con3215_flush through a panic_notifier
+ * before the system enters a disabled, endless loop.
*/
static void
-con3215_unblank(void)
+con3215_flush(void)
{
struct raw3215_info *raw;
unsigned long flags;
@@ -816,6 +780,23 @@ con3215_unblank(void)
spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
}
+static int con3215_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ con3215_flush();
+ return NOTIFY_OK;
+}
+
+static struct notifier_block on_panic_nb = {
+ .notifier_call = con3215_notify,
+ .priority = 0,
+};
+
+static struct notifier_block on_reboot_nb = {
+ .notifier_call = con3215_notify,
+ .priority = 0,
+};
+
/*
* The console structure for the 3215 console
*/
@@ -823,7 +804,6 @@ static struct console con3215 = {
.name = "ttyS",
.write = con3215_write,
.device = con3215_device,
- .unblank = con3215_unblank,
.flags = CON_PRINTBUFFER,
};
@@ -872,9 +852,6 @@ con3215_init(void)
cdev->handler = raw3215_irq;
raw->flags |= RAW3215_FIXED;
- tasklet_init(&raw->tasklet,
- (void (*)(unsigned long)) raw3215_tasklet,
- (unsigned long) raw);
init_waitqueue_head(&raw->empty_wait);
/* Request the console irq */
@@ -883,9 +860,10 @@ con3215_init(void)
free_bootmem((unsigned long) raw->buffer, RAW3215_BUFFER_SIZE);
free_bootmem((unsigned long) raw, sizeof(struct raw3215_info));
raw3215[0] = NULL;
- printk("Couldn't find a 3215 console device\n");
return -ENODEV;
}
+ atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
+ register_reboot_notifier(&on_reboot_nb);
register_console(&con3215);
return 0;
}
@@ -1157,7 +1135,6 @@ tty3215_init(void)
tty_set_operations(driver, &tty3215_ops);
ret = tty_register_driver(driver);
if (ret) {
- printk("Couldn't register tty3215 driver\n");
put_tty_driver(driver);
return ret;
}
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index 0b040557db0..d028d2ee83d 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -15,6 +15,7 @@
#include <linux/list.h>
#include <linux/types.h>
#include <linux/err.h>
+#include <linux/reboot.h>
#include <asm/ccwdev.h>
#include <asm/cio.h>
@@ -411,15 +412,15 @@ static int
con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb)
{
/* Handle ATTN. Schedule tasklet to read aid. */
- if (irb->scsw.dstat & DEV_STAT_ATTENTION)
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
con3270_issue_read(cp);
if (rq) {
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
rq->rc = -EIO;
else
/* Normal end. Copy residual count. */
- rq->rescnt = irb->scsw.count;
+ rq->rescnt = irb->scsw.cmd.count;
}
return RAW3270_IO_DONE;
}
@@ -528,11 +529,11 @@ con3270_wait_write(struct con3270 *cp)
}
/*
- * panic() calls console_unblank before the system enters a
- * disabled, endless loop.
+ * panic() calls con3270_flush through a panic_notifier
+ * before the system enters a disabled, endless loop.
*/
static void
-con3270_unblank(void)
+con3270_flush(void)
{
struct con3270 *cp;
unsigned long flags;
@@ -554,6 +555,23 @@ con3270_unblank(void)
spin_unlock_irqrestore(&cp->view.lock, flags);
}
+static int con3270_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ con3270_flush();
+ return NOTIFY_OK;
+}
+
+static struct notifier_block on_panic_nb = {
+ .notifier_call = con3270_notify,
+ .priority = 0,
+};
+
+static struct notifier_block on_reboot_nb = {
+ .notifier_call = con3270_notify,
+ .priority = 0,
+};
+
/*
* The console structure for the 3270 console
*/
@@ -561,7 +579,6 @@ static struct console con3270 = {
.name = "tty3270",
.write = con3270_write,
.device = con3270_device,
- .unblank = con3270_unblank,
.flags = CON_PRINTBUFFER,
};
@@ -623,6 +640,8 @@ con3270_init(void)
condev->cline->len = 0;
con3270_create_status(condev);
condev->input = alloc_string(&condev->freemem, 80);
+ atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
+ register_reboot_notifier(&on_reboot_nb);
register_console(&con3270);
return 0;
}
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c
index ef36f2132aa..40759c33477 100644
--- a/drivers/s390/char/fs3270.c
+++ b/drivers/s390/char/fs3270.c
@@ -14,6 +14,7 @@
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/types.h>
+#include <linux/smp_lock.h>
#include <asm/ccwdev.h>
#include <asm/cio.h>
@@ -216,17 +217,17 @@ static int
fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb)
{
/* Handle ATTN. Set indication and wake waiters for attention. */
- if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
fp->attention = 1;
wake_up(&fp->wait);
}
if (rq) {
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
rq->rc = -EIO;
else
/* Normal end. Copy residual count. */
- rq->rescnt = irb->scsw.count;
+ rq->rescnt = irb->scsw.cmd.count;
}
return RAW3270_IO_DONE;
}
@@ -417,40 +418,42 @@ fs3270_open(struct inode *inode, struct file *filp)
{
struct fs3270 *fp;
struct idal_buffer *ib;
- int minor, rc;
+ int minor, rc = 0;
if (imajor(filp->f_path.dentry->d_inode) != IBM_FS3270_MAJOR)
return -ENODEV;
minor = iminor(filp->f_path.dentry->d_inode);
/* Check for minor 0 multiplexer. */
if (minor == 0) {
- struct tty_struct *tty;
- mutex_lock(&tty_mutex);
- tty = get_current_tty();
+ struct tty_struct *tty = get_current_tty();
if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) {
- mutex_unlock(&tty_mutex);
+ tty_kref_put(tty);
return -ENODEV;
}
minor = tty->index + RAW3270_FIRSTMINOR;
- mutex_unlock(&tty_mutex);
+ tty_kref_put(tty);
}
+ lock_kernel();
/* Check if some other program is already using fullscreen mode. */
fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor);
if (!IS_ERR(fp)) {
raw3270_put_view(&fp->view);
- return -EBUSY;
+ rc = -EBUSY;
+ goto out;
}
/* Allocate fullscreen view structure. */
fp = fs3270_alloc_view();
- if (IS_ERR(fp))
- return PTR_ERR(fp);
+ if (IS_ERR(fp)) {
+ rc = PTR_ERR(fp);
+ goto out;
+ }
init_waitqueue_head(&fp->wait);
fp->fs_pid = get_pid(task_pid(current));
rc = raw3270_add_view(&fp->view, &fs3270_fn, minor);
if (rc) {
fs3270_free_view(&fp->view);
- return rc;
+ goto out;
}
/* Allocate idal-buffer. */
@@ -458,7 +461,8 @@ fs3270_open(struct inode *inode, struct file *filp)
if (IS_ERR(ib)) {
raw3270_put_view(&fp->view);
raw3270_del_view(&fp->view);
- return PTR_ERR(fp);
+ rc = PTR_ERR(fp);
+ goto out;
}
fp->rdbuf = ib;
@@ -466,10 +470,12 @@ fs3270_open(struct inode *inode, struct file *filp)
if (rc) {
raw3270_put_view(&fp->view);
raw3270_del_view(&fp->view);
- return rc;
+ goto out;
}
filp->private_data = fp;
- return 0;
+out:
+ unlock_kernel();
+ return rc;
}
/*
@@ -512,11 +518,8 @@ fs3270_init(void)
int rc;
rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops);
- if (rc) {
- printk(KERN_ERR "fs3270 can't get major number %d: errno %d\n",
- IBM_FS3270_MAJOR, rc);
+ if (rc)
return rc;
- }
return 0;
}
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index 1e1f50655bb..35fd8dfcaaa 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -3,14 +3,14 @@
*
* Character device driver for reading z/VM *MONITOR service records.
*
- * Copyright 2004 IBM Corporation, IBM Deutschland Entwicklung GmbH.
- *
- * Author: Gerald Schaefer <geraldsc@de.ibm.com>
+ * Copyright IBM Corp. 2004, 2008
+ * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
+#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -18,12 +18,11 @@
#include <linux/ctype.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <net/iucv/iucv.h>
#include <asm/uaccess.h>
#include <asm/ebcdic.h>
#include <asm/extmem.h>
-#include <linux/poll.h>
-#include <net/iucv/iucv.h>
-
//#define MON_DEBUG /* Debug messages on/off */
@@ -152,10 +151,7 @@ static int mon_check_mca(struct mon_msg *monmsg)
(mon_mca_end(monmsg) > mon_dcss_end) ||
(mon_mca_start(monmsg) < mon_dcss_start) ||
((mon_mca_type(monmsg, 1) == 0) && (mon_mca_type(monmsg, 2) == 0)))
- {
- P_DEBUG("READ, IGNORED INVALID MCA\n\n");
return -EINVAL;
- }
return 0;
}
@@ -164,10 +160,6 @@ static int mon_send_reply(struct mon_msg *monmsg,
{
int rc;
- P_DEBUG("read, REPLY: pathid = 0x%04X, msgid = 0x%08X, trgcls = "
- "0x%08X\n\n",
- monpriv->path->pathid, monmsg->msg.id, monmsg->msg.class);
-
rc = iucv_message_reply(monpriv->path, &monmsg->msg,
IUCV_IPRMDATA, NULL, 0);
atomic_dec(&monpriv->msglim_count);
@@ -202,15 +194,12 @@ static struct mon_private *mon_alloc_mem(void)
struct mon_private *monpriv;
monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
- if (!monpriv) {
- P_ERROR("no memory for monpriv\n");
+ if (!monpriv)
return NULL;
- }
for (i = 0; i < MON_MSGLIM; i++) {
monpriv->msg_array[i] = kzalloc(sizeof(struct mon_msg),
GFP_KERNEL);
if (!monpriv->msg_array[i]) {
- P_ERROR("open, no memory for msg_array\n");
mon_free_mem(monpriv);
return NULL;
}
@@ -218,41 +207,10 @@ static struct mon_private *mon_alloc_mem(void)
return monpriv;
}
-static inline void mon_read_debug(struct mon_msg *monmsg,
- struct mon_private *monpriv)
-{
-#ifdef MON_DEBUG
- u8 msg_type[2], mca_type;
- unsigned long records_len;
-
- records_len = mon_rec_end(monmsg) - mon_rec_start(monmsg) + 1;
-
- memcpy(msg_type, &monmsg->msg.class, 2);
- EBCASC(msg_type, 2);
- mca_type = mon_mca_type(monmsg, 0);
- EBCASC(&mca_type, 1);
-
- P_DEBUG("read, mon_read_index = %i, mon_write_index = %i\n",
- monpriv->read_index, monpriv->write_index);
- P_DEBUG("read, pathid = 0x%04X, msgid = 0x%08X, trgcls = 0x%08X\n",
- monpriv->path->pathid, monmsg->msg.id, monmsg->msg.class);
- P_DEBUG("read, msg_type = '%c%c', mca_type = '%c' / 0x%X / 0x%X\n",
- msg_type[0], msg_type[1], mca_type ? mca_type : 'X',
- mon_mca_type(monmsg, 1), mon_mca_type(monmsg, 2));
- P_DEBUG("read, MCA: start = 0x%lX, end = 0x%lX\n",
- mon_mca_start(monmsg), mon_mca_end(monmsg));
- P_DEBUG("read, REC: start = 0x%X, end = 0x%X, len = %lu\n\n",
- mon_rec_start(monmsg), mon_rec_end(monmsg), records_len);
- if (mon_mca_size(monmsg) > 12)
- P_DEBUG("READ, MORE THAN ONE MCA\n\n");
-#endif
-}
-
static inline void mon_next_mca(struct mon_msg *monmsg)
{
if (likely((mon_mca_size(monmsg) - monmsg->mca_offset) == 12))
return;
- P_DEBUG("READ, NEXT MCA\n\n");
monmsg->mca_offset += 12;
monmsg->pos = 0;
}
@@ -269,7 +227,6 @@ static struct mon_msg *mon_next_message(struct mon_private *monpriv)
monmsg->msglim_reached = 0;
monmsg->pos = 0;
monmsg->mca_offset = 0;
- P_WARNING("read, message limit reached\n");
monpriv->read_index = (monpriv->read_index + 1) %
MON_MSGLIM;
atomic_dec(&monpriv->read_ready);
@@ -286,10 +243,6 @@ static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
{
struct mon_private *monpriv = path->private;
- P_DEBUG("IUCV connection completed\n");
- P_DEBUG("IUCV ACCEPT (from *MONITOR): Version = 0x%02X, Event = "
- "0x%02X, Sample = 0x%02X\n",
- ipuser[0], ipuser[1], ipuser[2]);
atomic_set(&monpriv->iucv_connected, 1);
wake_up(&mon_conn_wait_queue);
}
@@ -310,7 +263,6 @@ static void mon_iucv_message_pending(struct iucv_path *path,
{
struct mon_private *monpriv = path->private;
- P_DEBUG("IUCV message pending\n");
memcpy(&monpriv->msg_array[monpriv->write_index]->msg,
msg, sizeof(*msg));
if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) {
@@ -340,6 +292,7 @@ static int mon_open(struct inode *inode, struct file *filp)
/*
* only one user allowed
*/
+ lock_kernel();
rc = -EBUSY;
if (test_and_set_bit(MON_IN_USE, &mon_in_use))
goto out;
@@ -375,8 +328,8 @@ static int mon_open(struct inode *inode, struct file *filp)
rc = -EIO;
goto out_path;
}
- P_INFO("open, established connection to *MONITOR service\n\n");
filp->private_data = monpriv;
+ unlock_kernel();
return nonseekable_open(inode, filp);
out_path:
@@ -386,6 +339,7 @@ out_priv:
out_use:
clear_bit(MON_IN_USE, &mon_in_use);
out:
+ unlock_kernel();
return rc;
}
@@ -400,8 +354,6 @@ static int mon_close(struct inode *inode, struct file *filp)
rc = iucv_path_sever(monpriv->path, user_data_sever);
if (rc)
P_ERROR("close, iucv_sever failed with rc = %i\n", rc);
- else
- P_INFO("close, terminated connection to *MONITOR service\n");
atomic_set(&monpriv->iucv_severed, 0);
atomic_set(&monpriv->iucv_connected, 0);
@@ -442,10 +394,8 @@ static ssize_t mon_read(struct file *filp, char __user *data,
monmsg = monpriv->msg_array[monpriv->read_index];
}
- if (!monmsg->pos) {
+ if (!monmsg->pos)
monmsg->pos = mon_mca_start(monmsg) + monmsg->mca_offset;
- mon_read_debug(monmsg, monpriv);
- }
if (mon_check_mca(monmsg))
goto reply;
@@ -531,7 +481,6 @@ static int __init mon_init(void)
P_ERROR("failed to register with iucv driver\n");
return rc;
}
- P_INFO("open, registered with IUCV\n");
rc = segment_type(mon_dcss_name);
if (rc < 0) {
@@ -555,13 +504,8 @@ static int __init mon_init(void)
dcss_mkname(mon_dcss_name, &user_data_connect[8]);
rc = misc_register(&mon_dev);
- if (rc < 0 ) {
- P_ERROR("misc_register failed, rc = %i\n", rc);
+ if (rc < 0 )
goto out;
- }
- P_INFO("Loaded segment %s from %p to %p, size = %lu Byte\n",
- mon_dcss_name, (void *) mon_dcss_start, (void *) mon_dcss_end,
- mon_dcss_end - mon_dcss_start + 1);
return 0;
out:
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
index a86c0534cd4..4d71aa8c1a7 100644
--- a/drivers/s390/char/monwriter.c
+++ b/drivers/s390/char/monwriter.c
@@ -12,6 +12,7 @@
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/errno.h>
+#include <linux/smp_lock.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
@@ -179,10 +180,12 @@ static int monwrite_open(struct inode *inode, struct file *filp)
monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
if (!monpriv)
return -ENOMEM;
+ lock_kernel();
INIT_LIST_HEAD(&monpriv->list);
monpriv->hdr_to_read = sizeof(monpriv->hdr);
mutex_init(&monpriv->thread_mutex);
filp->private_data = monpriv;
+ unlock_kernel();
return nonseekable_open(inode, filp);
}
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c
index 848ef7e8523..1792b2c0130 100644
--- a/drivers/s390/char/raw3270.c
+++ b/drivers/s390/char/raw3270.c
@@ -153,19 +153,10 @@ struct raw3270_request __init *raw3270_request_alloc_bootmem(size_t size)
struct raw3270_request *rq;
rq = alloc_bootmem_low(sizeof(struct raw3270));
- if (!rq)
- return ERR_PTR(-ENOMEM);
- memset(rq, 0, sizeof(struct raw3270_request));
/* alloc output buffer. */
- if (size > 0) {
+ if (size > 0)
rq->buffer = alloc_bootmem_low(size);
- if (!rq->buffer) {
- free_bootmem((unsigned long) rq,
- sizeof(struct raw3270));
- return ERR_PTR(-ENOMEM);
- }
- }
rq->size = size;
INIT_LIST_HEAD(&rq->list);
@@ -372,17 +363,17 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
if (IS_ERR(irb))
rc = RAW3270_IO_RETRY;
- else if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) {
+ else if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) {
rq->rc = -EIO;
rc = RAW3270_IO_DONE;
- } else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END |
- DEV_STAT_UNIT_EXCEP)) {
+ } else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END |
+ DEV_STAT_UNIT_EXCEP)) {
/* Handle CE-DE-UE and subsequent UDE */
set_bit(RAW3270_FLAGS_BUSY, &rp->flags);
rc = RAW3270_IO_BUSY;
} else if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) {
/* Wait for UDE if busy flag is set. */
- if (irb->scsw.dstat & DEV_STAT_DEV_END) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
clear_bit(RAW3270_FLAGS_BUSY, &rp->flags);
/* Got it, now retry. */
rc = RAW3270_IO_RETRY;
@@ -497,7 +488,7 @@ raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq,
* Unit-Check Processing:
* Expect Command Reject or Intervention Required.
*/
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
/* Request finished abnormally. */
if (irb->ecw[0] & SNS0_INTERVENTION_REQ) {
set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags);
@@ -505,16 +496,16 @@ raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq,
}
}
if (rq) {
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
if (irb->ecw[0] & SNS0_CMD_REJECT)
rq->rc = -EOPNOTSUPP;
else
rq->rc = -EIO;
} else
/* Request finished normally. Copy residual count. */
- rq->rescnt = irb->scsw.count;
+ rq->rescnt = irb->scsw.cmd.count;
}
- if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
set_bit(RAW3270_FLAGS_ATTN, &view->dev->flags);
wake_up(&raw3270_wait_queue);
}
@@ -619,7 +610,6 @@ __raw3270_size_device_vm(struct raw3270 *rp)
rp->cols = 132;
break;
default:
- printk(KERN_WARNING "vrdccrmd is 0x%.8x\n", model);
rc = -EOPNOTSUPP;
break;
}
@@ -1178,17 +1168,19 @@ static int raw3270_create_attributes(struct raw3270 *rp)
if (rc)
goto out;
- rp->clttydev = device_create(class3270, &rp->cdev->dev,
- MKDEV(IBM_TTY3270_MAJOR, rp->minor),
- "tty%s", rp->cdev->dev.bus_id);
+ rp->clttydev = device_create_drvdata(class3270, &rp->cdev->dev,
+ MKDEV(IBM_TTY3270_MAJOR, rp->minor),
+ NULL,
+ "tty%s", dev_name(&rp->cdev->dev));
if (IS_ERR(rp->clttydev)) {
rc = PTR_ERR(rp->clttydev);
goto out_ttydev;
}
- rp->cltubdev = device_create(class3270, &rp->cdev->dev,
- MKDEV(IBM_FS3270_MAJOR, rp->minor),
- "tub%s", rp->cdev->dev.bus_id);
+ rp->cltubdev = device_create_drvdata(class3270, &rp->cdev->dev,
+ MKDEV(IBM_FS3270_MAJOR, rp->minor),
+ NULL,
+ "tub%s", dev_name(&rp->cdev->dev));
if (!IS_ERR(rp->cltubdev))
goto out;
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 2c7a1ee6b04..1fd8f2193ed 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -399,6 +399,7 @@ sclp_tod_from_jiffies(unsigned long jiffies)
void
sclp_sync_wait(void)
{
+ unsigned long long old_tick;
unsigned long flags;
unsigned long cr0, cr0_sync;
u64 timeout;
@@ -419,11 +420,12 @@ sclp_sync_wait(void)
if (!irq_context)
local_bh_disable();
/* Enable service-signal interruption, disable timer interrupts */
+ old_tick = local_tick_disable();
trace_hardirqs_on();
__ctl_store(cr0, 0, 0);
cr0_sync = cr0;
+ cr0_sync &= 0xffff00a0;
cr0_sync |= 0x00000200;
- cr0_sync &= 0xFFFFF3AC;
__ctl_load(cr0_sync, 0, 0);
__raw_local_irq_stosm(0x01);
/* Loop until driver state indicates finished request */
@@ -439,9 +441,9 @@ sclp_sync_wait(void)
__ctl_load(cr0, 0, 0);
if (!irq_context)
_local_bh_enable();
+ local_tick_enable(old_tick);
local_irq_restore(flags);
}
-
EXPORT_SYMBOL(sclp_sync_wait);
/* Dispatch changes in send and receive mask to registered listeners. */
@@ -506,6 +508,8 @@ sclp_state_change_cb(struct evbuf_header *evbuf)
if (scbuf->validity_sclp_send_mask)
sclp_send_mask = scbuf->sclp_send_mask;
spin_unlock_irqrestore(&sclp_lock, flags);
+ if (scbuf->validity_sclp_active_facility_mask)
+ sclp_facilities = scbuf->sclp_active_facility_mask;
sclp_dispatch_state_change();
}
@@ -782,11 +786,9 @@ sclp_check_handler(__u16 code)
/* Is this the interrupt we are waiting for? */
if (finished_sccb == 0)
return;
- if (finished_sccb != (u32) (addr_t) sclp_init_sccb) {
- printk(KERN_WARNING SCLP_HEADER "unsolicited interrupt "
- "for buffer at 0x%x\n", finished_sccb);
- return;
- }
+ if (finished_sccb != (u32) (addr_t) sclp_init_sccb)
+ panic("sclp: unsolicited interrupt for buffer at 0x%x\n",
+ finished_sccb);
spin_lock(&sclp_lock);
if (sclp_running_state == sclp_running_state_running) {
sclp_init_req.status = SCLP_REQ_DONE;
@@ -883,8 +885,6 @@ sclp_init(void)
unsigned long flags;
int rc;
- if (!MACHINE_HAS_SCLP)
- return -ENODEV;
spin_lock_irqsave(&sclp_lock, flags);
/* Check for previous or running initialization */
if (sclp_init_state != sclp_init_state_uninitialized) {
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index b5c23396f8f..eb5f1b8bc57 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -11,6 +11,9 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/mmzone.h>
+#include <linux/memory.h>
#include <asm/chpid.h>
#include <asm/sclp.h>
#include "sclp.h"
@@ -43,6 +46,8 @@ static int __initdata early_read_info_sccb_valid;
u64 sclp_facilities;
static u8 sclp_fac84;
+static unsigned long long rzm;
+static unsigned long long rnmax;
static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
{
@@ -62,7 +67,7 @@ out:
return rc;
}
-void __init sclp_read_info_early(void)
+static void __init sclp_read_info_early(void)
{
int rc;
int i;
@@ -92,34 +97,33 @@ void __init sclp_read_info_early(void)
void __init sclp_facilities_detect(void)
{
+ struct read_info_sccb *sccb;
+
+ sclp_read_info_early();
if (!early_read_info_sccb_valid)
return;
- sclp_facilities = early_read_info_sccb.facilities;
- sclp_fac84 = early_read_info_sccb.fac84;
+
+ sccb = &early_read_info_sccb;
+ sclp_facilities = sccb->facilities;
+ sclp_fac84 = sccb->fac84;
+ rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
+ rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
+ rzm <<= 20;
}
-unsigned long long __init sclp_memory_detect(void)
+unsigned long long sclp_get_rnmax(void)
{
- unsigned long long memsize;
- struct read_info_sccb *sccb;
+ return rnmax;
+}
- if (!early_read_info_sccb_valid)
- return 0;
- sccb = &early_read_info_sccb;
- if (sccb->rnsize)
- memsize = sccb->rnsize << 20;
- else
- memsize = sccb->rnsize2 << 20;
- if (sccb->rnmax)
- memsize *= sccb->rnmax;
- else
- memsize *= sccb->rnmax2;
- return memsize;
+unsigned long long sclp_get_rzm(void)
+{
+ return rzm;
}
/*
- * This function will be called after sclp_memory_detect(), which gets called
- * early from early.c code. Therefore the sccb should have valid contents.
+ * This function will be called after sclp_facilities_detect(), which gets
+ * called from early.c code. Therefore the sccb should have valid contents.
*/
void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
{
@@ -278,6 +282,310 @@ int sclp_cpu_deconfigure(u8 cpu)
return do_cpu_configure(SCLP_CMDW_DECONFIGURE_CPU | cpu << 8);
}
+#ifdef CONFIG_MEMORY_HOTPLUG
+
+static DEFINE_MUTEX(sclp_mem_mutex);
+static LIST_HEAD(sclp_mem_list);
+static u8 sclp_max_storage_id;
+static unsigned long sclp_storage_ids[256 / BITS_PER_LONG];
+
+struct memory_increment {
+ struct list_head list;
+ u16 rn;
+ int standby;
+ int usecount;
+};
+
+struct assign_storage_sccb {
+ struct sccb_header header;
+ u16 rn;
+} __packed;
+
+static unsigned long long rn2addr(u16 rn)
+{
+ return (unsigned long long) (rn - 1) * rzm;
+}
+
+static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
+{
+ struct assign_storage_sccb *sccb;
+ int rc;
+
+ sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ return -ENOMEM;
+ sccb->header.length = PAGE_SIZE;
+ sccb->rn = rn;
+ rc = do_sync_request(cmd, sccb);
+ if (rc)
+ goto out;
+ switch (sccb->header.response_code) {
+ case 0x0020:
+ case 0x0120:
+ break;
+ default:
+ rc = -EIO;
+ break;
+ }
+out:
+ free_page((unsigned long) sccb);
+ return rc;
+}
+
+static int sclp_assign_storage(u16 rn)
+{
+ return do_assign_storage(0x000d0001, rn);
+}
+
+static int sclp_unassign_storage(u16 rn)
+{
+ return do_assign_storage(0x000c0001, rn);
+}
+
+struct attach_storage_sccb {
+ struct sccb_header header;
+ u16 :16;
+ u16 assigned;
+ u32 :32;
+ u32 entries[0];
+} __packed;
+
+static int sclp_attach_storage(u8 id)
+{
+ struct attach_storage_sccb *sccb;
+ int rc;
+ int i;
+
+ sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ return -ENOMEM;
+ sccb->header.length = PAGE_SIZE;
+ rc = do_sync_request(0x00080001 | id << 8, sccb);
+ if (rc)
+ goto out;
+ switch (sccb->header.response_code) {
+ case 0x0020:
+ set_bit(id, sclp_storage_ids);
+ for (i = 0; i < sccb->assigned; i++)
+ sclp_unassign_storage(sccb->entries[i] >> 16);
+ break;
+ default:
+ rc = -EIO;
+ break;
+ }
+out:
+ free_page((unsigned long) sccb);
+ return rc;
+}
+
+static int sclp_mem_change_state(unsigned long start, unsigned long size,
+ int online)
+{
+ struct memory_increment *incr;
+ unsigned long long istart;
+ int rc = 0;
+
+ list_for_each_entry(incr, &sclp_mem_list, list) {
+ istart = rn2addr(incr->rn);
+ if (start + size - 1 < istart)
+ break;
+ if (start > istart + rzm - 1)
+ continue;
+ if (online) {
+ if (incr->usecount++)
+ continue;
+ /*
+ * Don't break the loop if one assign fails. Loop may
+ * be walked again on CANCEL and we can't save
+ * information if state changed before or not.
+ * So continue and increase usecount for all increments.
+ */
+ rc |= sclp_assign_storage(incr->rn);
+ } else {
+ if (--incr->usecount)
+ continue;
+ sclp_unassign_storage(incr->rn);
+ }
+ }
+ return rc ? -EIO : 0;
+}
+
+static int sclp_mem_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ unsigned long start, size;
+ struct memory_notify *arg;
+ unsigned char id;
+ int rc = 0;
+
+ arg = data;
+ start = arg->start_pfn << PAGE_SHIFT;
+ size = arg->nr_pages << PAGE_SHIFT;
+ mutex_lock(&sclp_mem_mutex);
+ for (id = 0; id <= sclp_max_storage_id; id++)
+ if (!test_bit(id, sclp_storage_ids))
+ sclp_attach_storage(id);
+ switch (action) {
+ case MEM_ONLINE:
+ case MEM_GOING_OFFLINE:
+ case MEM_CANCEL_OFFLINE:
+ break;
+ case MEM_GOING_ONLINE:
+ rc = sclp_mem_change_state(start, size, 1);
+ break;
+ case MEM_CANCEL_ONLINE:
+ sclp_mem_change_state(start, size, 0);
+ break;
+ case MEM_OFFLINE:
+ sclp_mem_change_state(start, size, 0);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ mutex_unlock(&sclp_mem_mutex);
+ return rc ? NOTIFY_BAD : NOTIFY_OK;
+}
+
+static struct notifier_block sclp_mem_nb = {
+ .notifier_call = sclp_mem_notifier,
+};
+
+static void __init add_memory_merged(u16 rn)
+{
+ static u16 first_rn, num;
+ unsigned long long start, size;
+
+ if (rn && first_rn && (first_rn + num == rn)) {
+ num++;
+ return;
+ }
+ if (!first_rn)
+ goto skip_add;
+ start = rn2addr(first_rn);
+ size = (unsigned long long ) num * rzm;
+ if (start >= VMEM_MAX_PHYS)
+ goto skip_add;
+ if (start + size > VMEM_MAX_PHYS)
+ size = VMEM_MAX_PHYS - start;
+ add_memory(0, start, size);
+skip_add:
+ first_rn = rn;
+ num = 1;
+}
+
+static void __init sclp_add_standby_memory(void)
+{
+ struct memory_increment *incr;
+
+ list_for_each_entry(incr, &sclp_mem_list, list)
+ if (incr->standby)
+ add_memory_merged(incr->rn);
+ add_memory_merged(0);
+}
+
+static void __init insert_increment(u16 rn, int standby, int assigned)
+{
+ struct memory_increment *incr, *new_incr;
+ struct list_head *prev;
+ u16 last_rn;
+
+ new_incr = kzalloc(sizeof(*new_incr), GFP_KERNEL);
+ if (!new_incr)
+ return;
+ new_incr->rn = rn;
+ new_incr->standby = standby;
+ last_rn = 0;
+ prev = &sclp_mem_list;
+ list_for_each_entry(incr, &sclp_mem_list, list) {
+ if (assigned && incr->rn > rn)
+ break;
+ if (!assigned && incr->rn - last_rn > 1)
+ break;
+ last_rn = incr->rn;
+ prev = &incr->list;
+ }
+ if (!assigned)
+ new_incr->rn = last_rn + 1;
+ if (new_incr->rn > rnmax) {
+ kfree(new_incr);
+ return;
+ }
+ list_add(&new_incr->list, prev);
+}
+
+struct read_storage_sccb {
+ struct sccb_header header;
+ u16 max_id;
+ u16 assigned;
+ u16 standby;
+ u16 :16;
+ u32 entries[0];
+} __packed;
+
+static int __init sclp_detect_standby_memory(void)
+{
+ struct read_storage_sccb *sccb;
+ int i, id, assigned, rc;
+
+ if (!early_read_info_sccb_valid)
+ return 0;
+ if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL)
+ return 0;
+ rc = -ENOMEM;
+ sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ goto out;
+ assigned = 0;
+ for (id = 0; id <= sclp_max_storage_id; id++) {
+ memset(sccb, 0, PAGE_SIZE);
+ sccb->header.length = PAGE_SIZE;
+ rc = do_sync_request(0x00040001 | id << 8, sccb);
+ if (rc)
+ goto out;
+ switch (sccb->header.response_code) {
+ case 0x0010:
+ set_bit(id, sclp_storage_ids);
+ for (i = 0; i < sccb->assigned; i++) {
+ if (!sccb->entries[i])
+ continue;
+ assigned++;
+ insert_increment(sccb->entries[i] >> 16, 0, 1);
+ }
+ break;
+ case 0x0310:
+ break;
+ case 0x0410:
+ for (i = 0; i < sccb->assigned; i++) {
+ if (!sccb->entries[i])
+ continue;
+ assigned++;
+ insert_increment(sccb->entries[i] >> 16, 1, 1);
+ }
+ break;
+ default:
+ rc = -EIO;
+ break;
+ }
+ if (!rc)
+ sclp_max_storage_id = sccb->max_id;
+ }
+ if (rc || list_empty(&sclp_mem_list))
+ goto out;
+ for (i = 1; i <= rnmax - assigned; i++)
+ insert_increment(0, 1, 0);
+ rc = register_memory_notifier(&sclp_mem_nb);
+ if (rc)
+ goto out;
+ sclp_add_standby_memory();
+out:
+ free_page((unsigned long) sccb);
+ return rc;
+}
+__initcall(sclp_detect_standby_memory);
+
+#endif /* CONFIG_MEMORY_HOTPLUG */
+
/*
* Channel path configuration related functions.
*/
diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c
index ead1043d788..9a25c4bd142 100644
--- a/drivers/s390/char/sclp_con.c
+++ b/drivers/s390/char/sclp_con.c
@@ -14,14 +14,14 @@
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/bootmem.h>
+#include <linux/termios.h>
#include <linux/err.h>
+#include <linux/reboot.h>
#include "sclp.h"
#include "sclp_rw.h"
#include "sclp_tty.h"
-#define SCLP_CON_PRINT_HEADER "sclp console driver: "
-
#define sclp_console_major 4 /* TTYAUX_MAJOR */
#define sclp_console_minor 64
#define sclp_console_name "ttyS"
@@ -173,7 +173,7 @@ sclp_console_device(struct console *c, int *index)
* will be flushed to the SCLP.
*/
static void
-sclp_console_unblank(void)
+sclp_console_flush(void)
{
unsigned long flags;
@@ -189,6 +189,24 @@ sclp_console_unblank(void)
spin_unlock_irqrestore(&sclp_con_lock, flags);
}
+static int
+sclp_console_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ sclp_console_flush();
+ return NOTIFY_OK;
+}
+
+static struct notifier_block on_panic_nb = {
+ .notifier_call = sclp_console_notify,
+ .priority = 1,
+};
+
+static struct notifier_block on_reboot_nb = {
+ .notifier_call = sclp_console_notify,
+ .priority = 1,
+};
+
/*
* used to register the SCLP console to the kernel and to
* give printk necessary information
@@ -198,7 +216,6 @@ static struct console sclp_console =
.name = sclp_console_name,
.write = sclp_console_write,
.device = sclp_console_device,
- .unblank = sclp_console_unblank,
.flags = CON_PRINTBUFFER,
.index = 0 /* ttyS0 */
};
@@ -222,8 +239,6 @@ sclp_console_init(void)
INIT_LIST_HEAD(&sclp_con_pages);
for (i = 0; i < MAX_CONSOLE_PAGES; i++) {
page = alloc_bootmem_low_pages(PAGE_SIZE);
- if (page == NULL)
- return -ENOMEM;
list_add_tail((struct list_head *) page, &sclp_con_pages);
}
INIT_LIST_HEAD(&sclp_con_outqueue);
@@ -244,6 +259,8 @@ sclp_console_init(void)
sclp_con_width_htab = 8;
/* enable printk-access to this driver */
+ atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
+ register_reboot_notifier(&on_reboot_nb);
register_console(&sclp_console);
return 0;
}
diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c
index ad05a87bc48..4cebd6ee6d2 100644
--- a/drivers/s390/char/sclp_config.c
+++ b/drivers/s390/char/sclp_config.c
@@ -74,10 +74,8 @@ static int __init sclp_conf_init(void)
INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify);
rc = sclp_register(&sclp_conf_register);
- if (rc) {
- printk(KERN_ERR TAG "failed to register (%d).\n", rc);
+ if (rc)
return rc;
- }
if (!(sclp_conf_register.sclp_send_mask & EVTYP_CONFMGMDATA_MASK)) {
printk(KERN_WARNING TAG "no configuration management.\n");
diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c
index 9f37456222e..d887bd261d2 100644
--- a/drivers/s390/char/sclp_cpi_sys.c
+++ b/drivers/s390/char/sclp_cpi_sys.c
@@ -27,6 +27,8 @@
#define CPI_LENGTH_NAME 8
#define CPI_LENGTH_LEVEL 16
+static DEFINE_MUTEX(sclp_cpi_mutex);
+
struct cpi_evbuf {
struct evbuf_header header;
u8 id_format;
@@ -124,21 +126,15 @@ static int cpi_req(void)
int response;
rc = sclp_register(&sclp_cpi_event);
- if (rc) {
- printk(KERN_WARNING "cpi: could not register "
- "to hardware console.\n");
+ if (rc)
goto out;
- }
if (!(sclp_cpi_event.sclp_receive_mask & EVTYP_CTLPROGIDENT_MASK)) {
- printk(KERN_WARNING "cpi: no control program "
- "identification support\n");
rc = -EOPNOTSUPP;
goto out_unregister;
}
req = cpi_prepare_req();
if (IS_ERR(req)) {
- printk(KERN_WARNING "cpi: could not allocate request\n");
rc = PTR_ERR(req);
goto out_unregister;
}
@@ -148,10 +144,8 @@ static int cpi_req(void)
/* Add request to sclp queue */
rc = sclp_add_request(req);
- if (rc) {
- printk(KERN_WARNING "cpi: could not start request\n");
+ if (rc)
goto out_free_req;
- }
wait_for_completion(&completion);
@@ -223,7 +217,12 @@ static void set_string(char *attr, const char *value)
static ssize_t system_name_show(struct kobject *kobj,
struct kobj_attribute *attr, char *page)
{
- return snprintf(page, PAGE_SIZE, "%s\n", system_name);
+ int rc;
+
+ mutex_lock(&sclp_cpi_mutex);
+ rc = snprintf(page, PAGE_SIZE, "%s\n", system_name);
+ mutex_unlock(&sclp_cpi_mutex);
+ return rc;
}
static ssize_t system_name_store(struct kobject *kobj,
@@ -237,7 +236,9 @@ static ssize_t system_name_store(struct kobject *kobj,
if (rc)
return rc;
+ mutex_lock(&sclp_cpi_mutex);
set_string(system_name, buf);
+ mutex_unlock(&sclp_cpi_mutex);
return len;
}
@@ -248,7 +249,12 @@ static struct kobj_attribute system_name_attr =
static ssize_t sysplex_name_show(struct kobject *kobj,
struct kobj_attribute *attr, char *page)
{
- return snprintf(page, PAGE_SIZE, "%s\n", sysplex_name);
+ int rc;
+
+ mutex_lock(&sclp_cpi_mutex);
+ rc = snprintf(page, PAGE_SIZE, "%s\n", sysplex_name);
+ mutex_unlock(&sclp_cpi_mutex);
+ return rc;
}
static ssize_t sysplex_name_store(struct kobject *kobj,
@@ -262,7 +268,9 @@ static ssize_t sysplex_name_store(struct kobject *kobj,
if (rc)
return rc;
+ mutex_lock(&sclp_cpi_mutex);
set_string(sysplex_name, buf);
+ mutex_unlock(&sclp_cpi_mutex);
return len;
}
@@ -273,7 +281,12 @@ static struct kobj_attribute sysplex_name_attr =
static ssize_t system_type_show(struct kobject *kobj,
struct kobj_attribute *attr, char *page)
{
- return snprintf(page, PAGE_SIZE, "%s\n", system_type);
+ int rc;
+
+ mutex_lock(&sclp_cpi_mutex);
+ rc = snprintf(page, PAGE_SIZE, "%s\n", system_type);
+ mutex_unlock(&sclp_cpi_mutex);
+ return rc;
}
static ssize_t system_type_store(struct kobject *kobj,
@@ -287,7 +300,9 @@ static ssize_t system_type_store(struct kobject *kobj,
if (rc)
return rc;
+ mutex_lock(&sclp_cpi_mutex);
set_string(system_type, buf);
+ mutex_unlock(&sclp_cpi_mutex);
return len;
}
@@ -298,8 +313,11 @@ static struct kobj_attribute system_type_attr =
static ssize_t system_level_show(struct kobject *kobj,
struct kobj_attribute *attr, char *page)
{
- unsigned long long level = system_level;
+ unsigned long long level;
+ mutex_lock(&sclp_cpi_mutex);
+ level = system_level;
+ mutex_unlock(&sclp_cpi_mutex);
return snprintf(page, PAGE_SIZE, "%#018llx\n", level);
}
@@ -320,8 +338,9 @@ static ssize_t system_level_store(struct kobject *kobj,
if (*endp)
return -EINVAL;
+ mutex_lock(&sclp_cpi_mutex);
system_level = level;
-
+ mutex_unlock(&sclp_cpi_mutex);
return len;
}
@@ -334,7 +353,9 @@ static ssize_t set_store(struct kobject *kobj,
{
int rc;
+ mutex_lock(&sclp_cpi_mutex);
rc = cpi_req();
+ mutex_unlock(&sclp_cpi_mutex);
if (rc)
return rc;
@@ -373,12 +394,16 @@ int sclp_cpi_set_data(const char *system, const char *sysplex, const char *type,
if (rc)
return rc;
+ mutex_lock(&sclp_cpi_mutex);
set_string(system_name, system);
set_string(sysplex_name, sysplex);
set_string(system_type, type);
system_level = level;
- return cpi_req();
+ rc = cpi_req();
+ mutex_unlock(&sclp_cpi_mutex);
+
+ return rc;
}
EXPORT_SYMBOL(sclp_cpi_set_data);
diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c
index 45ff25e787c..84c191c1cd6 100644
--- a/drivers/s390/char/sclp_quiesce.c
+++ b/drivers/s390/char/sclp_quiesce.c
@@ -51,13 +51,7 @@ static struct sclp_register sclp_quiesce_event = {
static int __init
sclp_quiesce_init(void)
{
- int rc;
-
- rc = sclp_register(&sclp_quiesce_event);
- if (rc)
- printk(KERN_WARNING "sclp: could not register quiesce handler "
- "(rc=%d)\n", rc);
- return rc;
+ return sclp_register(&sclp_quiesce_event);
}
module_init(sclp_quiesce_init);
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index da09781b32f..710af42603f 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -19,8 +19,6 @@
#include "sclp.h"
#include "sclp_rw.h"
-#define SCLP_RW_PRINT_HEADER "sclp low level driver: "
-
/*
* The room for the SCCB (only for writing) is not equal to a pages size
* (as it is specified as the maximum size in the SCLP documentation)
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
index 1c064976b32..8b854857ba0 100644
--- a/drivers/s390/char/sclp_sdias.c
+++ b/drivers/s390/char/sclp_sdias.c
@@ -239,10 +239,8 @@ int __init sclp_sdias_init(void)
debug_register_view(sdias_dbf, &debug_sprintf_view);
debug_set_level(sdias_dbf, 6);
rc = sclp_register(&sclp_sdias_register);
- if (rc) {
- ERROR_MSG("sclp register failed\n");
+ if (rc)
return rc;
- }
init_waitqueue_head(&sdias_wq);
TRACE("init done\n");
return 0;
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index 40b11521cd2..434ba04b130 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -13,7 +13,6 @@
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
-#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/init.h>
@@ -25,8 +24,6 @@
#include "sclp_rw.h"
#include "sclp_tty.h"
-#define SCLP_TTY_PRINT_HEADER "sclp tty driver: "
-
/*
* size of a buffer that collects single characters coming in
* via sclp_tty_put_char()
@@ -50,8 +47,6 @@ static int sclp_tty_buffer_count;
static struct sclp_buffer *sclp_ttybuf;
/* Timer for delayed output of console messages. */
static struct timer_list sclp_tty_timer;
-/* Waitqueue to wait for buffers to get empty. */
-static wait_queue_head_t sclp_tty_waitq;
static struct tty_struct *sclp_tty;
static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE];
@@ -59,19 +54,11 @@ static unsigned short int sclp_tty_chars_count;
struct tty_driver *sclp_tty_driver;
-static struct sclp_ioctls sclp_ioctls;
-static struct sclp_ioctls sclp_ioctls_init =
-{
- 8, /* 1 hor. tab. = 8 spaces */
- 0, /* no echo of input by this driver */
- 80, /* 80 characters/line */
- 1, /* write after 1/10 s without final new line */
- MAX_KMEM_PAGES, /* quick fix: avoid __alloc_pages */
- MAX_KMEM_PAGES, /* take 32/64 pages from kernel memory, */
- 0, /* do not convert to lower case */
- 0x6c /* to seprate upper and lower case */
- /* ('%' in EBCDIC) */
-};
+static int sclp_tty_tolower;
+static int sclp_tty_columns = 80;
+
+#define SPACES_PER_TAB 8
+#define CASE_DELIMITER 0x6c /* to separate upper and lower case (% in EBCDIC) */
/* This routine is called whenever we try to open a SCLP terminal. */
static int
@@ -92,136 +79,6 @@ sclp_tty_close(struct tty_struct *tty, struct file *filp)
sclp_tty = NULL;
}
-/* execute commands to control the i/o behaviour of the SCLP tty at runtime */
-static int
-sclp_tty_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg)
-{
- unsigned long flags;
- unsigned int obuf;
- int check;
- int rc;
-
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
- rc = 0;
- check = 0;
- switch (cmd) {
- case TIOCSCLPSHTAB:
- /* set width of horizontal tab */
- if (get_user(sclp_ioctls.htab, (unsigned short __user *) arg))
- rc = -EFAULT;
- else
- check = 1;
- break;
- case TIOCSCLPGHTAB:
- /* get width of horizontal tab */
- if (put_user(sclp_ioctls.htab, (unsigned short __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPSECHO:
- /* enable/disable echo of input */
- if (get_user(sclp_ioctls.echo, (unsigned char __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPGECHO:
- /* Is echo of input enabled ? */
- if (put_user(sclp_ioctls.echo, (unsigned char __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPSCOLS:
- /* set number of columns for output */
- if (get_user(sclp_ioctls.columns, (unsigned short __user *) arg))
- rc = -EFAULT;
- else
- check = 1;
- break;
- case TIOCSCLPGCOLS:
- /* get number of columns for output */
- if (put_user(sclp_ioctls.columns, (unsigned short __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPSNL:
- /* enable/disable writing without final new line character */
- if (get_user(sclp_ioctls.final_nl, (signed char __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPGNL:
- /* Is writing without final new line character enabled ? */
- if (put_user(sclp_ioctls.final_nl, (signed char __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPSOBUF:
- /*
- * set the maximum buffers size for output, will be rounded
- * up to next 4kB boundary and stored as number of SCCBs
- * (4kB Buffers) limitation: 256 x 4kB
- */
- if (get_user(obuf, (unsigned int __user *) arg) == 0) {
- if (obuf & 0xFFF)
- sclp_ioctls.max_sccb = (obuf >> 12) + 1;
- else
- sclp_ioctls.max_sccb = (obuf >> 12);
- } else
- rc = -EFAULT;
- break;
- case TIOCSCLPGOBUF:
- /* get the maximum buffers size for output */
- obuf = sclp_ioctls.max_sccb << 12;
- if (put_user(obuf, (unsigned int __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPGKBUF:
- /* get the number of buffers got from kernel at startup */
- if (put_user(sclp_ioctls.kmem_sccb, (unsigned short __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPSCASE:
- /* enable/disable conversion from upper to lower case */
- if (get_user(sclp_ioctls.tolower, (unsigned char __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPGCASE:
- /* Is conversion from upper to lower case of input enabled? */
- if (put_user(sclp_ioctls.tolower, (unsigned char __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPSDELIM:
- /*
- * set special character used for separating upper and
- * lower case, 0x00 disables this feature
- */
- if (get_user(sclp_ioctls.delim, (unsigned char __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPGDELIM:
- /*
- * get special character used for separating upper and
- * lower case, 0x00 disables this feature
- */
- if (put_user(sclp_ioctls.delim, (unsigned char __user *) arg))
- rc = -EFAULT;
- break;
- case TIOCSCLPSINIT:
- /* set initial (default) sclp ioctls */
- sclp_ioctls = sclp_ioctls_init;
- check = 1;
- break;
- default:
- rc = -ENOIOCTLCMD;
- break;
- }
- if (check) {
- spin_lock_irqsave(&sclp_tty_lock, flags);
- if (sclp_ttybuf != NULL) {
- sclp_set_htab(sclp_ttybuf, sclp_ioctls.htab);
- sclp_set_columns(sclp_ttybuf, sclp_ioctls.columns);
- }
- spin_unlock_irqrestore(&sclp_tty_lock, flags);
- }
- return rc;
-}
-
/*
* This routine returns the numbers of characters the tty driver
* will accept for queuing to be written. This number is subject
@@ -268,7 +125,6 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
struct sclp_buffer, list);
spin_unlock_irqrestore(&sclp_tty_lock, flags);
} while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback));
- wake_up(&sclp_tty_waitq);
/* check if the tty needs a wake up call */
if (sclp_tty != NULL) {
tty_wakeup(sclp_tty);
@@ -316,37 +172,37 @@ sclp_tty_timeout(unsigned long data)
/*
* Write a string to the sclp tty.
*/
-static void
-sclp_tty_write_string(const unsigned char *str, int count)
+static int sclp_tty_write_string(const unsigned char *str, int count, int may_fail)
{
unsigned long flags;
void *page;
int written;
+ int overall_written;
struct sclp_buffer *buf;
if (count <= 0)
- return;
+ return 0;
+ overall_written = 0;
spin_lock_irqsave(&sclp_tty_lock, flags);
do {
/* Create a sclp output buffer if none exists yet */
if (sclp_ttybuf == NULL) {
while (list_empty(&sclp_tty_pages)) {
spin_unlock_irqrestore(&sclp_tty_lock, flags);
- if (in_interrupt())
- sclp_sync_wait();
+ if (may_fail)
+ goto out;
else
- wait_event(sclp_tty_waitq,
- !list_empty(&sclp_tty_pages));
+ sclp_sync_wait();
spin_lock_irqsave(&sclp_tty_lock, flags);
}
page = sclp_tty_pages.next;
list_del((struct list_head *) page);
- sclp_ttybuf = sclp_make_buffer(page,
- sclp_ioctls.columns,
- sclp_ioctls.htab);
+ sclp_ttybuf = sclp_make_buffer(page, sclp_tty_columns,
+ SPACES_PER_TAB);
}
/* try to write the string to the current output buffer */
written = sclp_write(sclp_ttybuf, str, count);
+ overall_written += written;
if (written == count)
break;
/*
@@ -363,27 +219,17 @@ sclp_tty_write_string(const unsigned char *str, int count)
count -= written;
} while (count > 0);
/* Setup timer to output current console buffer after 1/10 second */
- if (sclp_ioctls.final_nl) {
- if (sclp_ttybuf != NULL &&
- sclp_chars_in_buffer(sclp_ttybuf) != 0 &&
- !timer_pending(&sclp_tty_timer)) {
- init_timer(&sclp_tty_timer);
- sclp_tty_timer.function = sclp_tty_timeout;
- sclp_tty_timer.data = 0UL;
- sclp_tty_timer.expires = jiffies + HZ/10;
- add_timer(&sclp_tty_timer);
- }
- } else {
- if (sclp_ttybuf != NULL &&
- sclp_chars_in_buffer(sclp_ttybuf) != 0) {
- buf = sclp_ttybuf;
- sclp_ttybuf = NULL;
- spin_unlock_irqrestore(&sclp_tty_lock, flags);
- __sclp_ttybuf_emit(buf);
- spin_lock_irqsave(&sclp_tty_lock, flags);
- }
+ if (sclp_ttybuf && sclp_chars_in_buffer(sclp_ttybuf) &&
+ !timer_pending(&sclp_tty_timer)) {
+ init_timer(&sclp_tty_timer);
+ sclp_tty_timer.function = sclp_tty_timeout;
+ sclp_tty_timer.data = 0UL;
+ sclp_tty_timer.expires = jiffies + HZ/10;
+ add_timer(&sclp_tty_timer);
}
spin_unlock_irqrestore(&sclp_tty_lock, flags);
+out:
+ return overall_written;
}
/*
@@ -395,11 +241,10 @@ static int
sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
if (sclp_tty_chars_count > 0) {
- sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+ sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
}
- sclp_tty_write_string(buf, count);
- return count;
+ return sclp_tty_write_string(buf, count, 1);
}
/*
@@ -417,9 +262,10 @@ sclp_tty_put_char(struct tty_struct *tty, unsigned char ch)
{
sclp_tty_chars[sclp_tty_chars_count++] = ch;
if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) {
- sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+ sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
- } return 1;
+ }
+ return 1;
}
/*
@@ -430,7 +276,7 @@ static void
sclp_tty_flush_chars(struct tty_struct *tty)
{
if (sclp_tty_chars_count > 0) {
- sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+ sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
}
}
@@ -469,7 +315,7 @@ static void
sclp_tty_flush_buffer(struct tty_struct *tty)
{
if (sclp_tty_chars_count > 0) {
- sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+ sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
}
}
@@ -517,9 +363,7 @@ sclp_tty_input(unsigned char* buf, unsigned int count)
* modifiy original string,
* returns length of resulting string
*/
-static int
-sclp_switch_cases(unsigned char *buf, int count,
- unsigned char delim, int tolower)
+static int sclp_switch_cases(unsigned char *buf, int count)
{
unsigned char *ip, *op;
int toggle;
@@ -529,9 +373,9 @@ sclp_switch_cases(unsigned char *buf, int count,
ip = op = buf;
while (count-- > 0) {
/* compare with special character */
- if (*ip == delim) {
+ if (*ip == CASE_DELIMITER) {
/* followed by another special character? */
- if (count && ip[1] == delim) {
+ if (count && ip[1] == CASE_DELIMITER) {
/*
* ... then put a single copy of the special
* character to the output string
@@ -550,7 +394,7 @@ sclp_switch_cases(unsigned char *buf, int count,
/* not the special character */
if (toggle)
/* but case switching is on */
- if (tolower)
+ if (sclp_tty_tolower)
/* switch to uppercase */
*op++ = _ebc_toupper[(int) *ip++];
else
@@ -570,30 +414,12 @@ sclp_get_input(unsigned char *start, unsigned char *end)
int count;
count = end - start;
- /*
- * if set in ioctl convert EBCDIC to lower case
- * (modify original input in SCCB)
- */
- if (sclp_ioctls.tolower)
+ if (sclp_tty_tolower)
EBC_TOLOWER(start, count);
-
- /*
- * if set in ioctl find out characters in lower or upper case
- * (depends on current case) separated by a special character,
- * works on EBCDIC
- */
- if (sclp_ioctls.delim)
- count = sclp_switch_cases(start, count,
- sclp_ioctls.delim,
- sclp_ioctls.tolower);
-
+ count = sclp_switch_cases(start, count);
/* convert EBCDIC to ASCII (modify original input in SCCB) */
sclp_ebcasc_str(start, count);
- /* if set in ioctl write operators input to console */
- if (sclp_ioctls.echo)
- sclp_tty_write(sclp_tty, start, count);
-
/* transfer input to high level driver */
sclp_tty_input(start, count);
}
@@ -717,7 +543,6 @@ static const struct tty_operations sclp_ops = {
.write_room = sclp_tty_write_room,
.chars_in_buffer = sclp_tty_chars_in_buffer,
.flush_buffer = sclp_tty_flush_buffer,
- .ioctl = sclp_tty_ioctl,
};
static int __init
@@ -736,9 +561,6 @@ sclp_tty_init(void)
rc = sclp_rw_init();
if (rc) {
- printk(KERN_ERR SCLP_TTY_PRINT_HEADER
- "could not register tty - "
- "sclp_rw_init returned %d\n", rc);
put_tty_driver(driver);
return rc;
}
@@ -754,7 +576,6 @@ sclp_tty_init(void)
}
INIT_LIST_HEAD(&sclp_tty_outqueue);
spin_lock_init(&sclp_tty_lock);
- init_waitqueue_head(&sclp_tty_waitq);
init_timer(&sclp_tty_timer);
sclp_ttybuf = NULL;
sclp_tty_buffer_count = 0;
@@ -763,11 +584,10 @@ sclp_tty_init(void)
* save 4 characters for the CPU number
* written at start of each line by VM/CP
*/
- sclp_ioctls_init.columns = 76;
+ sclp_tty_columns = 76;
/* case input lines to lowercase */
- sclp_ioctls_init.tolower = 1;
+ sclp_tty_tolower = 1;
}
- sclp_ioctls = sclp_ioctls_init;
sclp_tty_chars_count = 0;
sclp_tty = NULL;
@@ -792,9 +612,6 @@ sclp_tty_init(void)
tty_set_operations(driver, &sclp_ops);
rc = tty_register_driver(driver);
if (rc) {
- printk(KERN_ERR SCLP_TTY_PRINT_HEADER
- "could not register tty - "
- "tty_register_driver returned %d\n", rc);
put_tty_driver(driver);
return rc;
}
diff --git a/drivers/s390/char/sclp_tty.h b/drivers/s390/char/sclp_tty.h
index 0ce2c1fc534..4b965b22fec 100644
--- a/drivers/s390/char/sclp_tty.h
+++ b/drivers/s390/char/sclp_tty.h
@@ -11,61 +11,8 @@
#ifndef __SCLP_TTY_H__
#define __SCLP_TTY_H__
-#include <linux/ioctl.h>
-#include <linux/termios.h>
#include <linux/tty_driver.h>
-/* This is the type of data structures storing sclp ioctl setting. */
-struct sclp_ioctls {
- unsigned short htab;
- unsigned char echo;
- unsigned short columns;
- unsigned char final_nl;
- unsigned short max_sccb;
- unsigned short kmem_sccb; /* can't be modified at run time */
- unsigned char tolower;
- unsigned char delim;
-};
-
-/* must be unique, FIXME: must be added in Documentation/ioctl_number.txt */
-#define SCLP_IOCTL_LETTER 'B'
-
-/* set width of horizontal tabulator */
-#define TIOCSCLPSHTAB _IOW(SCLP_IOCTL_LETTER, 0, unsigned short)
-/* enable/disable echo of input (independent from line discipline) */
-#define TIOCSCLPSECHO _IOW(SCLP_IOCTL_LETTER, 1, unsigned char)
-/* set number of colums for output */
-#define TIOCSCLPSCOLS _IOW(SCLP_IOCTL_LETTER, 2, unsigned short)
-/* enable/disable writing without final new line character */
-#define TIOCSCLPSNL _IOW(SCLP_IOCTL_LETTER, 4, signed char)
-/* set the maximum buffers size for output, rounded up to next 4kB boundary */
-#define TIOCSCLPSOBUF _IOW(SCLP_IOCTL_LETTER, 5, unsigned short)
-/* set initial (default) sclp ioctls */
-#define TIOCSCLPSINIT _IO(SCLP_IOCTL_LETTER, 6)
-/* enable/disable conversion from upper to lower case of input */
-#define TIOCSCLPSCASE _IOW(SCLP_IOCTL_LETTER, 7, unsigned char)
-/* set special character used for separating upper and lower case, */
-/* 0x00 disables this feature */
-#define TIOCSCLPSDELIM _IOW(SCLP_IOCTL_LETTER, 9, unsigned char)
-
-/* get width of horizontal tabulator */
-#define TIOCSCLPGHTAB _IOR(SCLP_IOCTL_LETTER, 10, unsigned short)
-/* Is echo of input enabled ? (independent from line discipline) */
-#define TIOCSCLPGECHO _IOR(SCLP_IOCTL_LETTER, 11, unsigned char)
-/* get number of colums for output */
-#define TIOCSCLPGCOLS _IOR(SCLP_IOCTL_LETTER, 12, unsigned short)
-/* Is writing without final new line character enabled ? */
-#define TIOCSCLPGNL _IOR(SCLP_IOCTL_LETTER, 14, signed char)
-/* get the maximum buffers size for output */
-#define TIOCSCLPGOBUF _IOR(SCLP_IOCTL_LETTER, 15, unsigned short)
-/* Is conversion from upper to lower case of input enabled ? */
-#define TIOCSCLPGCASE _IOR(SCLP_IOCTL_LETTER, 17, unsigned char)
-/* get special character used for separating upper and lower case, */
-/* 0x00 disables this feature */
-#define TIOCSCLPGDELIM _IOR(SCLP_IOCTL_LETTER, 19, unsigned char)
-/* get the number of buffers/pages got from kernel at startup */
-#define TIOCSCLPGKBUF _IOR(SCLP_IOCTL_LETTER, 20, unsigned short)
-
extern struct tty_driver *sclp_tty_driver;
#endif /* __SCLP_TTY_H__ */
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index 3e577f655b1..9854f19f5e6 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -24,10 +24,11 @@
#include <linux/bootmem.h>
#include <linux/interrupt.h>
#include <linux/init.h>
+#include <linux/reboot.h>
+
#include <asm/uaccess.h>
#include "sclp.h"
-#define SCLP_VT220_PRINT_HEADER "sclp vt220 tty driver: "
#define SCLP_VT220_MAJOR TTY_MAJOR
#define SCLP_VT220_MINOR 65
#define SCLP_VT220_DRIVER_NAME "sclp_vt220"
@@ -82,8 +83,8 @@ static struct sclp_vt220_request *sclp_vt220_current_request;
/* Number of characters in current request buffer */
static int sclp_vt220_buffered_chars;
-/* Flag indicating whether this driver has already been initialized */
-static int sclp_vt220_initialized = 0;
+/* Counter controlling core driver initialization. */
+static int __initdata sclp_vt220_init_count;
/* Flag indicating that sclp_vt220_current_request should really
* have been already queued but wasn't because the SCLP was processing
@@ -609,10 +610,8 @@ sclp_vt220_flush_buffer(struct tty_struct *tty)
sclp_vt220_emit_current();
}
-/*
- * Initialize all relevant components and register driver with system.
- */
-static void __init __sclp_vt220_cleanup(void)
+/* Release allocated pages. */
+static void __init __sclp_vt220_free_pages(void)
{
struct list_head *page, *p;
@@ -623,21 +622,30 @@ static void __init __sclp_vt220_cleanup(void)
else
free_bootmem((unsigned long) page, PAGE_SIZE);
}
- if (!list_empty(&sclp_vt220_register.list))
- sclp_unregister(&sclp_vt220_register);
- sclp_vt220_initialized = 0;
}
-static int __init __sclp_vt220_init(void)
+/* Release memory and unregister from sclp core. Controlled by init counting -
+ * only the last invoker will actually perform these actions. */
+static void __init __sclp_vt220_cleanup(void)
+{
+ sclp_vt220_init_count--;
+ if (sclp_vt220_init_count != 0)
+ return;
+ sclp_unregister(&sclp_vt220_register);
+ __sclp_vt220_free_pages();
+}
+
+/* Allocate buffer pages and register with sclp core. Controlled by init
+ * counting - only the first invoker will actually perform these actions. */
+static int __init __sclp_vt220_init(int num_pages)
{
void *page;
int i;
- int num_pages;
int rc;
- if (sclp_vt220_initialized)
+ sclp_vt220_init_count++;
+ if (sclp_vt220_init_count != 1)
return 0;
- sclp_vt220_initialized = 1;
spin_lock_init(&sclp_vt220_lock);
INIT_LIST_HEAD(&sclp_vt220_empty);
INIT_LIST_HEAD(&sclp_vt220_outqueue);
@@ -649,24 +657,22 @@ static int __init __sclp_vt220_init(void)
sclp_vt220_flush_later = 0;
/* Allocate pages for output buffering */
- num_pages = slab_is_available() ? MAX_KMEM_PAGES : MAX_CONSOLE_PAGES;
for (i = 0; i < num_pages; i++) {
if (slab_is_available())
page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
else
page = alloc_bootmem_low_pages(PAGE_SIZE);
if (!page) {
- __sclp_vt220_cleanup();
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto out;
}
list_add_tail((struct list_head *) page, &sclp_vt220_empty);
}
rc = sclp_register(&sclp_vt220_register);
+out:
if (rc) {
- printk(KERN_ERR SCLP_VT220_PRINT_HEADER
- "could not register vt220 - "
- "sclp_register returned %d\n", rc);
- __sclp_vt220_cleanup();
+ __sclp_vt220_free_pages();
+ sclp_vt220_init_count--;
}
return rc;
}
@@ -689,15 +695,13 @@ static int __init sclp_vt220_tty_init(void)
{
struct tty_driver *driver;
int rc;
- int cleanup;
/* Note: we're not testing for CONSOLE_IS_SCLP here to preserve
* symmetry between VM and LPAR systems regarding ttyS1. */
driver = alloc_tty_driver(1);
if (!driver)
return -ENOMEM;
- cleanup = !sclp_vt220_initialized;
- rc = __sclp_vt220_init();
+ rc = __sclp_vt220_init(MAX_KMEM_PAGES);
if (rc)
goto out_driver;
@@ -713,18 +717,13 @@ static int __init sclp_vt220_tty_init(void)
tty_set_operations(driver, &sclp_vt220_ops);
rc = tty_register_driver(driver);
- if (rc) {
- printk(KERN_ERR SCLP_VT220_PRINT_HEADER
- "could not register tty - "
- "tty_register_driver returned %d\n", rc);
+ if (rc)
goto out_init;
- }
sclp_vt220_driver = driver;
return 0;
out_init:
- if (cleanup)
- __sclp_vt220_cleanup();
+ __sclp_vt220_cleanup();
out_driver:
put_tty_driver(driver);
return rc;
@@ -746,24 +745,30 @@ sclp_vt220_con_device(struct console *c, int *index)
return sclp_vt220_driver;
}
-/*
- * This routine is called from panic when the kernel is going to give up.
- * We have to make sure that all buffers will be flushed to the SCLP.
- * Note that this function may be called from within an interrupt context.
- */
-static void
-sclp_vt220_con_unblank(void)
+static int
+sclp_vt220_notify(struct notifier_block *self,
+ unsigned long event, void *data)
{
__sclp_vt220_flush_buffer();
+ return NOTIFY_OK;
}
+static struct notifier_block on_panic_nb = {
+ .notifier_call = sclp_vt220_notify,
+ .priority = 1,
+};
+
+static struct notifier_block on_reboot_nb = {
+ .notifier_call = sclp_vt220_notify,
+ .priority = 1,
+};
+
/* Structure needed to register with printk */
static struct console sclp_vt220_console =
{
.name = SCLP_VT220_CONSOLE_NAME,
.write = sclp_vt220_con_write,
.device = sclp_vt220_con_device,
- .unblank = sclp_vt220_con_unblank,
.flags = CON_PRINTBUFFER,
.index = SCLP_VT220_CONSOLE_INDEX
};
@@ -773,13 +778,14 @@ sclp_vt220_con_init(void)
{
int rc;
- INIT_LIST_HEAD(&sclp_vt220_register.list);
if (!CONSOLE_IS_SCLP)
return 0;
- rc = __sclp_vt220_init();
+ rc = __sclp_vt220_init(MAX_CONSOLE_PAGES);
if (rc)
return rc;
/* Attach linux console */
+ atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
+ register_reboot_notifier(&on_reboot_nb);
register_console(&sclp_vt220_console);
return 0;
}
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c
index 874adf365e4..22ca34361ed 100644
--- a/drivers/s390/char/tape_34xx.c
+++ b/drivers/s390/char/tape_34xx.c
@@ -196,7 +196,7 @@ tape_34xx_erp_retry(struct tape_request *request)
static int
tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb)
{
- if (irb->scsw.dstat == 0x85 /* READY */) {
+ if (irb->scsw.cmd.dstat == 0x85) { /* READY */
/* A medium was inserted in the drive. */
DBF_EVENT(6, "xuud med\n");
tape_34xx_delete_sbid_from(device, 0);
@@ -844,22 +844,22 @@ tape_34xx_irq(struct tape_device *device, struct tape_request *request,
if (request == NULL)
return tape_34xx_unsolicited_irq(device, irb);
- if ((irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) &&
- (irb->scsw.dstat & DEV_STAT_DEV_END) &&
+ if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
+ (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
(request->op == TO_WRI)) {
/* Write at end of volume */
PRINT_INFO("End of volume\n"); /* XXX */
return tape_34xx_erp_failed(request, -ENOSPC);
}
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
return tape_34xx_unit_check(device, request, irb);
- if (irb->scsw.dstat & DEV_STAT_DEV_END) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
/*
* A unit exception occurs on skipping over a tapemark block.
*/
- if (irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) {
if (request->op == TO_BSB || request->op == TO_FSB)
request->rescnt++;
else
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index 42ce7915fc5..4005c44a404 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -837,13 +837,13 @@ tape_3590_erp_retry(struct tape_device *device, struct tape_request *request,
static int
tape_3590_unsolicited_irq(struct tape_device *device, struct irb *irb)
{
- if (irb->scsw.dstat == DEV_STAT_CHN_END)
+ if (irb->scsw.cmd.dstat == DEV_STAT_CHN_END)
/* Probably result of halt ssch */
return TAPE_IO_PENDING;
- else if (irb->scsw.dstat == 0x85)
+ else if (irb->scsw.cmd.dstat == 0x85)
/* Device Ready */
DBF_EVENT(3, "unsol.irq! tape ready: %08x\n", device->cdev_id);
- else if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ else if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
tape_3590_schedule_work(device, TO_READ_ATTMSG);
} else {
DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);
@@ -910,7 +910,7 @@ tape_3590_erp_swap(struct tape_device *device, struct tape_request *request,
* should proceed with the new tape... this
* should probably be done in user space!
*/
- PRINT_WARN("(%s): Swap Tape Device!\n", device->cdev->dev.bus_id);
+ PRINT_WARN("(%s): Swap Tape Device!\n", dev_name(&device->cdev->dev));
return tape_3590_erp_basic(device, request, irb, -EIO);
}
@@ -1003,40 +1003,43 @@ tape_3590_print_mim_msg_f0(struct tape_device *device, struct irb *irb)
/* Exception Message */
switch (sense->fmt.f70.emc) {
case 0x02:
- PRINT_WARN("(%s): Data degraded\n", device->cdev->dev.bus_id);
+ PRINT_WARN("(%s): Data degraded\n",
+ dev_name(&device->cdev->dev));
break;
case 0x03:
PRINT_WARN("(%s): Data degraded in partion %i\n",
- device->cdev->dev.bus_id, sense->fmt.f70.mp);
+ dev_name(&device->cdev->dev), sense->fmt.f70.mp);
break;
case 0x04:
- PRINT_WARN("(%s): Medium degraded\n", device->cdev->dev.bus_id);
+ PRINT_WARN("(%s): Medium degraded\n",
+ dev_name(&device->cdev->dev));
break;
case 0x05:
PRINT_WARN("(%s): Medium degraded in partition %i\n",
- device->cdev->dev.bus_id, sense->fmt.f70.mp);
+ dev_name(&device->cdev->dev), sense->fmt.f70.mp);
break;
case 0x06:
- PRINT_WARN("(%s): Block 0 Error\n", device->cdev->dev.bus_id);
+ PRINT_WARN("(%s): Block 0 Error\n",
+ dev_name(&device->cdev->dev));
break;
case 0x07:
PRINT_WARN("(%s): Medium Exception 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f70.md);
+ dev_name(&device->cdev->dev), sense->fmt.f70.md);
break;
default:
PRINT_WARN("(%s): MIM ExMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f70.emc);
+ dev_name(&device->cdev->dev), sense->fmt.f70.emc);
break;
}
/* Service Message */
switch (sense->fmt.f70.smc) {
case 0x02:
PRINT_WARN("(%s): Reference Media maintenance procedure %i\n",
- device->cdev->dev.bus_id, sense->fmt.f70.md);
+ dev_name(&device->cdev->dev), sense->fmt.f70.md);
break;
default:
PRINT_WARN("(%s): MIM ServiceMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f70.smc);
+ dev_name(&device->cdev->dev), sense->fmt.f70.smc);
break;
}
}
@@ -1054,101 +1057,101 @@ tape_3590_print_io_sim_msg_f1(struct tape_device *device, struct irb *irb)
switch (sense->fmt.f71.emc) {
case 0x01:
PRINT_WARN("(%s): Effect of failure is unknown\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x02:
PRINT_WARN("(%s): CU Exception - no performance impact\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x03:
PRINT_WARN("(%s): CU Exception on channel interface 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x04:
PRINT_WARN("(%s): CU Exception on device path 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x05:
PRINT_WARN("(%s): CU Exception on library path 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x06:
PRINT_WARN("(%s): CU Exception on node 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x07:
PRINT_WARN("(%s): CU Exception on partition 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
default:
PRINT_WARN("(%s): SIM ExMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.emc);
+ dev_name(&device->cdev->dev), sense->fmt.f71.emc);
}
/* Service Message */
switch (sense->fmt.f71.smc) {
case 0x01:
PRINT_WARN("(%s): Repair impact is unknown\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x02:
PRINT_WARN("(%s): Repair will not impact cu performance\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x03:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable node "
"0x%x on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable nodes "
"(0x%x-0x%x) on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x04:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable cannel path "
"0x%x on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable cannel paths "
"(0x%x-0x%x) on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x05:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable device path "
"0x%x on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable device paths "
"(0x%x-0x%x) on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x06:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable library path "
"0x%x on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable library paths "
"(0x%x-0x%x) on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x07:
PRINT_WARN("(%s): Repair will disable access to CU\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
default:
PRINT_WARN("(%s): SIM ServiceMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.smc);
+ dev_name(&device->cdev->dev), sense->fmt.f71.smc);
}
}
@@ -1165,104 +1168,104 @@ tape_3590_print_dev_sim_msg_f2(struct tape_device *device, struct irb *irb)
switch (sense->fmt.f71.emc) {
case 0x01:
PRINT_WARN("(%s): Effect of failure is unknown\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x02:
PRINT_WARN("(%s): DV Exception - no performance impact\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x03:
PRINT_WARN("(%s): DV Exception on channel interface 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x04:
PRINT_WARN("(%s): DV Exception on loader 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x05:
PRINT_WARN("(%s): DV Exception on message display 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x06:
PRINT_WARN("(%s): DV Exception in tape path\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x07:
PRINT_WARN("(%s): DV Exception in drive\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
default:
PRINT_WARN("(%s): DSIM ExMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.emc);
+ dev_name(&device->cdev->dev), sense->fmt.f71.emc);
}
/* Service Message */
switch (sense->fmt.f71.smc) {
case 0x01:
PRINT_WARN("(%s): Repair impact is unknown\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x02:
PRINT_WARN("(%s): Repair will not impact device performance\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x03:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable channel path "
"0x%x on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable channel path "
"(0x%x-0x%x) on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x04:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable interface 0x%x "
"on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable interfaces "
"(0x%x-0x%x) on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x05:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable loader 0x%x "
"on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable loader "
"(0x%x-0x%x) on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x07:
PRINT_WARN("(%s): Repair will disable access to DV\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x08:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable message "
"display 0x%x on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable message "
"displays (0x%x-0x%x) on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x09:
- PRINT_WARN("(%s): Clean DV\n", device->cdev->dev.bus_id);
+ PRINT_WARN("(%s): Clean DV\n", dev_name(&device->cdev->dev));
break;
default:
PRINT_WARN("(%s): DSIM ServiceMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.smc);
+ dev_name(&device->cdev->dev), sense->fmt.f71.smc);
}
}
@@ -1279,18 +1282,18 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
return;
if ((sense->mc > 0) && (sense->mc < TAPE_3590_MAX_MSG)) {
if (tape_3590_msg[sense->mc] != NULL)
- PRINT_WARN("(%s): %s\n", device->cdev->dev.bus_id,
+ PRINT_WARN("(%s): %s\n", dev_name(&device->cdev->dev),
tape_3590_msg[sense->mc]);
else {
PRINT_WARN("(%s): Message Code 0x%x\n",
- device->cdev->dev.bus_id, sense->mc);
+ dev_name(&device->cdev->dev), sense->mc);
}
return;
}
if (sense->mc == 0xf0) {
/* Standard Media Information Message */
PRINT_WARN("(%s): MIM SEV=%i, MC=%02x, ES=%x/%x, "
- "RC=%02x-%04x-%02x\n", device->cdev->dev.bus_id,
+ "RC=%02x-%04x-%02x\n", dev_name(&device->cdev->dev),
sense->fmt.f70.sev, sense->mc,
sense->fmt.f70.emc, sense->fmt.f70.smc,
sense->fmt.f70.refcode, sense->fmt.f70.mid,
@@ -1302,7 +1305,7 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
/* Standard I/O Subsystem Service Information Message */
PRINT_WARN("(%s): IOSIM SEV=%i, DEVTYPE=3590/%02x, "
"MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.sev,
+ dev_name(&device->cdev->dev), sense->fmt.f71.sev,
device->cdev->id.dev_model,
sense->mc, sense->fmt.f71.emc,
sense->fmt.f71.smc, sense->fmt.f71.refcode1,
@@ -1314,7 +1317,7 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
/* Standard Device Service Information Message */
PRINT_WARN("(%s): DEVSIM SEV=%i, DEVTYPE=3590/%02x, "
"MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.sev,
+ dev_name(&device->cdev->dev), sense->fmt.f71.sev,
device->cdev->id.dev_model,
sense->mc, sense->fmt.f71.emc,
sense->fmt.f71.smc, sense->fmt.f71.refcode1,
@@ -1327,7 +1330,7 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
return;
}
PRINT_WARN("(%s): Device Message(%x)\n",
- device->cdev->dev.bus_id, sense->mc);
+ dev_name(&device->cdev->dev), sense->mc);
}
static int tape_3590_crypt_error(struct tape_device *device,
@@ -1336,10 +1339,11 @@ static int tape_3590_crypt_error(struct tape_device *device,
u8 cu_rc, ekm_rc1;
u16 ekm_rc2;
u32 drv_rc;
- char *bus_id, *sense;
+ const char *bus_id;
+ char *sense;
sense = ((struct tape_3590_sense *) irb->ecw)->fmt.data;
- bus_id = device->cdev->dev.bus_id;
+ bus_id = dev_name(&device->cdev->dev);
cu_rc = sense[0];
drv_rc = *((u32*) &sense[5]) & 0xffffff;
ekm_rc1 = sense[9];
@@ -1440,7 +1444,7 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
* "device intervention" is not very meaningfull
*/
PRINT_WARN("(%s): Tape operation when medium not loaded\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
tape_med_state_set(device, MS_UNLOADED);
tape_3590_schedule_work(device, TO_CRYPT_OFF);
return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM);
@@ -1487,18 +1491,18 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
case 0x6020:
PRINT_WARN("(%s): Cartridge of wrong type ?\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE);
case 0x8011:
PRINT_WARN("(%s): Another host has reserved the tape device\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
return tape_3590_erp_basic(device, request, irb, -EPERM);
case 0x8013:
PRINT_WARN("(%s): Another host has privileged access to the "
- "tape device\n", device->cdev->dev.bus_id);
+ "tape device\n", dev_name(&device->cdev->dev));
PRINT_WARN("(%s): To solve the problem unload the current "
- "cartridge!\n", device->cdev->dev.bus_id);
+ "cartridge!\n", dev_name(&device->cdev->dev));
return tape_3590_erp_basic(device, request, irb, -EPERM);
default:
return tape_3590_erp_basic(device, request, irb, -EIO);
@@ -1515,18 +1519,19 @@ tape_3590_irq(struct tape_device *device, struct tape_request *request,
if (request == NULL)
return tape_3590_unsolicited_irq(device, irb);
- if ((irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) &&
- (irb->scsw.dstat & DEV_STAT_DEV_END) && (request->op == TO_WRI)) {
+ if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
+ (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
+ (request->op == TO_WRI)) {
/* Write at end of volume */
DBF_EVENT(2, "End of volume\n");
return tape_3590_erp_failed(device, request, irb, -ENOSPC);
}
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
return tape_3590_unit_check(device, request, irb);
- if (irb->scsw.dstat & DEV_STAT_DEV_END) {
- if (irb->scsw.dstat == DEV_STAT_UNIT_EXCEP) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
+ if (irb->scsw.cmd.dstat == DEV_STAT_UNIT_EXCEP) {
if (request->op == TO_FSB || request->op == TO_BSB)
request->rescnt++;
else
@@ -1536,12 +1541,12 @@ tape_3590_irq(struct tape_device *device, struct tape_request *request,
return tape_3590_done(device, request);
}
- if (irb->scsw.dstat & DEV_STAT_CHN_END) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) {
DBF_EVENT(2, "cannel end\n");
return TAPE_IO_PENDING;
}
- if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
DBF_EVENT(2, "Unit Attention when busy..\n");
return TAPE_IO_PENDING;
}
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
index 95da72bc17e..a25b8bf54f4 100644
--- a/drivers/s390/char/tape_block.c
+++ b/drivers/s390/char/tape_block.c
@@ -278,7 +278,7 @@ tapeblock_cleanup_device(struct tape_device *device)
if (!device->blk_data.disk) {
PRINT_ERR("(%s): No gendisk to clean up!\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
goto cleanup_queue;
}
diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c
index ebe84067bae..be0ce2215c8 100644
--- a/drivers/s390/char/tape_char.c
+++ b/drivers/s390/char/tape_char.c
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/mtio.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
@@ -108,7 +109,7 @@ tapechar_check_idalbuffer(struct tape_device *device, size_t block_size)
/* The current idal buffer is not correct. Allocate a new one. */
new = idal_buffer_alloc(block_size, 0);
- if (new == NULL)
+ if (IS_ERR(new))
return -ENOMEM;
if (device->char_data.idal_buf != NULL)
@@ -289,21 +290,26 @@ tapechar_open (struct inode *inode, struct file *filp)
if (imajor(filp->f_path.dentry->d_inode) != tapechar_major)
return -ENODEV;
+ lock_kernel();
minor = iminor(filp->f_path.dentry->d_inode);
device = tape_get_device(minor / TAPE_MINORS_PER_DEV);
if (IS_ERR(device)) {
DBF_EVENT(3, "TCHAR:open: tape_get_device() failed\n");
- return PTR_ERR(device);
+ rc = PTR_ERR(device);
+ goto out;
}
rc = tape_open(device);
if (rc == 0) {
filp->private_data = device;
- return nonseekable_open(inode, filp);
+ rc = nonseekable_open(inode, filp);
}
- tape_put_device(device);
+ else
+ tape_put_device(device);
+out:
+ unlock_kernel();
return rc;
}
diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c
index 6dfdb7c1798..12c2a5aaf31 100644
--- a/drivers/s390/char/tape_class.c
+++ b/drivers/s390/char/tape_class.c
@@ -69,10 +69,9 @@ struct tape_class_device *register_tape_dev(
if (rc)
goto fail_with_cdev;
- tcd->class_device = device_create(tape_class, device,
- tcd->char_device->dev,
- "%s", tcd->device_name
- );
+ tcd->class_device = device_create_drvdata(tape_class, device,
+ tcd->char_device->dev,
+ NULL, "%s", tcd->device_name);
rc = IS_ERR(tcd->class_device) ? PTR_ERR(tcd->class_device) : 0;
if (rc)
goto fail_with_cdev;
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index c20e3c54834..d7073dbf825 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -215,12 +215,12 @@ tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate)
case MS_UNLOADED:
device->tape_generic_status |= GMT_DR_OPEN(~0);
PRINT_INFO("(%s): Tape is unloaded\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case MS_LOADED:
device->tape_generic_status &= ~GMT_DR_OPEN(~0);
PRINT_INFO("(%s): Tape has been mounted\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
default:
// print nothing
@@ -415,7 +415,7 @@ tape_generic_offline(struct tape_device *device)
device->cdev_id);
PRINT_WARN("(%s): Set offline failed "
"- drive in use.\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
return -EBUSY;
}
@@ -538,7 +538,8 @@ tape_generic_probe(struct ccw_device *cdev)
ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group);
if (ret) {
tape_put_device(device);
- PRINT_ERR("probe failed for tape device %s\n", cdev->dev.bus_id);
+ PRINT_ERR("probe failed for tape device %s\n",
+ dev_name(&cdev->dev));
return ret;
}
cdev->dev.driver_data = device;
@@ -546,7 +547,7 @@ tape_generic_probe(struct ccw_device *cdev)
device->cdev = cdev;
ccw_device_get_id(cdev, &dev_id);
device->cdev_id = devid_to_int(&dev_id);
- PRINT_INFO("tape device %s found\n", cdev->dev.bus_id);
+ PRINT_INFO("tape device %s found\n", dev_name(&cdev->dev));
return ret;
}
@@ -616,7 +617,7 @@ tape_generic_remove(struct ccw_device *cdev)
device->cdev_id);
PRINT_WARN("(%s): Drive in use vanished - "
"expect trouble!\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
PRINT_WARN("State was %i\n", device->tape_state);
tape_state_set(device, TS_NOT_OPER);
__tape_discard_requests(device);
@@ -839,8 +840,8 @@ tape_dump_sense(struct tape_device* device, struct tape_request *request,
PRINT_INFO("-------------------------------------------------\n");
PRINT_INFO("DSTAT : %02x CSTAT: %02x CPA: %04x\n",
- irb->scsw.dstat, irb->scsw.cstat, irb->scsw.cpa);
- PRINT_INFO("DEVICE: %s\n", device->cdev->dev.bus_id);
+ irb->scsw.cmd.dstat, irb->scsw.cmd.cstat, irb->scsw.cmd.cpa);
+ PRINT_INFO("DEVICE: %s\n", dev_name(&device->cdev->dev));
if (request != NULL)
PRINT_INFO("OP : %s\n", tape_op_verbose[request->op]);
@@ -867,7 +868,7 @@ tape_dump_sense_dbf(struct tape_device *device, struct tape_request *request,
else
op = "---";
DBF_EVENT(3, "DSTAT : %02x CSTAT: %02x\n",
- irb->scsw.dstat,irb->scsw.cstat);
+ irb->scsw.cmd.dstat, irb->scsw.cmd.cstat);
DBF_EVENT(3, "DEVICE: %08x OP\t: %s\n", device->cdev_id, op);
sptr = (unsigned int *) irb->ecw;
DBF_EVENT(3, "%08x %08x\n", sptr[0], sptr[1]);
@@ -1051,7 +1052,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
device = (struct tape_device *) cdev->dev.driver_data;
if (device == NULL) {
PRINT_ERR("could not get device structure for %s "
- "in interrupt\n", cdev->dev.bus_id);
+ "in interrupt\n", dev_name(&cdev->dev));
return;
}
request = (struct tape_request *) intparm;
@@ -1064,13 +1065,13 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
switch (PTR_ERR(irb)) {
case -ETIMEDOUT:
PRINT_WARN("(%s): Request timed out\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
case -EIO:
__tape_end_request(device, request, -EIO);
break;
default:
PRINT_ERR("(%s): Unexpected i/o error %li\n",
- cdev->dev.bus_id,
+ dev_name(&cdev->dev),
PTR_ERR(irb));
}
return;
@@ -1083,10 +1084,11 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
* error might still apply. So we just schedule the request to be
* started later.
*/
- if (irb->scsw.cc != 0 && (irb->scsw.fctl & SCSW_FCTL_START_FUNC) &&
+ if (irb->scsw.cmd.cc != 0 &&
+ (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) &&
(request->status == TAPE_REQUEST_IN_IO)) {
DBF_EVENT(3,"(%08x): deferred cc=%i, fctl=%i. restarting\n",
- device->cdev_id, irb->scsw.cc, irb->scsw.fctl);
+ device->cdev_id, irb->scsw.cmd.cc, irb->scsw.cmd.fctl);
request->status = TAPE_REQUEST_QUEUED;
schedule_delayed_work(&device->tape_dnr, HZ);
return;
@@ -1094,8 +1096,8 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
/* May be an unsolicited irq */
if(request != NULL)
- request->rescnt = irb->scsw.count;
- else if ((irb->scsw.dstat == 0x85 || irb->scsw.dstat == 0x80) &&
+ request->rescnt = irb->scsw.cmd.count;
+ else if ((irb->scsw.cmd.dstat == 0x85 || irb->scsw.cmd.dstat == 0x80) &&
!list_empty(&device->req_queue)) {
/* Not Ready to Ready after long busy ? */
struct tape_request *req;
@@ -1111,7 +1113,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
return;
}
}
- if (irb->scsw.dstat != 0x0c) {
+ if (irb->scsw.cmd.dstat != 0x0c) {
/* Set the 'ONLINE' flag depending on sense byte 1 */
if(*(((__u8 *) irb->ecw) + 1) & SENSE_DRIVE_ONLINE)
device->tape_generic_status |= GMT_ONLINE(~0);
diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c
index e7c888c14e7..8a376af926a 100644
--- a/drivers/s390/char/tape_proc.c
+++ b/drivers/s390/char/tape_proc.c
@@ -52,7 +52,7 @@ static int tape_proc_show(struct seq_file *m, void *v)
return 0;
spin_lock_irq(get_ccwdev_lock(device->cdev));
seq_printf(m, "%d\t", (int) n);
- seq_printf(m, "%-10.10s ", device->cdev->dev.bus_id);
+ seq_printf(m, "%-10.10s ", dev_name(&device->cdev->dev));
seq_printf(m, "%04X/", device->cdev->id.cu_type);
seq_printf(m, "%02X\t", device->cdev->id.cu_model);
seq_printf(m, "%04X/", device->cdev->id.dev_type);
diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c
index 2a1af4e60be..5bd573d144d 100644
--- a/drivers/s390/char/tape_std.c
+++ b/drivers/s390/char/tape_std.c
@@ -47,7 +47,7 @@ tape_std_assign_timeout(unsigned long data)
rc = tape_cancel_io(device, request);
if(rc)
PRINT_ERR("(%s): Assign timeout: Cancel failed with rc = %i\n",
- device->cdev->dev.bus_id, rc);
+ dev_name(&device->cdev->dev), rc);
}
@@ -83,7 +83,7 @@ tape_std_assign(struct tape_device *device)
if (rc != 0) {
PRINT_WARN("%s: assign failed - device might be busy\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
DBF_EVENT(3, "%08x: assign failed - device might be busy\n",
device->cdev_id);
} else {
@@ -106,7 +106,7 @@ tape_std_unassign (struct tape_device *device)
DBF_EVENT(3, "(%08x): Can't unassign device\n",
device->cdev_id);
PRINT_WARN("(%s): Can't unassign device - device gone\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
return -EIO;
}
@@ -120,7 +120,8 @@ tape_std_unassign (struct tape_device *device)
if ((rc = tape_do_io(device, request)) != 0) {
DBF_EVENT(3, "%08x: Unassign failed\n", device->cdev_id);
- PRINT_WARN("%s: Unassign failed\n", device->cdev->dev.bus_id);
+ PRINT_WARN("%s: Unassign failed\n",
+ dev_name(&device->cdev->dev));
} else {
DBF_EVENT(3, "%08x: Tape unassigned\n", device->cdev_id);
}
@@ -248,7 +249,7 @@ tape_std_mtsetblk(struct tape_device *device, int count)
/* Allocate a new idal buffer. */
new = idal_buffer_alloc(count, 0);
- if (new == NULL)
+ if (IS_ERR(new))
return -ENOMEM;
if (device->char_data.idal_buf != NULL)
idal_buffer_free(device->char_data.idal_buf);
@@ -634,10 +635,10 @@ tape_std_mtcompression(struct tape_device *device, int mt_count)
DBF_EXCEPTION(6, "xcom parm\n");
if (*device->modeset_byte & 0x08)
PRINT_INFO("(%s) Compression is currently on\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
else
PRINT_INFO("(%s) Compression is currently off\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
PRINT_INFO("Use 1 to switch compression on, 0 to "
"switch it off\n");
return -EINVAL;
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c
index 5043150019a..a7fe6302c98 100644
--- a/drivers/s390/char/tty3270.c
+++ b/drivers/s390/char/tty3270.c
@@ -663,7 +663,7 @@ static int
tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
{
/* Handle ATTN. Schedule tasklet to read aid. */
- if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
if (!tp->throttle)
tty3270_issue_read(tp, 0);
else
@@ -671,11 +671,11 @@ tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
}
if (rq) {
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
rq->rc = -EIO;
else
/* Normal end. Copy residual count. */
- rq->rescnt = irb->scsw.count;
+ rq->rescnt = irb->scsw.cmd.count;
}
return RAW3270_IO_DONE;
}
@@ -1792,15 +1792,12 @@ static int __init tty3270_init(void)
tty_set_operations(driver, &tty3270_ops);
ret = tty_register_driver(driver);
if (ret) {
- printk(KERN_ERR "tty3270 registration failed with %d\n", ret);
put_tty_driver(driver);
return ret;
}
tty3270_driver = driver;
ret = raw3270_register_notifier(tty3270_notifier);
if (ret) {
- printk(KERN_ERR "tty3270 notifier registration failed "
- "with %d\n", ret);
put_tty_driver(driver);
return ret;
diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c
index 2f419b0ea62..09e7d9bf438 100644
--- a/drivers/s390/char/vmcp.c
+++ b/drivers/s390/char/vmcp.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
+#include <linux/smp_lock.h>
#include <asm/cpcmd.h>
#include <asm/debug.h>
#include <asm/uaccess.h>
@@ -39,11 +40,14 @@ static int vmcp_open(struct inode *inode, struct file *file)
session = kmalloc(sizeof(*session), GFP_KERNEL);
if (!session)
return -ENOMEM;
+
+ lock_kernel();
session->bufsize = PAGE_SIZE;
session->response = NULL;
session->resp_size = 0;
mutex_init(&session->mutex);
file->private_data = session;
+ unlock_kernel();
return nonseekable_open(inode, file);
}
@@ -61,30 +65,24 @@ static int vmcp_release(struct inode *inode, struct file *file)
static ssize_t
vmcp_read(struct file *file, char __user *buff, size_t count, loff_t *ppos)
{
- size_t tocopy;
+ ssize_t ret;
+ size_t size;
struct vmcp_session *session;
- session = (struct vmcp_session *)file->private_data;
+ session = file->private_data;
if (mutex_lock_interruptible(&session->mutex))
return -ERESTARTSYS;
if (!session->response) {
mutex_unlock(&session->mutex);
return 0;
}
- if (*ppos > session->resp_size) {
- mutex_unlock(&session->mutex);
- return 0;
- }
- tocopy = min(session->resp_size - (size_t) (*ppos), count);
- tocopy = min(tocopy, session->bufsize - (size_t) (*ppos));
+ size = min_t(size_t, session->resp_size, session->bufsize);
+ ret = simple_read_from_buffer(buff, count, ppos,
+ session->response, size);
- if (copy_to_user(buff, session->response + (*ppos), tocopy)) {
- mutex_unlock(&session->mutex);
- return -EFAULT;
- }
mutex_unlock(&session->mutex);
- *ppos += tocopy;
- return tocopy;
+
+ return ret;
}
static ssize_t
@@ -198,27 +196,23 @@ static int __init vmcp_init(void)
PRINT_WARN("z/VM CP interface is only available under z/VM\n");
return -ENODEV;
}
+
vmcp_debug = debug_register("vmcp", 1, 1, 240);
- if (!vmcp_debug) {
- PRINT_ERR("z/VM CP interface not loaded. Could not register "
- "debug feature\n");
+ if (!vmcp_debug)
return -ENOMEM;
- }
+
ret = debug_register_view(vmcp_debug, &debug_hex_ascii_view);
if (ret) {
- PRINT_ERR("z/VM CP interface not loaded. Could not register "
- "debug feature view. Error code: %d\n", ret);
debug_unregister(vmcp_debug);
return ret;
}
+
ret = misc_register(&vmcp_dev);
if (ret) {
- PRINT_ERR("z/VM CP interface not loaded. Could not register "
- "misc device. Error code: %d\n", ret);
debug_unregister(vmcp_debug);
return ret;
}
- PRINT_INFO("z/VM CP interface loaded\n");
+
return 0;
}
@@ -226,7 +220,6 @@ static void __exit vmcp_exit(void)
{
misc_deregister(&vmcp_dev);
debug_unregister(vmcp_debug);
- PRINT_INFO("z/VM CP interface unloaded.\n");
}
module_init(vmcp_init);
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 2c2428cc05d..42173cc3461 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -25,6 +25,7 @@
#include <linux/kmod.h>
#include <linux/cdev.h>
#include <linux/device.h>
+#include <linux/smp_lock.h>
#include <linux/string.h>
@@ -216,9 +217,7 @@ static int vmlogrdr_get_recording_class_AB(void)
char *tail;
int len,i;
- printk (KERN_DEBUG "vmlogrdr: query command: %s\n", cp_command);
cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
- printk (KERN_DEBUG "vmlogrdr: response: %s", cp_response);
len = strnlen(cp_response,sizeof(cp_response));
// now the parsing
tail=strnchr(cp_response,len,'=');
@@ -268,11 +267,7 @@ static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr,
logptr->recording_name,
qid_string);
- printk (KERN_DEBUG "vmlogrdr: recording command: %s\n",
- cp_command);
cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
- printk (KERN_DEBUG "vmlogrdr: recording response: %s",
- cp_response);
}
memset(cp_command, 0x00, sizeof(cp_command));
@@ -282,10 +277,7 @@ static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr,
onoff,
qid_string);
- printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command);
cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
- printk (KERN_DEBUG "vmlogrdr: recording response: %s",
- cp_response);
/* 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
@@ -319,9 +311,11 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
return -ENOSYS;
/* Besure this device hasn't already been opened */
+ lock_kernel();
spin_lock_bh(&logptr->priv_lock);
if (logptr->dev_in_use) {
spin_unlock_bh(&logptr->priv_lock);
+ unlock_kernel();
return -EBUSY;
}
logptr->dev_in_use = 1;
@@ -365,7 +359,9 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
|| (logptr->iucv_path_severed));
if (logptr->iucv_path_severed)
goto out_record;
- return nonseekable_open(inode, filp);
+ ret = nonseekable_open(inode, filp);
+ unlock_kernel();
+ return ret;
out_record:
if (logptr->autorecording)
@@ -375,6 +371,7 @@ out_path:
logptr->path = NULL;
out_dev:
logptr->dev_in_use = 0;
+ unlock_kernel();
return -EIO;
}
@@ -567,10 +564,7 @@ static ssize_t vmlogrdr_purge_store(struct device * dev,
"RECORDING %s PURGE ",
priv->recording_name);
- printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command);
cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
- printk (KERN_DEBUG "vmlogrdr: recording response: %s",
- cp_response);
return count;
}
@@ -682,28 +676,20 @@ static int vmlogrdr_register_driver(void)
/* Register with iucv driver */
ret = iucv_register(&vmlogrdr_iucv_handler, 1);
- if (ret) {
- printk (KERN_ERR "vmlogrdr: failed to register with "
- "iucv driver\n");
+ if (ret)
goto out;
- }
ret = driver_register(&vmlogrdr_driver);
- if (ret) {
- printk(KERN_ERR "vmlogrdr: failed to register driver.\n");
+ if (ret)
goto out_iucv;
- }
ret = driver_create_file(&vmlogrdr_driver,
&driver_attr_recording_status);
- if (ret) {
- printk(KERN_ERR "vmlogrdr: failed to add driver attribute.\n");
+ if (ret)
goto out_driver;
- }
vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr");
if (IS_ERR(vmlogrdr_class)) {
- printk(KERN_ERR "vmlogrdr: failed to create class.\n");
ret = PTR_ERR(vmlogrdr_class);
vmlogrdr_class = NULL;
goto out_attr;
@@ -738,8 +724,7 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)
dev = kzalloc(sizeof(struct device), GFP_KERNEL);
if (dev) {
- snprintf(dev->bus_id, BUS_ID_SIZE, "%s",
- priv->internal_name);
+ dev_set_name(dev, priv->internal_name);
dev->bus = &iucv_bus;
dev->parent = iucv_root;
dev->driver = &vmlogrdr_driver;
@@ -765,7 +750,7 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)
priv->class_device = device_create_drvdata(vmlogrdr_class, dev,
MKDEV(vmlogrdr_major,
priv->minor_num),
- priv, "%s", dev->bus_id);
+ priv, "%s", dev_name(dev));
if (IS_ERR(priv->class_device)) {
ret = PTR_ERR(priv->class_device);
priv->class_device=NULL;
@@ -871,12 +856,10 @@ static int __init vmlogrdr_init(void)
rc = vmlogrdr_register_cdev(dev);
if (rc)
goto cleanup;
- printk (KERN_INFO "vmlogrdr: driver loaded\n");
return 0;
cleanup:
vmlogrdr_cleanup();
- printk (KERN_ERR "vmlogrdr: driver not loaded.\n");
return rc;
}
@@ -884,7 +867,6 @@ cleanup:
static void __exit vmlogrdr_exit(void)
{
vmlogrdr_cleanup();
- printk (KERN_INFO "vmlogrdr: driver unloaded\n");
return;
}
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 83ae9a852f0..6fdfa5ddeca 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -9,6 +9,7 @@
*/
#include <linux/cdev.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/cio.h>
@@ -277,7 +278,8 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
struct urdev *urd;
TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n",
- intparm, irb->scsw.cstat, irb->scsw.dstat, irb->scsw.count);
+ intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
+ irb->scsw.cmd.count);
if (!intparm) {
TRACE("ur_int_handler: unsolicited interrupt\n");
@@ -288,7 +290,7 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
/* On special conditions irb is an error pointer */
if (IS_ERR(irb))
urd->io_request_rc = PTR_ERR(irb);
- else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
+ else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
urd->io_request_rc = 0;
else
urd->io_request_rc = -EIO;
@@ -343,7 +345,7 @@ static int get_urd_class(struct urdev *urd)
cc = diag210(&ur_diag210);
switch (cc) {
case 0:
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
case 2:
return ur_diag210.vrdcvcla; /* virtual device class */
case 3:
@@ -619,7 +621,7 @@ static int verify_device(struct urdev *urd)
case DEV_CLASS_UR_I:
return verify_uri_device(urd);
default:
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
}
@@ -652,7 +654,7 @@ static int get_file_reclen(struct urdev *urd)
case DEV_CLASS_UR_I:
return get_uri_file_reclen(urd);
default:
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
}
@@ -668,7 +670,7 @@ static int ur_open(struct inode *inode, struct file *file)
if (accmode == O_RDWR)
return -EACCES;
-
+ lock_kernel();
/*
* We treat the minor number as the devno of the ur device
* to find in the driver tree.
@@ -676,8 +678,10 @@ static int ur_open(struct inode *inode, struct file *file)
devno = MINOR(file->f_dentry->d_inode->i_rdev);
urd = urdev_get_from_devno(devno);
- if (!urd)
- return -ENXIO;
+ if (!urd) {
+ rc = -ENXIO;
+ goto out;
+ }
spin_lock(&urd->open_lock);
while (urd->open_flag) {
@@ -720,6 +724,7 @@ static int ur_open(struct inode *inode, struct file *file)
goto fail_urfile_free;
urf->file_reclen = rc;
file->private_data = urf;
+ unlock_kernel();
return 0;
fail_urfile_free:
@@ -730,6 +735,8 @@ fail_unlock:
spin_unlock(&urd->open_lock);
fail_put:
urdev_put(urd);
+out:
+ unlock_kernel();
return rc;
}
@@ -820,7 +827,7 @@ static int ur_probe(struct ccw_device *cdev)
goto fail_remove_attr;
}
if ((urd->class != DEV_CLASS_UR_I) && (urd->class != DEV_CLASS_UR_O)) {
- rc = -ENOTSUPP;
+ rc = -EOPNOTSUPP;
goto fail_remove_attr;
}
spin_lock_irq(get_ccwdev_lock(cdev));
@@ -879,18 +886,19 @@ static int ur_set_online(struct ccw_device *cdev)
goto fail_free_cdev;
if (urd->cdev->id.cu_type == READER_PUNCH_DEVTYPE) {
if (urd->class == DEV_CLASS_UR_I)
- sprintf(node_id, "vmrdr-%s", cdev->dev.bus_id);
+ sprintf(node_id, "vmrdr-%s", dev_name(&cdev->dev));
if (urd->class == DEV_CLASS_UR_O)
- sprintf(node_id, "vmpun-%s", cdev->dev.bus_id);
+ sprintf(node_id, "vmpun-%s", dev_name(&cdev->dev));
} else if (urd->cdev->id.cu_type == PRINTER_DEVTYPE) {
- sprintf(node_id, "vmprt-%s", cdev->dev.bus_id);
+ sprintf(node_id, "vmprt-%s", dev_name(&cdev->dev));
} else {
- rc = -ENOTSUPP;
+ rc = -EOPNOTSUPP;
goto fail_free_cdev;
}
- urd->device = device_create(vmur_class, NULL, urd->char_device->dev,
- "%s", node_id);
+ urd->device = device_create_drvdata(vmur_class, NULL,
+ urd->char_device->dev, NULL,
+ "%s", node_id);
if (IS_ERR(urd->device)) {
rc = PTR_ERR(urd->device);
TRACE("ur_set_online: device_create rc=%d\n", rc);
diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c
index 19f8389291b..21a2a829bf4 100644
--- a/drivers/s390/char/vmwatchdog.c
+++ b/drivers/s390/char/vmwatchdog.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/watchdog.h>
+#include <linux/smp_lock.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
@@ -92,23 +93,15 @@ static int vmwdt_keepalive(void)
func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init;
ret = __diag288(func, vmwdt_interval, ebc_cmd, len);
+ WARN_ON(ret != 0);
kfree(ebc_cmd);
-
- if (ret) {
- printk(KERN_WARNING "%s: problem setting interval %d, "
- "cmd %s\n", __func__, vmwdt_interval,
- vmwdt_cmd);
- }
return ret;
}
static int vmwdt_disable(void)
{
int ret = __diag288(wdt_cancel, 0, "", 0);
- if (ret) {
- printk(KERN_WARNING "%s: problem disabling watchdog\n",
- __func__);
- }
+ WARN_ON(ret != 0);
return ret;
}
@@ -121,21 +114,23 @@ static int __init vmwdt_probe(void)
static char __initdata ebc_begin[] = {
194, 197, 199, 201, 213
};
- if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) {
- printk(KERN_INFO "z/VM watchdog not available\n");
+ if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0)
return -EINVAL;
- }
return vmwdt_disable();
}
static int vmwdt_open(struct inode *i, struct file *f)
{
int ret;
- if (test_and_set_bit(0, &vmwdt_is_open))
+ lock_kernel();
+ if (test_and_set_bit(0, &vmwdt_is_open)) {
+ unlock_kernel();
return -EBUSY;
+ }
ret = vmwdt_keepalive();
if (ret)
clear_bit(0, &vmwdt_is_open);
+ unlock_kernel();
return ret ? ret : nonseekable_open(i, f);
}
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
index bbbd14e9d48..7fd84be1193 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -29,6 +29,7 @@
#define TO_USER 0
#define TO_KERNEL 1
+#define CHUNK_INFO_SIZE 34 /* 2 16-byte char, each followed by blank */
enum arch_id {
ARCH_S390 = 0,
@@ -51,6 +52,7 @@ static struct debug_info *zcore_dbf;
static int hsa_available;
static struct dentry *zcore_dir;
static struct dentry *zcore_file;
+static struct dentry *zcore_memmap_file;
/*
* Copy memory from HSA to kernel or user memory (not reentrant):
@@ -223,12 +225,10 @@ static int __init init_cpu_info(enum arch_id arch)
/* get info for boot cpu from lowcore, stored in the HSA */
sa = kmalloc(sizeof(*sa), GFP_KERNEL);
- if (!sa) {
- ERROR_MSG("kmalloc failed: %s: %i\n",__func__, __LINE__);
+ if (!sa)
return -ENOMEM;
- }
if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) {
- ERROR_MSG("could not copy from HSA\n");
+ TRACE("could not copy from HSA\n");
kfree(sa);
return -EIO;
}
@@ -478,6 +478,54 @@ static const struct file_operations zcore_fops = {
.release = zcore_release,
};
+static ssize_t zcore_memmap_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return simple_read_from_buffer(buf, count, ppos, filp->private_data,
+ MEMORY_CHUNKS * CHUNK_INFO_SIZE);
+}
+
+static int zcore_memmap_open(struct inode *inode, struct file *filp)
+{
+ int i;
+ char *buf;
+ struct mem_chunk *chunk_array;
+
+ chunk_array = kzalloc(MEMORY_CHUNKS * sizeof(struct mem_chunk),
+ GFP_KERNEL);
+ if (!chunk_array)
+ return -ENOMEM;
+ detect_memory_layout(chunk_array);
+ buf = kzalloc(MEMORY_CHUNKS * CHUNK_INFO_SIZE, GFP_KERNEL);
+ if (!buf) {
+ kfree(chunk_array);
+ return -ENOMEM;
+ }
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
+ sprintf(buf + (i * CHUNK_INFO_SIZE), "%016llx %016llx ",
+ (unsigned long long) chunk_array[i].addr,
+ (unsigned long long) chunk_array[i].size);
+ if (chunk_array[i].size == 0)
+ break;
+ }
+ kfree(chunk_array);
+ filp->private_data = buf;
+ return 0;
+}
+
+static int zcore_memmap_release(struct inode *inode, struct file *filp)
+{
+ kfree(filp->private_data);
+ return 0;
+}
+
+static const struct file_operations zcore_memmap_fops = {
+ .owner = THIS_MODULE,
+ .read = zcore_memmap_read,
+ .open = zcore_memmap_open,
+ .release = zcore_memmap_release,
+};
+
static void __init set_s390_lc_mask(union save_area *map)
{
@@ -511,6 +559,8 @@ static void __init set_s390x_lc_mask(union save_area *map)
*/
static int __init sys_info_init(enum arch_id arch)
{
+ int rc;
+
switch (arch) {
case ARCH_S390X:
MSG("DETECTED 'S390X (64 bit) OS'\n");
@@ -529,10 +579,9 @@ static int __init sys_info_init(enum arch_id arch)
return -EINVAL;
}
sys_info.arch = arch;
- if (init_cpu_info(arch)) {
- ERROR_MSG("get cpu info failed\n");
- return -ENOMEM;
- }
+ rc = init_cpu_info(arch);
+ if (rc)
+ return rc;
sys_info.mem_size = real_memory_size;
return 0;
@@ -544,29 +593,55 @@ static int __init check_sdias(void)
rc = sclp_sdias_blk_count();
if (rc < 0) {
- ERROR_MSG("Could not determine HSA size\n");
+ TRACE("Could not determine HSA size\n");
return rc;
}
act_hsa_size = (rc - 1) * PAGE_SIZE;
if (act_hsa_size < ZFCPDUMP_HSA_SIZE) {
- ERROR_MSG("HSA size too small: %i\n", act_hsa_size);
+ TRACE("HSA size too small: %i\n", act_hsa_size);
return -EINVAL;
}
return 0;
}
-static void __init zcore_header_init(int arch, struct zcore_header *hdr)
+static int __init get_mem_size(unsigned long *mem)
{
+ int i;
+ struct mem_chunk *chunk_array;
+
+ chunk_array = kzalloc(MEMORY_CHUNKS * sizeof(struct mem_chunk),
+ GFP_KERNEL);
+ if (!chunk_array)
+ return -ENOMEM;
+ detect_memory_layout(chunk_array);
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
+ if (chunk_array[i].size == 0)
+ break;
+ *mem += chunk_array[i].size;
+ }
+ kfree(chunk_array);
+ return 0;
+}
+
+static int __init zcore_header_init(int arch, struct zcore_header *hdr)
+{
+ int rc;
+ unsigned long memory = 0;
+
if (arch == ARCH_S390X)
hdr->arch_id = DUMP_ARCH_S390X;
else
hdr->arch_id = DUMP_ARCH_S390;
- hdr->mem_size = sys_info.mem_size;
- hdr->rmem_size = sys_info.mem_size;
+ rc = get_mem_size(&memory);
+ if (rc)
+ return rc;
+ hdr->mem_size = memory;
+ hdr->rmem_size = memory;
hdr->mem_end = sys_info.mem_size;
- hdr->num_pages = sys_info.mem_size / PAGE_SIZE;
+ hdr->num_pages = memory / PAGE_SIZE;
hdr->tod = get_clock();
get_cpu_id(&hdr->cpu_id);
+ return 0;
}
static int __init zcore_init(void)
@@ -590,16 +665,12 @@ static int __init zcore_init(void)
goto fail;
rc = check_sdias();
- if (rc) {
- ERROR_MSG("Dump initialization failed\n");
+ if (rc)
goto fail;
- }
rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1);
- if (rc) {
- ERROR_MSG("sdial memcpy for arch id failed\n");
+ if (rc)
goto fail;
- }
#ifndef __s390x__
if (arch == ARCH_S390X) {
@@ -610,12 +681,12 @@ static int __init zcore_init(void)
#endif
rc = sys_info_init(arch);
- if (rc) {
- ERROR_MSG("arch init failed\n");
+ if (rc)
goto fail;
- }
- zcore_header_init(arch, &zcore_header);
+ rc = zcore_header_init(arch, &zcore_header);
+ if (rc)
+ goto fail;
zcore_dir = debugfs_create_dir("zcore" , NULL);
if (!zcore_dir) {
@@ -625,13 +696,22 @@ static int __init zcore_init(void)
zcore_file = debugfs_create_file("mem", S_IRUSR, zcore_dir, NULL,
&zcore_fops);
if (!zcore_file) {
- debugfs_remove(zcore_dir);
rc = -ENOMEM;
- goto fail;
+ goto fail_dir;
+ }
+ zcore_memmap_file = debugfs_create_file("memmap", S_IRUSR, zcore_dir,
+ NULL, &zcore_memmap_fops);
+ if (!zcore_memmap_file) {
+ rc = -ENOMEM;
+ goto fail_file;
}
hsa_available = 1;
return 0;
+fail_file:
+ debugfs_remove(zcore_file);
+fail_dir:
+ debugfs_remove(zcore_dir);
fail:
diag308(DIAG308_REL_HSA, NULL);
return rc;
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index cfaf77b320f..bd79bd16539 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -2,9 +2,13 @@
# Makefile for the S/390 common i/o drivers
#
-obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o
+obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o scsw.o \
+ fcx.o itcw.o
ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o
+obj-$(CONFIG_CHSC_SCH) += chsc_sch.o
obj-$(CONFIG_CCWGROUP) += ccwgroup.o
+
+qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_perf.o qdio_setup.o
obj-$(CONFIG_QDIO) += qdio.o
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c
index b7a07a86629..fe6cea15bba 100644
--- a/drivers/s390/cio/airq.c
+++ b/drivers/s390/cio/airq.c
@@ -15,6 +15,7 @@
#include <linux/rcupdate.h>
#include <asm/airq.h>
+#include <asm/isc.h>
#include "cio.h"
#include "cio_debug.h"
@@ -33,15 +34,15 @@ struct airq_t {
void *drv_data;
};
-static union indicator_t indicators;
-static struct airq_t *airqs[NR_AIRQS];
+static union indicator_t indicators[MAX_ISC];
+static struct airq_t *airqs[MAX_ISC][NR_AIRQS];
-static int register_airq(struct airq_t *airq)
+static int register_airq(struct airq_t *airq, u8 isc)
{
int i;
for (i = 0; i < NR_AIRQS; i++)
- if (!cmpxchg(&airqs[i], NULL, airq))
+ if (!cmpxchg(&airqs[isc][i], NULL, airq))
return i;
return -ENOMEM;
}
@@ -50,18 +51,21 @@ static int register_airq(struct airq_t *airq)
* s390_register_adapter_interrupt() - register adapter interrupt handler
* @handler: adapter handler to be registered
* @drv_data: driver data passed with each call to the handler
+ * @isc: isc for which the handler should be called
*
* Returns:
* Pointer to the indicator to be used on success
* ERR_PTR() if registration failed
*/
void *s390_register_adapter_interrupt(adapter_int_handler_t handler,
- void *drv_data)
+ void *drv_data, u8 isc)
{
struct airq_t *airq;
char dbf_txt[16];
int ret;
+ if (isc > MAX_ISC)
+ return ERR_PTR(-EINVAL);
airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL);
if (!airq) {
ret = -ENOMEM;
@@ -69,34 +73,35 @@ void *s390_register_adapter_interrupt(adapter_int_handler_t handler,
}
airq->handler = handler;
airq->drv_data = drv_data;
- ret = register_airq(airq);
- if (ret < 0)
- kfree(airq);
+
+ ret = register_airq(airq, isc);
out:
snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret);
CIO_TRACE_EVENT(4, dbf_txt);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(airq);
return ERR_PTR(ret);
- else
- return &indicators.byte[ret];
+ } else
+ return &indicators[isc].byte[ret];
}
EXPORT_SYMBOL(s390_register_adapter_interrupt);
/**
* s390_unregister_adapter_interrupt - unregister adapter interrupt handler
* @ind: indicator for which the handler is to be unregistered
+ * @isc: interruption subclass
*/
-void s390_unregister_adapter_interrupt(void *ind)
+void s390_unregister_adapter_interrupt(void *ind, u8 isc)
{
struct airq_t *airq;
char dbf_txt[16];
int i;
- i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]);
+ i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]);
snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i);
CIO_TRACE_EVENT(4, dbf_txt);
- indicators.byte[i] = 0;
- airq = xchg(&airqs[i], NULL);
+ indicators[isc].byte[i] = 0;
+ airq = xchg(&airqs[isc][i], NULL);
/*
* Allow interrupts to complete. This will ensure that the airq handle
* is no longer referenced by any interrupt handler.
@@ -108,7 +113,7 @@ EXPORT_SYMBOL(s390_unregister_adapter_interrupt);
#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
-void do_adapter_IO(void)
+void do_adapter_IO(u8 isc)
{
int w;
int i;
@@ -120,22 +125,22 @@ void do_adapter_IO(void)
* fetch operations.
*/
for (w = 0; w < NR_AIRQ_WORDS; w++) {
- word = indicators.word[w];
+ word = indicators[isc].word[w];
i = w * NR_AIRQS_PER_WORD;
/*
* Check bytes within word for active indicators.
*/
while (word) {
if (word & INDICATOR_MASK) {
- airq = airqs[i];
+ airq = airqs[isc][i];
if (likely(airq))
- airq->handler(&indicators.byte[i],
+ airq->handler(&indicators[isc].byte[i],
airq->drv_data);
else
/*
* Reset ill-behaved indicator.
*/
- indicators.byte[i] = 0;
+ indicators[isc].byte[i] = 0;
}
word <<= 8;
i++;
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 0bfcbbe375c..2f547b840ef 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -24,6 +24,7 @@
#include "cio.h"
#include "cio_debug.h"
#include "css.h"
+#include "device.h"
/*
* "Blacklisting" of certain devices:
@@ -191,9 +192,9 @@ static int blacklist_parse_parameters(char *str, range_action action,
rc = blacklist_range(ra, from_ssid, to_ssid, from, to,
msgtrigger);
if (rc)
- totalrc = 1;
+ totalrc = -EINVAL;
} else
- totalrc = 1;
+ totalrc = -EINVAL;
}
return totalrc;
@@ -240,8 +241,10 @@ static int blacklist_parse_proc_parameters(char *buf)
rc = blacklist_parse_parameters(buf, free, 0);
else if (strcmp("add", parm) == 0)
rc = blacklist_parse_parameters(buf, add, 0);
+ else if (strcmp("purge", parm) == 0)
+ return ccw_purge_blacklisted();
else
- return 1;
+ return -EINVAL;
css_schedule_reprobe();
@@ -353,7 +356,7 @@ cio_ignore_write(struct file *file, const char __user *user_buf,
}
ret = blacklist_parse_proc_parameters(buf);
if (ret)
- rc = -EINVAL;
+ rc = ret;
else
rc = user_len;
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 26a930e832b..3ac2c2019f5 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -112,8 +112,11 @@ ccwgroup_release (struct device *dev)
gdev = to_ccwgroupdev(dev);
for (i = 0; i < gdev->count; i++) {
- dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
- put_device(&gdev->cdev[i]->dev);
+ if (gdev->cdev[i]) {
+ if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
+ dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
+ put_device(&gdev->cdev[i]->dev);
+ }
}
kfree(gdev);
}
@@ -221,6 +224,13 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
atomic_set(&gdev->onoff, 0);
mutex_init(&gdev->reg_mutex);
mutex_lock(&gdev->reg_mutex);
+ gdev->creator_id = creator_id;
+ gdev->count = num_devices;
+ gdev->dev.bus = &ccwgroup_bus_type;
+ gdev->dev.parent = root;
+ gdev->dev.release = ccwgroup_release;
+ device_initialize(&gdev->dev);
+
curr_buf = buf;
for (i = 0; i < num_devices && curr_buf; i++) {
rc = __get_next_bus_id(&curr_buf, tmp_bus_id);
@@ -258,16 +268,10 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
rc = -EINVAL;
goto error;
}
- gdev->creator_id = creator_id;
- gdev->count = num_devices;
- gdev->dev.bus = &ccwgroup_bus_type;
- gdev->dev.parent = root;
- gdev->dev.release = ccwgroup_release;
- snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s",
- gdev->cdev[0]->dev.bus_id);
+ dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
- rc = device_register(&gdev->dev);
+ rc = device_add(&gdev->dev);
if (rc)
goto error;
get_device(&gdev->dev);
@@ -292,6 +296,7 @@ error:
if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
put_device(&gdev->cdev[i]->dev);
+ gdev->cdev[i] = NULL;
}
mutex_unlock(&gdev->reg_mutex);
put_device(&gdev->dev);
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index 297cdceb0ca..1246f61a533 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -18,6 +18,7 @@
#include <asm/chpid.h>
#include <asm/sclp.h>
+#include "../s390mach.h"
#include "cio.h"
#include "css.h"
#include "ioasm.h"
@@ -94,6 +95,7 @@ u8 chp_get_sch_opm(struct subchannel *sch)
}
return opm;
}
+EXPORT_SYMBOL_GPL(chp_get_sch_opm);
/**
* chp_is_registered - check if a channel-path is registered
@@ -121,11 +123,8 @@ static int s390_vary_chpid(struct chp_id chpid, int on)
CIO_TRACE_EVENT(2, dbf_text);
status = chp_get_status(chpid);
- if (!on && !status) {
- printk(KERN_ERR "cio: chpid %x.%02x is already offline\n",
- chpid.cssid, chpid.id);
- return -EINVAL;
- }
+ if (!on && !status)
+ return 0;
set_chp_logically_online(chpid, on);
chsc_chp_vary(chpid, on);
@@ -141,21 +140,14 @@ static ssize_t chp_measurement_chars_read(struct kobject *kobj,
{
struct channel_path *chp;
struct device *device;
- unsigned int size;
device = container_of(kobj, struct device, kobj);
chp = to_channelpath(device);
if (!chp->cmg_chars)
return 0;
- size = sizeof(struct cmg_chars);
-
- if (off > size)
- return 0;
- if (off + count > size)
- count = size - off;
- memcpy(buf, chp->cmg_chars + off, count);
- return count;
+ return memory_read_from_buffer(buf, count, &off,
+ chp->cmg_chars, sizeof(struct cmg_chars));
}
static struct bin_attribute chp_measurement_chars_attr = {
@@ -401,11 +393,10 @@ int chp_new(struct chp_id chpid)
chp->state = 1;
chp->dev.parent = &channel_subsystems[chpid.cssid]->device;
chp->dev.release = chp_release;
- snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid,
- chpid.id);
+ dev_set_name(&chp->dev, "chp%x.%02x", chpid.cssid, chpid.id);
/* Obtain channel path description and fill it in. */
- ret = chsc_determine_channel_path_description(chpid, &chp->desc);
+ ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc);
if (ret)
goto out_free;
if ((chp->desc.flags & 0x80) == 0) {
@@ -413,8 +404,7 @@ int chp_new(struct chp_id chpid)
goto out_free;
}
/* Get channel-measurement characteristics. */
- if (css_characteristics_avail && css_chsc_characteristics.scmc
- && css_chsc_characteristics.secm) {
+ if (css_chsc_characteristics.scmc && css_chsc_characteristics.secm) {
ret = chsc_get_channel_measurement_chars(chp);
if (ret)
goto out_free;
@@ -432,7 +422,7 @@ int chp_new(struct chp_id chpid)
ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group);
if (ret) {
device_unregister(&chp->dev);
- goto out_free;
+ goto out;
}
mutex_lock(&channel_subsystems[chpid.cssid]->mutex);
if (channel_subsystems[chpid.cssid]->cm_enabled) {
@@ -441,14 +431,15 @@ int chp_new(struct chp_id chpid)
sysfs_remove_group(&chp->dev.kobj, &chp_attr_group);
device_unregister(&chp->dev);
mutex_unlock(&channel_subsystems[chpid.cssid]->mutex);
- goto out_free;
+ goto out;
}
}
channel_subsystems[chpid.cssid]->chps[chpid.id] = chp;
mutex_unlock(&channel_subsystems[chpid.cssid]->mutex);
- return ret;
+ goto out;
out_free:
kfree(chp);
+out:
return ret;
}
@@ -476,26 +467,74 @@ void *chp_get_chp_desc(struct chp_id chpid)
/**
* chp_process_crw - process channel-path status change
- * @id: channel-path ID number
- * @status: non-zero if channel-path has become available, zero otherwise
+ * @crw0: channel report-word to handler
+ * @crw1: second channel-report word (always NULL)
+ * @overflow: crw overflow indication
*
* Handle channel-report-words indicating that the status of a channel-path
* has changed.
*/
-void chp_process_crw(int id, int status)
+static void chp_process_crw(struct crw *crw0, struct crw *crw1,
+ int overflow)
{
struct chp_id chpid;
+ if (overflow) {
+ css_schedule_eval_all();
+ return;
+ }
+ CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
+ "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+ crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
+ crw0->erc, crw0->rsid);
+ /*
+ * Check for solicited machine checks. These are
+ * created by reset channel path and need not be
+ * handled here.
+ */
+ if (crw0->slct) {
+ CIO_CRW_EVENT(2, "solicited machine check for "
+ "channel path %02X\n", crw0->rsid);
+ return;
+ }
chp_id_init(&chpid);
- chpid.id = id;
- if (status) {
+ chpid.id = crw0->rsid;
+ switch (crw0->erc) {
+ case CRW_ERC_IPARM: /* Path has come. */
if (!chp_is_registered(chpid))
chp_new(chpid);
chsc_chp_online(chpid);
- } else
+ break;
+ case CRW_ERC_PERRI: /* Path has gone. */
+ case CRW_ERC_PERRN:
chsc_chp_offline(chpid);
+ break;
+ default:
+ CIO_CRW_EVENT(2, "Don't know how to handle erc=%x\n",
+ crw0->erc);
+ }
}
+int chp_ssd_get_mask(struct chsc_ssd_info *ssd, struct chp_link *link)
+{
+ int i;
+ int mask;
+
+ for (i = 0; i < 8; i++) {
+ mask = 0x80 >> i;
+ if (!(ssd->path_mask & mask))
+ continue;
+ if (!chp_id_is_equal(&ssd->chpid[i], &link->chpid))
+ continue;
+ if ((ssd->fla_valid_mask & mask) &&
+ ((ssd->fla[i] & link->fla_mask) != link->fla))
+ continue;
+ return mask;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(chp_ssd_get_mask);
+
static inline int info_bit_num(struct chp_id id)
{
return id.id + id.cssid * (__MAX_CHPID + 1);
@@ -575,6 +614,7 @@ static void cfg_func(struct work_struct *work)
{
struct chp_id chpid;
enum cfg_task_t t;
+ int rc;
mutex_lock(&cfg_lock);
t = cfg_none;
@@ -589,14 +629,24 @@ static void cfg_func(struct work_struct *work)
switch (t) {
case cfg_configure:
- sclp_chp_configure(chpid);
- info_expire();
- chsc_chp_online(chpid);
+ rc = sclp_chp_configure(chpid);
+ if (rc)
+ CIO_MSG_EVENT(2, "chp: sclp_chp_configure(%x.%02x)="
+ "%d\n", chpid.cssid, chpid.id, rc);
+ else {
+ info_expire();
+ chsc_chp_online(chpid);
+ }
break;
case cfg_deconfigure:
- sclp_chp_deconfigure(chpid);
- info_expire();
- chsc_chp_offline(chpid);
+ rc = sclp_chp_deconfigure(chpid);
+ if (rc)
+ CIO_MSG_EVENT(2, "chp: sclp_chp_deconfigure(%x.%02x)="
+ "%d\n", chpid.cssid, chpid.id, rc);
+ else {
+ info_expire();
+ chsc_chp_offline(chpid);
+ }
break;
case cfg_none:
/* Get updated information after last change. */
@@ -654,10 +704,16 @@ static int cfg_wait_idle(void)
static int __init chp_init(void)
{
struct chp_id chpid;
+ int ret;
+ ret = s390_register_crw_handler(CRW_RSC_CPATH, chp_process_crw);
+ if (ret)
+ return ret;
chp_wq = create_singlethread_workqueue("cio_chp");
- if (!chp_wq)
+ if (!chp_wq) {
+ s390_unregister_crw_handler(CRW_RSC_CPATH);
return -ENOMEM;
+ }
INIT_WORK(&cfg_work, cfg_func);
init_waitqueue_head(&cfg_wait_queue);
if (info_update())
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
index 65286563c59..26c3d224617 100644
--- a/drivers/s390/cio/chp.h
+++ b/drivers/s390/cio/chp.h
@@ -12,12 +12,24 @@
#include <linux/device.h>
#include <asm/chpid.h>
#include "chsc.h"
+#include "css.h"
#define CHP_STATUS_STANDBY 0
#define CHP_STATUS_CONFIGURED 1
#define CHP_STATUS_RESERVED 2
#define CHP_STATUS_NOT_RECOGNIZED 3
+#define CHP_ONLINE 0
+#define CHP_OFFLINE 1
+#define CHP_VARY_ON 2
+#define CHP_VARY_OFF 3
+
+struct chp_link {
+ struct chp_id chpid;
+ u32 fla_mask;
+ u16 fla;
+};
+
static inline int chp_test_bit(u8 *bitmap, int num)
{
int byte = num >> 3;
@@ -42,12 +54,11 @@ int chp_get_status(struct chp_id chpid);
u8 chp_get_sch_opm(struct subchannel *sch);
int chp_is_registered(struct chp_id chpid);
void *chp_get_chp_desc(struct chp_id chpid);
-void chp_process_crw(int id, int available);
void chp_remove_cmg_attr(struct channel_path *chp);
int chp_add_cmg_attr(struct channel_path *chp);
int chp_new(struct chp_id chpid);
void chp_cfg_schedule(struct chp_id chpid, int configure);
void chp_cfg_cancel_deconfigure(struct chp_id chpid);
int chp_info_get_status(struct chp_id chpid);
-
+int chp_ssd_get_mask(struct chsc_ssd_info *, struct chp_link *);
#endif /* S390_CHP_H */
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 5de86908b0d..29826fdd47b 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -2,8 +2,7 @@
* drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call
*
- * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
+ * Copyright IBM Corp. 1999,2008
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
@@ -16,7 +15,9 @@
#include <asm/cio.h>
#include <asm/chpid.h>
+#include <asm/chsc.h>
+#include "../s390mach.h"
#include "css.h"
#include "cio.h"
#include "cio_debug.h"
@@ -26,7 +27,13 @@
static void *sei_page;
-static int chsc_error_from_response(int response)
+/**
+ * chsc_error_from_response() - convert a chsc response to an error
+ * @response: chsc response code
+ *
+ * Returns an appropriate Linux error code for @response.
+ */
+int chsc_error_from_response(int response)
{
switch (response) {
case 0x0001:
@@ -44,6 +51,7 @@ static int chsc_error_from_response(int response)
return -EIO;
}
}
+EXPORT_SYMBOL_GPL(chsc_error_from_response);
struct chsc_ssd_area {
struct chsc_header request;
@@ -127,77 +135,12 @@ out_free:
return ret;
}
-static int check_for_io_on_path(struct subchannel *sch, int mask)
-{
- int cc;
-
- cc = stsch(sch->schid, &sch->schib);
- if (cc)
- return 0;
- if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask)
- return 1;
- return 0;
-}
-
-static void terminate_internal_io(struct subchannel *sch)
-{
- if (cio_clear(sch)) {
- /* Recheck device in case clear failed. */
- sch->lpm = 0;
- if (device_trigger_verify(sch) != 0)
- css_schedule_eval(sch->schid);
- return;
- }
- /* Request retry of internal operation. */
- device_set_intretry(sch);
- /* Call handler. */
- if (sch->driver && sch->driver->termination)
- sch->driver->termination(sch);
-}
-
static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
{
- int j;
- int mask;
- struct chp_id *chpid = data;
- struct schib schib;
-
- for (j = 0; j < 8; j++) {
- mask = 0x80 >> j;
- if ((sch->schib.pmcw.pim & mask) &&
- (sch->schib.pmcw.chpid[j] == chpid->id))
- break;
- }
- if (j >= 8)
- return 0;
-
spin_lock_irq(sch->lock);
-
- stsch(sch->schid, &schib);
- if (!css_sch_is_valid(&schib))
- goto out_unreg;
- memcpy(&sch->schib, &schib, sizeof(struct schib));
- /* Check for single path devices. */
- if (sch->schib.pmcw.pim == 0x80)
- goto out_unreg;
-
- if (check_for_io_on_path(sch, mask)) {
- if (device_is_online(sch))
- device_kill_io(sch);
- else {
- terminate_internal_io(sch);
- /* Re-start path verification. */
- if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
- }
- } else {
- /* trigger path verification. */
- if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
- else if (sch->lpm == mask)
+ if (sch->driver && sch->driver->chp_event)
+ if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0)
goto out_unreg;
- }
-
spin_unlock_irq(sch->lock);
return 0;
@@ -211,15 +154,18 @@ out_unreg:
void chsc_chp_offline(struct chp_id chpid)
{
char dbf_txt[15];
+ struct chp_link link;
sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
CIO_TRACE_EVENT(2, dbf_txt);
if (chp_get_status(chpid) <= 0)
return;
+ memset(&link, 0, sizeof(struct chp_link));
+ link.chpid = chpid;
/* Wait until previous actions have settled. */
css_wait_for_slow_path();
- for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid);
+ for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &link);
}
static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
@@ -242,67 +188,25 @@ static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
return 0;
}
-struct res_acc_data {
- struct chp_id chpid;
- u32 fla_mask;
- u16 fla;
-};
-
-static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
- struct res_acc_data *data)
-{
- int i;
- int mask;
-
- for (i = 0; i < 8; i++) {
- mask = 0x80 >> i;
- if (!(ssd->path_mask & mask))
- continue;
- if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
- continue;
- if ((ssd->fla_valid_mask & mask) &&
- ((ssd->fla[i] & data->fla_mask) != data->fla))
- continue;
- return mask;
- }
- return 0;
-}
-
static int __s390_process_res_acc(struct subchannel *sch, void *data)
{
- int chp_mask, old_lpm;
- struct res_acc_data *res_data = data;
-
spin_lock_irq(sch->lock);
- chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
- if (chp_mask == 0)
- goto out;
- if (stsch(sch->schid, &sch->schib))
- goto out;
- old_lpm = sch->lpm;
- sch->lpm = ((sch->schib.pmcw.pim &
- sch->schib.pmcw.pam &
- sch->schib.pmcw.pom)
- | chp_mask) & sch->opm;
- if (!old_lpm && sch->lpm)
- device_trigger_reprobe(sch);
- else if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
-out:
+ if (sch->driver && sch->driver->chp_event)
+ sch->driver->chp_event(sch, data, CHP_ONLINE);
spin_unlock_irq(sch->lock);
return 0;
}
-static void s390_process_res_acc (struct res_acc_data *res_data)
+static void s390_process_res_acc(struct chp_link *link)
{
char dbf_txt[15];
- sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid,
- res_data->chpid.id);
+ sprintf(dbf_txt, "accpr%x.%02x", link->chpid.cssid,
+ link->chpid.id);
CIO_TRACE_EVENT( 2, dbf_txt);
- if (res_data->fla != 0) {
- sprintf(dbf_txt, "fla%x", res_data->fla);
+ if (link->fla != 0) {
+ sprintf(dbf_txt, "fla%x", link->fla);
CIO_TRACE_EVENT( 2, dbf_txt);
}
/* Wait until previous actions have settled. */
@@ -315,7 +219,7 @@ static void s390_process_res_acc (struct res_acc_data *res_data)
* will we have to do.
*/
for_each_subchannel_staged(__s390_process_res_acc,
- s390_process_res_acc_new_sch, res_data);
+ s390_process_res_acc_new_sch, link);
}
static int
@@ -388,7 +292,7 @@ static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
{
- struct res_acc_data res_data;
+ struct chp_link link;
struct chp_id chpid;
int status;
@@ -404,18 +308,18 @@ static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
chp_new(chpid);
else if (!status)
return;
- memset(&res_data, 0, sizeof(struct res_acc_data));
- res_data.chpid = chpid;
+ memset(&link, 0, sizeof(struct chp_link));
+ link.chpid = chpid;
if ((sei_area->vf & 0xc0) != 0) {
- res_data.fla = sei_area->fla;
+ link.fla = sei_area->fla;
if ((sei_area->vf & 0xc0) == 0xc0)
/* full link address */
- res_data.fla_mask = 0xffff;
+ link.fla_mask = 0xffff;
else
/* link address */
- res_data.fla_mask = 0xff00;
+ link.fla_mask = 0xff00;
}
- s390_process_res_acc(&res_data);
+ s390_process_res_acc(&link);
}
struct chp_config_data {
@@ -480,17 +384,25 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area)
}
}
-void chsc_process_crw(void)
+static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
{
struct chsc_sei_area *sei_area;
+ if (overflow) {
+ css_schedule_eval_all();
+ return;
+ }
+ CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
+ "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+ crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
+ crw0->erc, crw0->rsid);
if (!sei_page)
return;
/* Access to sei_page is serialized through machine check handler
* thread, so no need for locking. */
sei_area = sei_page;
- CIO_TRACE_EVENT( 2, "prcss");
+ CIO_TRACE_EVENT(2, "prcss");
do {
memset(sei_area, 0, sizeof(*sei_area));
sei_area->request.length = 0x0010;
@@ -509,114 +421,36 @@ void chsc_process_crw(void)
} while (sei_area->flags & 0x80);
}
-static int __chp_add_new_sch(struct subchannel_id schid, void *data)
-{
- struct schib schib;
-
- if (stsch_err(schid, &schib))
- /* We're through */
- return -ENXIO;
-
- /* Put it on the slow path. */
- css_schedule_eval(schid);
- return 0;
-}
-
-
-static int __chp_add(struct subchannel *sch, void *data)
-{
- int i, mask;
- struct chp_id *chpid = data;
-
- spin_lock_irq(sch->lock);
- for (i=0; i<8; i++) {
- mask = 0x80 >> i;
- if ((sch->schib.pmcw.pim & mask) &&
- (sch->schib.pmcw.chpid[i] == chpid->id))
- break;
- }
- if (i==8) {
- spin_unlock_irq(sch->lock);
- return 0;
- }
- if (stsch(sch->schid, &sch->schib)) {
- spin_unlock_irq(sch->lock);
- css_schedule_eval(sch->schid);
- return 0;
- }
- sch->lpm = ((sch->schib.pmcw.pim &
- sch->schib.pmcw.pam &
- sch->schib.pmcw.pom)
- | mask) & sch->opm;
-
- if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
-
- spin_unlock_irq(sch->lock);
-
- return 0;
-}
-
void chsc_chp_online(struct chp_id chpid)
{
char dbf_txt[15];
+ struct chp_link link;
sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
CIO_TRACE_EVENT(2, dbf_txt);
if (chp_get_status(chpid) != 0) {
+ memset(&link, 0, sizeof(struct chp_link));
+ link.chpid = chpid;
/* Wait until previous actions have settled. */
css_wait_for_slow_path();
- for_each_subchannel_staged(__chp_add, __chp_add_new_sch,
- &chpid);
+ for_each_subchannel_staged(__s390_process_res_acc, NULL,
+ &link);
}
}
static void __s390_subchannel_vary_chpid(struct subchannel *sch,
struct chp_id chpid, int on)
{
- int chp, old_lpm;
- int mask;
unsigned long flags;
+ struct chp_link link;
+ memset(&link, 0, sizeof(struct chp_link));
+ link.chpid = chpid;
spin_lock_irqsave(sch->lock, flags);
- old_lpm = sch->lpm;
- for (chp = 0; chp < 8; chp++) {
- mask = 0x80 >> chp;
- if (!(sch->ssd_info.path_mask & mask))
- continue;
- if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid))
- continue;
-
- if (on) {
- sch->opm |= mask;
- sch->lpm |= mask;
- if (!old_lpm)
- device_trigger_reprobe(sch);
- else if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
- break;
- }
- sch->opm &= ~mask;
- sch->lpm &= ~mask;
- if (check_for_io_on_path(sch, mask)) {
- if (device_is_online(sch))
- /* Path verification is done after killing. */
- device_kill_io(sch);
- else {
- /* Kill and retry internal I/O. */
- terminate_internal_io(sch);
- /* Re-start path verification. */
- if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
- }
- } else if (!sch->lpm) {
- if (device_trigger_verify(sch) != 0)
- css_schedule_eval(sch->schid);
- } else if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
- break;
- }
+ if (sch->driver && sch->driver->chp_event)
+ sch->driver->chp_event(sch, &link,
+ on ? CHP_VARY_ON : CHP_VARY_OFF);
spin_unlock_irqrestore(sch->lock, flags);
}
@@ -656,6 +490,10 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data)
*/
int chsc_chp_vary(struct chp_id chpid, int on)
{
+ struct chp_link link;
+
+ memset(&link, 0, sizeof(struct chp_link));
+ link.chpid = chpid;
/* Wait until previous actions have settled. */
css_wait_for_slow_path();
/*
@@ -664,10 +502,10 @@ int chsc_chp_vary(struct chp_id chpid, int on)
if (on)
for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
- __s390_vary_chpid_on, &chpid);
+ __s390_vary_chpid_on, &link);
else
for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
- NULL, &chpid);
+ NULL, &link);
return 0;
}
@@ -797,23 +635,33 @@ chsc_secm(struct channel_subsystem *css, int enable)
return ret;
}
-int chsc_determine_channel_path_description(struct chp_id chpid,
- struct channel_path_desc *desc)
+int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
+ int c, int m,
+ struct chsc_response_struct *resp)
{
int ccode, ret;
struct {
struct chsc_header request;
- u32 : 24;
+ u32 : 2;
+ u32 m : 1;
+ u32 c : 1;
+ u32 fmt : 4;
+ u32 cssid : 8;
+ u32 : 4;
+ u32 rfmt : 4;
u32 first_chpid : 8;
u32 : 24;
u32 last_chpid : 8;
u32 zeroes1;
struct chsc_header response;
- u32 zeroes2;
- struct channel_path_desc desc;
+ u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *scpd_area;
+ if ((rfmt == 1) && !css_general_characteristics.fcs)
+ return -EINVAL;
+ if ((rfmt == 2) && !css_general_characteristics.cib)
+ return -EINVAL;
scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scpd_area)
return -ENOMEM;
@@ -821,8 +669,13 @@ int chsc_determine_channel_path_description(struct chp_id chpid,
scpd_area->request.length = 0x0010;
scpd_area->request.code = 0x0002;
+ scpd_area->cssid = chpid.cssid;
scpd_area->first_chpid = chpid.id;
scpd_area->last_chpid = chpid.id;
+ scpd_area->m = m;
+ scpd_area->c = c;
+ scpd_area->fmt = fmt;
+ scpd_area->rfmt = rfmt;
ccode = chsc(scpd_area);
if (ccode > 0) {
@@ -833,8 +686,7 @@ int chsc_determine_channel_path_description(struct chp_id chpid,
ret = chsc_error_from_response(scpd_area->response.code);
if (ret == 0)
/* Success. */
- memcpy(desc, &scpd_area->desc,
- sizeof(struct channel_path_desc));
+ memcpy(resp, &scpd_area->response, scpd_area->response.length);
else
CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n",
scpd_area->response.code);
@@ -842,6 +694,25 @@ out:
free_page((unsigned long)scpd_area);
return ret;
}
+EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc);
+
+int chsc_determine_base_channel_path_desc(struct chp_id chpid,
+ struct channel_path_desc *desc)
+{
+ struct chsc_response_struct *chsc_resp;
+ int ret;
+
+ chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL);
+ if (!chsc_resp)
+ return -ENOMEM;
+ ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp);
+ if (ret)
+ goto out_free;
+ memcpy(desc, &chsc_resp->data, chsc_resp->length);
+out_free:
+ kfree(chsc_resp);
+ return ret;
+}
static void
chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
@@ -937,15 +808,23 @@ out:
int __init chsc_alloc_sei_area(void)
{
+ int ret;
+
sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!sei_page)
+ if (!sei_page) {
CIO_MSG_EVENT(0, "Can't allocate page for processing of "
"chsc machine checks!\n");
- return (sei_page ? 0 : -ENOMEM);
+ return -ENOMEM;
+ }
+ ret = s390_register_crw_handler(CRW_RSC_CSS, chsc_process_crw);
+ if (ret)
+ kfree(sei_page);
+ return ret;
}
void __init chsc_free_sei_area(void)
{
+ s390_unregister_crw_handler(CRW_RSC_CSS);
kfree(sei_page);
}
@@ -1043,3 +922,52 @@ exit:
EXPORT_SYMBOL_GPL(css_general_characteristics);
EXPORT_SYMBOL_GPL(css_chsc_characteristics);
+
+int chsc_sstpc(void *page, unsigned int op, u16 ctrl)
+{
+ struct {
+ struct chsc_header request;
+ unsigned int rsvd0;
+ unsigned int op : 8;
+ unsigned int rsvd1 : 8;
+ unsigned int ctrl : 16;
+ unsigned int rsvd2[5];
+ struct chsc_header response;
+ unsigned int rsvd3[7];
+ } __attribute__ ((packed)) *rr;
+ int rc;
+
+ memset(page, 0, PAGE_SIZE);
+ rr = page;
+ rr->request.length = 0x0020;
+ rr->request.code = 0x0033;
+ rr->op = op;
+ rr->ctrl = ctrl;
+ rc = chsc(rr);
+ if (rc)
+ return -EIO;
+ rc = (rr->response.code == 0x0001) ? 0 : -EIO;
+ return rc;
+}
+
+int chsc_sstpi(void *page, void *result, size_t size)
+{
+ struct {
+ struct chsc_header request;
+ unsigned int rsvd0[3];
+ struct chsc_header response;
+ char data[size];
+ } __attribute__ ((packed)) *rr;
+ int rc;
+
+ memset(page, 0, PAGE_SIZE);
+ rr = page;
+ rr->request.length = 0x0010;
+ rr->request.code = 0x0038;
+ rc = chsc(rr);
+ if (rc)
+ return -EIO;
+ memcpy(result, &rr->data, size);
+ return (rr->response.code == 0x0001) ? 0 : -EIO;
+}
+
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index d1f5db1e69b..ba59bceace9 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -4,7 +4,8 @@
#include <linux/types.h>
#include <linux/device.h>
#include <asm/chpid.h>
-#include "schid.h"
+#include <asm/chsc.h>
+#include <asm/schid.h>
#define CHSC_SDA_OC_MSS 0x2
@@ -36,14 +37,15 @@ struct channel_path_desc {
struct channel_path;
-extern void chsc_process_crw(void);
-
struct css_general_char {
- u64 : 41;
+ u64 : 12;
+ u32 dynio : 1; /* bit 12 */
+ u32 : 28;
u32 aif : 1; /* bit 41 */
u32 : 3;
u32 mcss : 1; /* bit 45 */
- u32 : 2;
+ u32 fcs : 1; /* bit 46 */
+ u32 : 1;
u32 ext_mb : 1; /* bit 48 */
u32 : 7;
u32 aif_tdd : 1; /* bit 56 */
@@ -51,7 +53,11 @@ struct css_general_char {
u32 qebsm : 1; /* bit 58 */
u32 : 8;
u32 aif_osa : 1; /* bit 67 */
- u32 : 28;
+ u32 : 14;
+ u32 cib : 1; /* bit 82 */
+ u32 : 5;
+ u32 fcx : 1; /* bit 88 */
+ u32 : 7;
}__attribute__((packed));
struct css_chsc_char {
@@ -78,7 +84,6 @@ struct chsc_ssd_info {
extern int chsc_get_ssd_info(struct subchannel_id schid,
struct chsc_ssd_info *ssd);
extern int chsc_determine_css_characteristics(void);
-extern int css_characteristics_avail;
extern int chsc_alloc_sei_area(void);
extern void chsc_free_sei_area(void);
@@ -87,10 +92,15 @@ struct channel_subsystem;
extern int chsc_secm(struct channel_subsystem *, int);
int chsc_chp_vary(struct chp_id chpid, int on);
-int chsc_determine_channel_path_description(struct chp_id chpid,
- struct channel_path_desc *desc);
+int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
+ int c, int m,
+ struct chsc_response_struct *resp);
+int chsc_determine_base_channel_path_desc(struct chp_id chpid,
+ struct channel_path_desc *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);
+int chsc_error_from_response(int response);
+
#endif
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
new file mode 100644
index 00000000000..f49f0e502b8
--- /dev/null
+++ b/drivers/s390/cio/chsc_sch.c
@@ -0,0 +1,820 @@
+/*
+ * Driver for s390 chsc subchannels
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+
+#include <asm/cio.h>
+#include <asm/chsc.h>
+#include <asm/isc.h>
+
+#include "cio.h"
+#include "cio_debug.h"
+#include "css.h"
+#include "chsc_sch.h"
+#include "ioasm.h"
+
+static debug_info_t *chsc_debug_msg_id;
+static debug_info_t *chsc_debug_log_id;
+
+#define CHSC_MSG(imp, args...) do { \
+ debug_sprintf_event(chsc_debug_msg_id, imp , ##args); \
+ } while (0)
+
+#define CHSC_LOG(imp, txt) do { \
+ debug_text_event(chsc_debug_log_id, imp , txt); \
+ } while (0)
+
+static void CHSC_LOG_HEX(int level, void *data, int length)
+{
+ while (length > 0) {
+ debug_event(chsc_debug_log_id, level, data, length);
+ length -= chsc_debug_log_id->buf_size;
+ data += chsc_debug_log_id->buf_size;
+ }
+}
+
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("driver for s390 chsc subchannels");
+MODULE_LICENSE("GPL");
+
+static void chsc_subchannel_irq(struct subchannel *sch)
+{
+ struct chsc_private *private = sch->private;
+ struct chsc_request *request = private->request;
+ struct irb *irb = (struct irb *)__LC_IRB;
+
+ CHSC_LOG(4, "irb");
+ CHSC_LOG_HEX(4, irb, sizeof(*irb));
+ /* Copy irb to provided request and set done. */
+ if (!request) {
+ CHSC_MSG(0, "Interrupt on sch 0.%x.%04x with no request\n",
+ sch->schid.ssid, sch->schid.sch_no);
+ return;
+ }
+ private->request = NULL;
+ memcpy(&request->irb, irb, sizeof(*irb));
+ stsch(sch->schid, &sch->schib);
+ complete(&request->completion);
+ put_device(&sch->dev);
+}
+
+static int chsc_subchannel_probe(struct subchannel *sch)
+{
+ struct chsc_private *private;
+ int ret;
+
+ CHSC_MSG(6, "Detected chsc subchannel 0.%x.%04x\n",
+ sch->schid.ssid, sch->schid.sch_no);
+ sch->isc = CHSC_SCH_ISC;
+ private = kzalloc(sizeof(*private), GFP_KERNEL);
+ if (!private)
+ return -ENOMEM;
+ ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
+ if (ret) {
+ CHSC_MSG(0, "Failed to enable 0.%x.%04x: %d\n",
+ sch->schid.ssid, sch->schid.sch_no, ret);
+ kfree(private);
+ } else {
+ sch->private = private;
+ if (sch->dev.uevent_suppress) {
+ sch->dev.uevent_suppress = 0;
+ kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
+ }
+ }
+ return ret;
+}
+
+static int chsc_subchannel_remove(struct subchannel *sch)
+{
+ struct chsc_private *private;
+
+ cio_disable_subchannel(sch);
+ private = sch->private;
+ sch->private = NULL;
+ if (private->request) {
+ complete(&private->request->completion);
+ put_device(&sch->dev);
+ }
+ kfree(private);
+ return 0;
+}
+
+static void chsc_subchannel_shutdown(struct subchannel *sch)
+{
+ cio_disable_subchannel(sch);
+}
+
+static struct css_device_id chsc_subchannel_ids[] = {
+ { .match_flags = 0x1, .type =SUBCHANNEL_TYPE_CHSC, },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(css, chsc_subchannel_ids);
+
+static struct css_driver chsc_subchannel_driver = {
+ .owner = THIS_MODULE,
+ .subchannel_type = chsc_subchannel_ids,
+ .irq = chsc_subchannel_irq,
+ .probe = chsc_subchannel_probe,
+ .remove = chsc_subchannel_remove,
+ .shutdown = chsc_subchannel_shutdown,
+ .name = "chsc_subchannel",
+};
+
+static int __init chsc_init_dbfs(void)
+{
+ chsc_debug_msg_id = debug_register("chsc_msg", 16, 1,
+ 16 * sizeof(long));
+ if (!chsc_debug_msg_id)
+ goto out;
+ debug_register_view(chsc_debug_msg_id, &debug_sprintf_view);
+ debug_set_level(chsc_debug_msg_id, 2);
+ chsc_debug_log_id = debug_register("chsc_log", 16, 1, 16);
+ if (!chsc_debug_log_id)
+ goto out;
+ debug_register_view(chsc_debug_log_id, &debug_hex_ascii_view);
+ debug_set_level(chsc_debug_log_id, 2);
+ return 0;
+out:
+ if (chsc_debug_msg_id)
+ debug_unregister(chsc_debug_msg_id);
+ return -ENOMEM;
+}
+
+static void chsc_remove_dbfs(void)
+{
+ debug_unregister(chsc_debug_log_id);
+ debug_unregister(chsc_debug_msg_id);
+}
+
+static int __init chsc_init_sch_driver(void)
+{
+ return css_driver_register(&chsc_subchannel_driver);
+}
+
+static void chsc_cleanup_sch_driver(void)
+{
+ css_driver_unregister(&chsc_subchannel_driver);
+}
+
+static DEFINE_SPINLOCK(chsc_lock);
+
+static int chsc_subchannel_match_next_free(struct device *dev, void *data)
+{
+ struct subchannel *sch = to_subchannel(dev);
+
+ return sch->schib.pmcw.ena && !scsw_fctl(&sch->schib.scsw);
+}
+
+static struct subchannel *chsc_get_next_subchannel(struct subchannel *sch)
+{
+ struct device *dev;
+
+ dev = driver_find_device(&chsc_subchannel_driver.drv,
+ sch ? &sch->dev : NULL, NULL,
+ chsc_subchannel_match_next_free);
+ return dev ? to_subchannel(dev) : NULL;
+}
+
+/**
+ * chsc_async() - try to start a chsc request asynchronously
+ * @chsc_area: request to be started
+ * @request: request structure to associate
+ *
+ * Tries to start a chsc request on one of the existing chsc subchannels.
+ * Returns:
+ * %0 if the request was performed synchronously
+ * %-EINPROGRESS if the request was successfully started
+ * %-EBUSY if all chsc subchannels are busy
+ * %-ENODEV if no chsc subchannels are available
+ * Context:
+ * interrupts disabled, chsc_lock held
+ */
+static int chsc_async(struct chsc_async_area *chsc_area,
+ struct chsc_request *request)
+{
+ int cc;
+ struct chsc_private *private;
+ struct subchannel *sch = NULL;
+ int ret = -ENODEV;
+ char dbf[10];
+
+ chsc_area->header.key = PAGE_DEFAULT_KEY;
+ while ((sch = chsc_get_next_subchannel(sch))) {
+ spin_lock(sch->lock);
+ private = sch->private;
+ if (private->request) {
+ spin_unlock(sch->lock);
+ ret = -EBUSY;
+ continue;
+ }
+ chsc_area->header.sid = sch->schid;
+ CHSC_LOG(2, "schid");
+ CHSC_LOG_HEX(2, &sch->schid, sizeof(sch->schid));
+ cc = chsc(chsc_area);
+ sprintf(dbf, "cc:%d", cc);
+ CHSC_LOG(2, dbf);
+ switch (cc) {
+ case 0:
+ ret = 0;
+ break;
+ case 1:
+ sch->schib.scsw.cmd.fctl |= SCSW_FCTL_START_FUNC;
+ ret = -EINPROGRESS;
+ private->request = request;
+ break;
+ case 2:
+ ret = -EBUSY;
+ break;
+ default:
+ ret = -ENODEV;
+ }
+ spin_unlock(sch->lock);
+ CHSC_MSG(2, "chsc on 0.%x.%04x returned cc=%d\n",
+ sch->schid.ssid, sch->schid.sch_no, cc);
+ if (ret == -EINPROGRESS)
+ return -EINPROGRESS;
+ put_device(&sch->dev);
+ if (ret == 0)
+ return 0;
+ }
+ return ret;
+}
+
+static void chsc_log_command(struct chsc_async_area *chsc_area)
+{
+ char dbf[10];
+
+ sprintf(dbf, "CHSC:%x", chsc_area->header.code);
+ CHSC_LOG(0, dbf);
+ CHSC_LOG_HEX(0, chsc_area, 32);
+}
+
+static int chsc_examine_irb(struct chsc_request *request)
+{
+ int backed_up;
+
+ if (!(scsw_stctl(&request->irb.scsw) & SCSW_STCTL_STATUS_PEND))
+ return -EIO;
+ backed_up = scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHAIN_CHECK;
+ request->irb.scsw.cmd.cstat &= ~SCHN_STAT_CHAIN_CHECK;
+ if (scsw_cstat(&request->irb.scsw) == 0)
+ return 0;
+ if (!backed_up)
+ return 0;
+ if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROG_CHECK)
+ return -EIO;
+ if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROT_CHECK)
+ return -EPERM;
+ if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_DATA_CHK)
+ return -EAGAIN;
+ if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_CTRL_CHK)
+ return -EAGAIN;
+ return -EIO;
+}
+
+static int chsc_ioctl_start(void __user *user_area)
+{
+ struct chsc_request *request;
+ struct chsc_async_area *chsc_area;
+ int ret;
+ char dbf[10];
+
+ if (!css_general_characteristics.dynio)
+ /* It makes no sense to try. */
+ return -EOPNOTSUPP;
+ chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
+ if (!chsc_area)
+ return -ENOMEM;
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (!request) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ init_completion(&request->completion);
+ if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+ chsc_log_command(chsc_area);
+ spin_lock_irq(&chsc_lock);
+ ret = chsc_async(chsc_area, request);
+ spin_unlock_irq(&chsc_lock);
+ if (ret == -EINPROGRESS) {
+ wait_for_completion(&request->completion);
+ ret = chsc_examine_irb(request);
+ }
+ /* copy area back to user */
+ if (!ret)
+ if (copy_to_user(user_area, chsc_area, PAGE_SIZE))
+ ret = -EFAULT;
+out_free:
+ sprintf(dbf, "ret:%d", ret);
+ CHSC_LOG(0, dbf);
+ kfree(request);
+ free_page((unsigned long)chsc_area);
+ return ret;
+}
+
+static int chsc_ioctl_info_channel_path(void __user *user_cd)
+{
+ struct chsc_chp_cd *cd;
+ int ret, ccode;
+ struct {
+ struct chsc_header request;
+ u32 : 2;
+ u32 m : 1;
+ u32 : 1;
+ u32 fmt1 : 4;
+ u32 cssid : 8;
+ u32 : 8;
+ u32 first_chpid : 8;
+ u32 : 24;
+ u32 last_chpid : 8;
+ u32 : 32;
+ struct chsc_header response;
+ u8 data[PAGE_SIZE - 20];
+ } __attribute__ ((packed)) *scpcd_area;
+
+ scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!scpcd_area)
+ return -ENOMEM;
+ cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+ if (!cd) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ if (copy_from_user(cd, user_cd, sizeof(*cd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+ scpcd_area->request.length = 0x0010;
+ scpcd_area->request.code = 0x0028;
+ scpcd_area->m = cd->m;
+ scpcd_area->fmt1 = cd->fmt;
+ scpcd_area->cssid = cd->chpid.cssid;
+ scpcd_area->first_chpid = cd->chpid.id;
+ scpcd_area->last_chpid = cd->chpid.id;
+
+ ccode = chsc(scpcd_area);
+ if (ccode != 0) {
+ ret = -EIO;
+ goto out_free;
+ }
+ if (scpcd_area->response.code != 0x0001) {
+ ret = -EIO;
+ CHSC_MSG(0, "scpcd: response code=%x\n",
+ scpcd_area->response.code);
+ goto out_free;
+ }
+ memcpy(&cd->cpcb, &scpcd_area->response, scpcd_area->response.length);
+ if (copy_to_user(user_cd, cd, sizeof(*cd)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+out_free:
+ kfree(cd);
+ free_page((unsigned long)scpcd_area);
+ return ret;
+}
+
+static int chsc_ioctl_info_cu(void __user *user_cd)
+{
+ struct chsc_cu_cd *cd;
+ int ret, ccode;
+ struct {
+ struct chsc_header request;
+ u32 : 2;
+ u32 m : 1;
+ u32 : 1;
+ u32 fmt1 : 4;
+ u32 cssid : 8;
+ u32 : 8;
+ u32 first_cun : 8;
+ u32 : 24;
+ u32 last_cun : 8;
+ u32 : 32;
+ struct chsc_header response;
+ u8 data[PAGE_SIZE - 20];
+ } __attribute__ ((packed)) *scucd_area;
+
+ scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!scucd_area)
+ return -ENOMEM;
+ cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+ if (!cd) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ if (copy_from_user(cd, user_cd, sizeof(*cd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+ scucd_area->request.length = 0x0010;
+ scucd_area->request.code = 0x0028;
+ scucd_area->m = cd->m;
+ scucd_area->fmt1 = cd->fmt;
+ scucd_area->cssid = cd->cssid;
+ scucd_area->first_cun = cd->cun;
+ scucd_area->last_cun = cd->cun;
+
+ ccode = chsc(scucd_area);
+ if (ccode != 0) {
+ ret = -EIO;
+ goto out_free;
+ }
+ if (scucd_area->response.code != 0x0001) {
+ ret = -EIO;
+ CHSC_MSG(0, "scucd: response code=%x\n",
+ scucd_area->response.code);
+ goto out_free;
+ }
+ memcpy(&cd->cucb, &scucd_area->response, scucd_area->response.length);
+ if (copy_to_user(user_cd, cd, sizeof(*cd)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+out_free:
+ kfree(cd);
+ free_page((unsigned long)scucd_area);
+ return ret;
+}
+
+static int chsc_ioctl_info_sch_cu(void __user *user_cud)
+{
+ struct chsc_sch_cud *cud;
+ int ret, ccode;
+ struct {
+ struct chsc_header request;
+ u32 : 2;
+ u32 m : 1;
+ u32 : 5;
+ u32 fmt1 : 4;
+ u32 : 2;
+ u32 ssid : 2;
+ u32 first_sch : 16;
+ u32 : 8;
+ u32 cssid : 8;
+ u32 last_sch : 16;
+ u32 : 32;
+ struct chsc_header response;
+ u8 data[PAGE_SIZE - 20];
+ } __attribute__ ((packed)) *sscud_area;
+
+ sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sscud_area)
+ return -ENOMEM;
+ cud = kzalloc(sizeof(*cud), GFP_KERNEL);
+ if (!cud) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ if (copy_from_user(cud, user_cud, sizeof(*cud))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+ sscud_area->request.length = 0x0010;
+ sscud_area->request.code = 0x0006;
+ sscud_area->m = cud->schid.m;
+ sscud_area->fmt1 = cud->fmt;
+ sscud_area->ssid = cud->schid.ssid;
+ sscud_area->first_sch = cud->schid.sch_no;
+ sscud_area->cssid = cud->schid.cssid;
+ sscud_area->last_sch = cud->schid.sch_no;
+
+ ccode = chsc(sscud_area);
+ if (ccode != 0) {
+ ret = -EIO;
+ goto out_free;
+ }
+ if (sscud_area->response.code != 0x0001) {
+ ret = -EIO;
+ CHSC_MSG(0, "sscud: response code=%x\n",
+ sscud_area->response.code);
+ goto out_free;
+ }
+ memcpy(&cud->scub, &sscud_area->response, sscud_area->response.length);
+ if (copy_to_user(user_cud, cud, sizeof(*cud)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+out_free:
+ kfree(cud);
+ free_page((unsigned long)sscud_area);
+ return ret;
+}
+
+static int chsc_ioctl_conf_info(void __user *user_ci)
+{
+ struct chsc_conf_info *ci;
+ int ret, ccode;
+ struct {
+ struct chsc_header request;
+ u32 : 2;
+ u32 m : 1;
+ u32 : 1;
+ u32 fmt1 : 4;
+ u32 cssid : 8;
+ u32 : 6;
+ u32 ssid : 2;
+ u32 : 8;
+ u64 : 64;
+ struct chsc_header response;
+ u8 data[PAGE_SIZE - 20];
+ } __attribute__ ((packed)) *sci_area;
+
+ sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sci_area)
+ return -ENOMEM;
+ ci = kzalloc(sizeof(*ci), GFP_KERNEL);
+ if (!ci) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ if (copy_from_user(ci, user_ci, sizeof(*ci))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+ sci_area->request.length = 0x0010;
+ sci_area->request.code = 0x0012;
+ sci_area->m = ci->id.m;
+ sci_area->fmt1 = ci->fmt;
+ sci_area->cssid = ci->id.cssid;
+ sci_area->ssid = ci->id.ssid;
+
+ ccode = chsc(sci_area);
+ if (ccode != 0) {
+ ret = -EIO;
+ goto out_free;
+ }
+ if (sci_area->response.code != 0x0001) {
+ ret = -EIO;
+ CHSC_MSG(0, "sci: response code=%x\n",
+ sci_area->response.code);
+ goto out_free;
+ }
+ memcpy(&ci->scid, &sci_area->response, sci_area->response.length);
+ if (copy_to_user(user_ci, ci, sizeof(*ci)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+out_free:
+ kfree(ci);
+ free_page((unsigned long)sci_area);
+ return ret;
+}
+
+static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
+{
+ struct chsc_comp_list *ccl;
+ int ret, ccode;
+ struct {
+ struct chsc_header request;
+ u32 ctype : 8;
+ u32 : 4;
+ u32 fmt : 4;
+ u32 : 16;
+ u64 : 64;
+ u32 list_parm[2];
+ u64 : 64;
+ struct chsc_header response;
+ u8 data[PAGE_SIZE - 36];
+ } __attribute__ ((packed)) *sccl_area;
+ struct {
+ u32 m : 1;
+ u32 : 31;
+ u32 cssid : 8;
+ u32 : 16;
+ u32 chpid : 8;
+ } __attribute__ ((packed)) *chpid_parm;
+ struct {
+ u32 f_cssid : 8;
+ u32 l_cssid : 8;
+ u32 : 16;
+ u32 res;
+ } __attribute__ ((packed)) *cssids_parm;
+
+ sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccl_area)
+ return -ENOMEM;
+ ccl = kzalloc(sizeof(*ccl), GFP_KERNEL);
+ if (!ccl) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ if (copy_from_user(ccl, user_ccl, sizeof(*ccl))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+ sccl_area->request.length = 0x0020;
+ sccl_area->request.code = 0x0030;
+ sccl_area->fmt = ccl->req.fmt;
+ sccl_area->ctype = ccl->req.ctype;
+ switch (sccl_area->ctype) {
+ case CCL_CU_ON_CHP:
+ case CCL_IOP_CHP:
+ chpid_parm = (void *)&sccl_area->list_parm;
+ chpid_parm->m = ccl->req.chpid.m;
+ chpid_parm->cssid = ccl->req.chpid.chp.cssid;
+ chpid_parm->chpid = ccl->req.chpid.chp.id;
+ break;
+ case CCL_CSS_IMG:
+ case CCL_CSS_IMG_CONF_CHAR:
+ cssids_parm = (void *)&sccl_area->list_parm;
+ cssids_parm->f_cssid = ccl->req.cssids.f_cssid;
+ cssids_parm->l_cssid = ccl->req.cssids.l_cssid;
+ break;
+ }
+ ccode = chsc(sccl_area);
+ if (ccode != 0) {
+ ret = -EIO;
+ goto out_free;
+ }
+ if (sccl_area->response.code != 0x0001) {
+ ret = -EIO;
+ CHSC_MSG(0, "sccl: response code=%x\n",
+ sccl_area->response.code);
+ goto out_free;
+ }
+ memcpy(&ccl->sccl, &sccl_area->response, sccl_area->response.length);
+ if (copy_to_user(user_ccl, ccl, sizeof(*ccl)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+out_free:
+ kfree(ccl);
+ free_page((unsigned long)sccl_area);
+ return ret;
+}
+
+static int chsc_ioctl_chpd(void __user *user_chpd)
+{
+ struct chsc_cpd_info *chpd;
+ int ret;
+
+ chpd = kzalloc(sizeof(*chpd), GFP_KERNEL);
+ if (!chpd)
+ return -ENOMEM;
+ if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+ ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt,
+ chpd->rfmt, chpd->c, chpd->m,
+ &chpd->chpdb);
+ if (ret)
+ goto out_free;
+ if (copy_to_user(user_chpd, chpd, sizeof(*chpd)))
+ ret = -EFAULT;
+out_free:
+ kfree(chpd);
+ return ret;
+}
+
+static int chsc_ioctl_dcal(void __user *user_dcal)
+{
+ struct chsc_dcal *dcal;
+ int ret, ccode;
+ struct {
+ struct chsc_header request;
+ u32 atype : 8;
+ u32 : 4;
+ u32 fmt : 4;
+ u32 : 16;
+ u32 res0[2];
+ u32 list_parm[2];
+ u32 res1[2];
+ struct chsc_header response;
+ u8 data[PAGE_SIZE - 36];
+ } __attribute__ ((packed)) *sdcal_area;
+
+ sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sdcal_area)
+ return -ENOMEM;
+ dcal = kzalloc(sizeof(*dcal), GFP_KERNEL);
+ if (!dcal) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ if (copy_from_user(dcal, user_dcal, sizeof(*dcal))) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+ sdcal_area->request.length = 0x0020;
+ sdcal_area->request.code = 0x0034;
+ sdcal_area->atype = dcal->req.atype;
+ sdcal_area->fmt = dcal->req.fmt;
+ memcpy(&sdcal_area->list_parm, &dcal->req.list_parm,
+ sizeof(sdcal_area->list_parm));
+
+ ccode = chsc(sdcal_area);
+ if (ccode != 0) {
+ ret = -EIO;
+ goto out_free;
+ }
+ if (sdcal_area->response.code != 0x0001) {
+ ret = -EIO;
+ CHSC_MSG(0, "sdcal: response code=%x\n",
+ sdcal_area->response.code);
+ goto out_free;
+ }
+ memcpy(&dcal->sdcal, &sdcal_area->response,
+ sdcal_area->response.length);
+ if (copy_to_user(user_dcal, dcal, sizeof(*dcal)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+out_free:
+ kfree(dcal);
+ free_page((unsigned long)sdcal_area);
+ return ret;
+}
+
+static long chsc_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ CHSC_MSG(2, "chsc_ioctl called, cmd=%x\n", cmd);
+ switch (cmd) {
+ case CHSC_START:
+ return chsc_ioctl_start((void __user *)arg);
+ case CHSC_INFO_CHANNEL_PATH:
+ return chsc_ioctl_info_channel_path((void __user *)arg);
+ case CHSC_INFO_CU:
+ return chsc_ioctl_info_cu((void __user *)arg);
+ case CHSC_INFO_SCH_CU:
+ return chsc_ioctl_info_sch_cu((void __user *)arg);
+ case CHSC_INFO_CI:
+ return chsc_ioctl_conf_info((void __user *)arg);
+ case CHSC_INFO_CCL:
+ return chsc_ioctl_conf_comp_list((void __user *)arg);
+ case CHSC_INFO_CPD:
+ return chsc_ioctl_chpd((void __user *)arg);
+ case CHSC_INFO_DCAL:
+ return chsc_ioctl_dcal((void __user *)arg);
+ default: /* unknown ioctl number */
+ return -ENOIOCTLCMD;
+ }
+}
+
+static const struct file_operations chsc_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = chsc_ioctl,
+ .compat_ioctl = chsc_ioctl,
+};
+
+static struct miscdevice chsc_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "chsc",
+ .fops = &chsc_fops,
+};
+
+static int __init chsc_misc_init(void)
+{
+ return misc_register(&chsc_misc_device);
+}
+
+static void chsc_misc_cleanup(void)
+{
+ misc_deregister(&chsc_misc_device);
+}
+
+static int __init chsc_sch_init(void)
+{
+ int ret;
+
+ ret = chsc_init_dbfs();
+ if (ret)
+ return ret;
+ isc_register(CHSC_SCH_ISC);
+ ret = chsc_init_sch_driver();
+ if (ret)
+ goto out_dbf;
+ ret = chsc_misc_init();
+ if (ret)
+ goto out_driver;
+ return ret;
+out_driver:
+ chsc_cleanup_sch_driver();
+out_dbf:
+ isc_unregister(CHSC_SCH_ISC);
+ chsc_remove_dbfs();
+ return ret;
+}
+
+static void __exit chsc_sch_exit(void)
+{
+ chsc_misc_cleanup();
+ chsc_cleanup_sch_driver();
+ isc_unregister(CHSC_SCH_ISC);
+ chsc_remove_dbfs();
+}
+
+module_init(chsc_sch_init);
+module_exit(chsc_sch_exit);
diff --git a/drivers/s390/cio/chsc_sch.h b/drivers/s390/cio/chsc_sch.h
new file mode 100644
index 00000000000..589ebfad6aa
--- /dev/null
+++ b/drivers/s390/cio/chsc_sch.h
@@ -0,0 +1,13 @@
+#ifndef _CHSC_SCH_H
+#define _CHSC_SCH_H
+
+struct chsc_request {
+ struct completion completion;
+ struct irb irb;
+};
+
+struct chsc_private {
+ struct chsc_request *request;
+};
+
+#endif
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index b32d7eb3d81..3db2c386546 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -2,7 +2,7 @@
* drivers/s390/cio/cio.c
* S/390 common I/O routines -- low level i/o calls
*
- * Copyright (C) IBM Corp. 1999,2006
+ * Copyright IBM Corp. 1999,2008
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
@@ -24,7 +24,9 @@
#include <asm/ipl.h>
#include <asm/chpid.h>
#include <asm/airq.h>
+#include <asm/isc.h>
#include <asm/cpu.h>
+#include <asm/fcx.h>
#include "cio.h"
#include "css.h"
#include "chsc.h"
@@ -72,7 +74,6 @@ out_unregister:
debug_unregister(cio_debug_trace_id);
if (cio_debug_crw_id)
debug_unregister(cio_debug_crw_id);
- printk(KERN_WARNING"cio: could not initialize debugging\n");
return -1;
}
@@ -113,6 +114,7 @@ cio_tpi(void)
struct tpi_info *tpi_info;
struct subchannel *sch;
struct irb *irb;
+ int irq_context;
tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID;
if (tpi (NULL) != 1)
@@ -125,15 +127,18 @@ cio_tpi(void)
sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
if (!sch)
return 1;
- local_bh_disable();
+ irq_context = in_interrupt();
+ if (!irq_context)
+ local_bh_disable();
irq_enter ();
spin_lock(sch->lock);
- memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw));
+ memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
if (sch->driver && sch->driver->irq)
sch->driver->irq(sch);
spin_unlock(sch->lock);
irq_exit ();
- _local_bh_enable();
+ if (!irq_context)
+ _local_bh_enable();
return 1;
}
@@ -152,7 +157,7 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
CIO_MSG_EVENT(2, "cio_start: 'not oper' status for "
"subchannel 0.%x.%04x!\n", sch->schid.ssid,
sch->schid.sch_no);
- sprintf(dbf_text, "no%s", sch->dev.bus_id);
+ sprintf(dbf_text, "no%s", dev_name(&sch->dev));
CIO_TRACE_EVENT(0, dbf_text);
CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
@@ -167,30 +172,31 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
{
char dbf_txt[15];
int ccode;
- struct orb *orb;
+ union orb *orb;
CIO_TRACE_EVENT(4, "stIO");
- CIO_TRACE_EVENT(4, sch->dev.bus_id);
+ CIO_TRACE_EVENT(4, dev_name(&sch->dev));
orb = &to_io_private(sch)->orb;
+ memset(orb, 0, sizeof(union orb));
/* sch is always under 2G. */
- orb->intparm = (u32)(addr_t)sch;
- orb->fmt = 1;
+ orb->cmd.intparm = (u32)(addr_t)sch;
+ orb->cmd.fmt = 1;
- orb->pfch = sch->options.prefetch == 0;
- orb->spnd = sch->options.suspend;
- orb->ssic = sch->options.suspend && sch->options.inter;
- orb->lpm = (lpm != 0) ? lpm : sch->lpm;
+ orb->cmd.pfch = sch->options.prefetch == 0;
+ orb->cmd.spnd = sch->options.suspend;
+ orb->cmd.ssic = sch->options.suspend && sch->options.inter;
+ orb->cmd.lpm = (lpm != 0) ? lpm : sch->lpm;
#ifdef CONFIG_64BIT
/*
* for 64 bit we always support 64 bit IDAWs with 4k page size only
*/
- orb->c64 = 1;
- orb->i2k = 0;
+ orb->cmd.c64 = 1;
+ orb->cmd.i2k = 0;
#endif
- orb->key = key >> 4;
+ orb->cmd.key = key >> 4;
/* issue "Start Subchannel" */
- orb->cpa = (__u32) __pa(cpa);
+ orb->cmd.cpa = (__u32) __pa(cpa);
ccode = ssch(sch->schid, orb);
/* process condition code */
@@ -202,13 +208,15 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
/*
* initialize device status information
*/
- sch->schib.scsw.actl |= SCSW_ACTL_START_PEND;
+ sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
return 0;
case 1: /* status pending */
case 2: /* busy */
return -EBUSY;
- default: /* device/path not operational */
+ case 3: /* device/path not operational */
return cio_start_handle_notoper(sch, lpm);
+ default:
+ return ccode;
}
}
@@ -228,7 +236,7 @@ cio_resume (struct subchannel *sch)
int ccode;
CIO_TRACE_EVENT (4, "resIO");
- CIO_TRACE_EVENT (4, sch->dev.bus_id);
+ CIO_TRACE_EVENT(4, dev_name(&sch->dev));
ccode = rsch (sch->schid);
@@ -237,7 +245,7 @@ cio_resume (struct subchannel *sch)
switch (ccode) {
case 0:
- sch->schib.scsw.actl |= SCSW_ACTL_RESUME_PEND;
+ sch->schib.scsw.cmd.actl |= SCSW_ACTL_RESUME_PEND;
return 0;
case 1:
return -EBUSY;
@@ -265,7 +273,7 @@ cio_halt(struct subchannel *sch)
return -ENODEV;
CIO_TRACE_EVENT (2, "haltIO");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
/*
* Issue "Halt subchannel" and process condition code
@@ -277,7 +285,7 @@ cio_halt(struct subchannel *sch)
switch (ccode) {
case 0:
- sch->schib.scsw.actl |= SCSW_ACTL_HALT_PEND;
+ sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND;
return 0;
case 1: /* status pending */
case 2: /* busy */
@@ -300,7 +308,7 @@ cio_clear(struct subchannel *sch)
return -ENODEV;
CIO_TRACE_EVENT (2, "clearIO");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
/*
* Issue "Clear subchannel" and process condition code
@@ -312,7 +320,7 @@ cio_clear(struct subchannel *sch)
switch (ccode) {
case 0:
- sch->schib.scsw.actl |= SCSW_ACTL_CLEAR_PEND;
+ sch->schib.scsw.cmd.actl |= SCSW_ACTL_CLEAR_PEND;
return 0;
default: /* device not operational */
return -ENODEV;
@@ -336,7 +344,7 @@ cio_cancel (struct subchannel *sch)
return -ENODEV;
CIO_TRACE_EVENT (2, "cancelIO");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
ccode = xsch (sch->schid);
@@ -387,8 +395,10 @@ cio_modify (struct subchannel *sch)
return ret;
}
-/*
- * Enable subchannel.
+/**
+ * cio_enable_subchannel - enable a subchannel.
+ * @sch: subchannel to be enabled
+ * @intparm: interruption parameter to set
*/
int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
{
@@ -398,7 +408,7 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
int ret;
CIO_TRACE_EVENT (2, "ensch");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
if (sch_is_pseudo_sch(sch))
return -EINVAL;
@@ -434,12 +444,13 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
CIO_TRACE_EVENT (2, dbf_txt);
return ret;
}
+EXPORT_SYMBOL_GPL(cio_enable_subchannel);
-/*
- * Disable subchannel.
+/**
+ * cio_disable_subchannel - disable a subchannel.
+ * @sch: subchannel to disable
*/
-int
-cio_disable_subchannel (struct subchannel *sch)
+int cio_disable_subchannel(struct subchannel *sch)
{
char dbf_txt[15];
int ccode;
@@ -447,7 +458,7 @@ cio_disable_subchannel (struct subchannel *sch)
int ret;
CIO_TRACE_EVENT (2, "dissch");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
if (sch_is_pseudo_sch(sch))
return 0;
@@ -455,7 +466,7 @@ cio_disable_subchannel (struct subchannel *sch)
if (ccode == 3) /* Not operational. */
return -ENODEV;
- if (sch->schib.scsw.actl != 0)
+ if (scsw_actl(&sch->schib.scsw) != 0)
/*
* the disable function must not be called while there are
* requests pending for completion !
@@ -484,6 +495,7 @@ cio_disable_subchannel (struct subchannel *sch)
CIO_TRACE_EVENT (2, dbf_txt);
return ret;
}
+EXPORT_SYMBOL_GPL(cio_disable_subchannel);
int cio_create_sch_lock(struct subchannel *sch)
{
@@ -494,27 +506,61 @@ int cio_create_sch_lock(struct subchannel *sch)
return 0;
}
-/*
- * cio_validate_subchannel()
+static int cio_check_devno_blacklisted(struct subchannel *sch)
+{
+ if (is_blacklisted(sch->schid.ssid, sch->schib.pmcw.dev)) {
+ /*
+ * This device must not be known to Linux. So we simply
+ * say that there is no device and return ENODEV.
+ */
+ CIO_MSG_EVENT(6, "Blacklisted device detected "
+ "at devno %04X, subchannel set %x\n",
+ sch->schib.pmcw.dev, sch->schid.ssid);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int cio_validate_io_subchannel(struct subchannel *sch)
+{
+ /* Initialization for io subchannels. */
+ if (!css_sch_is_valid(&sch->schib))
+ return -ENODEV;
+
+ /* Devno is valid. */
+ return cio_check_devno_blacklisted(sch);
+}
+
+static int cio_validate_msg_subchannel(struct subchannel *sch)
+{
+ /* Initialization for message subchannels. */
+ if (!css_sch_is_valid(&sch->schib))
+ return -ENODEV;
+
+ /* Devno is valid. */
+ return cio_check_devno_blacklisted(sch);
+}
+
+/**
+ * cio_validate_subchannel - basic validation of subchannel
+ * @sch: subchannel structure to be filled out
+ * @schid: subchannel id
*
* Find out subchannel type and initialize struct subchannel.
* Return codes:
- * SUBCHANNEL_TYPE_IO for a normal io subchannel
- * SUBCHANNEL_TYPE_CHSC for a chsc subchannel
- * SUBCHANNEL_TYPE_MESSAGE for a messaging subchannel
- * SUBCHANNEL_TYPE_ADM for a adm(?) subchannel
+ * 0 on success
* -ENXIO for non-defined subchannels
- * -ENODEV for subchannels with invalid device number or blacklisted devices
+ * -ENODEV for invalid subchannels or blacklisted devices
+ * -EIO for subchannels in an invalid subchannel set
*/
-int
-cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
+int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid)
{
char dbf_txt[15];
int ccode;
int err;
- sprintf (dbf_txt, "valsch%x", schid.sch_no);
- CIO_TRACE_EVENT (4, dbf_txt);
+ sprintf(dbf_txt, "valsch%x", schid.sch_no);
+ CIO_TRACE_EVENT(4, dbf_txt);
/* Nuke all fields. */
memset(sch, 0, sizeof(struct subchannel));
@@ -529,8 +575,10 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
}
mutex_init(&sch->reg_mutex);
/* Set a name for the subchannel */
- snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid,
- schid.sch_no);
+ if (cio_is_console(schid))
+ sch->dev.init_name = cio_get_console_sch_name(schid);
+ else
+ dev_set_name(&sch->dev, "0.%x.%04x", schid.ssid, schid.sch_no);
/*
* The first subchannel that is not-operational (ccode==3)
@@ -546,67 +594,21 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
/* Copy subchannel type from path management control word. */
sch->st = sch->schib.pmcw.st;
- /*
- * ... just being curious we check for non I/O subchannels
- */
- if (sch->st != 0) {
- CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports "
- "non-I/O subchannel type %04X\n",
- sch->schid.ssid, sch->schid.sch_no, sch->st);
- /* We stop here for non-io subchannels. */
- err = sch->st;
- goto out;
- }
-
- /* Initialization for io subchannels. */
- if (!css_sch_is_valid(&sch->schib)) {
- err = -ENODEV;
- goto out;
+ switch (sch->st) {
+ case SUBCHANNEL_TYPE_IO:
+ err = cio_validate_io_subchannel(sch);
+ break;
+ case SUBCHANNEL_TYPE_MSG:
+ err = cio_validate_msg_subchannel(sch);
+ break;
+ default:
+ err = 0;
}
-
- /* Devno is valid. */
- if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) {
- /*
- * This device must not be known to Linux. So we simply
- * say that there is no device and return ENODEV.
- */
- CIO_MSG_EVENT(6, "Blacklisted device detected "
- "at devno %04X, subchannel set %x\n",
- sch->schib.pmcw.dev, sch->schid.ssid);
- err = -ENODEV;
+ if (err)
goto out;
- }
- if (cio_is_console(sch->schid)) {
- sch->opm = 0xff;
- sch->isc = 1;
- } else {
- sch->opm = chp_get_sch_opm(sch);
- sch->isc = 3;
- }
- sch->lpm = sch->schib.pmcw.pam & sch->opm;
-
- CIO_MSG_EVENT(6, "Detected device %04x on subchannel 0.%x.%04X "
- "- PIM = %02X, PAM = %02X, POM = %02X\n",
- sch->schib.pmcw.dev, sch->schid.ssid,
- sch->schid.sch_no, sch->schib.pmcw.pim,
- sch->schib.pmcw.pam, sch->schib.pmcw.pom);
- /*
- * We now have to initially ...
- * ... enable "concurrent sense"
- * ... enable "multipath mode" if more than one
- * CHPID is available. This is done regardless
- * whether multiple paths are available for us.
- */
- sch->schib.pmcw.csense = 1; /* concurrent sense */
- sch->schib.pmcw.ena = 0;
- if ((sch->lpm & (sch->lpm - 1)) != 0)
- sch->schib.pmcw.mp = 1; /* multipath mode */
- /* clean up possible residual cmf stuff */
- sch->schib.pmcw.mme = 0;
- sch->schib.pmcw.mbfc = 0;
- sch->schib.pmcw.mbi = 0;
- sch->schib.mba = 0;
+ CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports subchannel type %04X\n",
+ sch->schid.ssid, sch->schid.sch_no, sch->st);
return 0;
out:
if (!cio_is_console(schid))
@@ -647,7 +649,7 @@ do_IRQ (struct pt_regs *regs)
*/
if (tpi_info->adapter_IO == 1 &&
tpi_info->int_type == IO_INTERRUPT_TYPE) {
- do_adapter_IO();
+ do_adapter_IO(tpi_info->isc);
continue;
}
sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
@@ -681,6 +683,7 @@ do_IRQ (struct pt_regs *regs)
#ifdef CONFIG_CCW_CONSOLE
static struct subchannel console_subchannel;
+static char console_sch_name[10] = "0.x.xxxx";
static struct io_subchannel_private console_priv;
static int console_subchannel_in_use;
@@ -706,9 +709,9 @@ void wait_cons_dev(void)
if (!console_subchannel_in_use)
return;
- /* disable all but isc 1 (console device) */
+ /* disable all but the console isc */
__ctl_store (save_cr6, 6, 6);
- cr6 = 0x40000000;
+ cr6 = 1UL << (31 - CONSOLE_ISC);
__ctl_load (cr6, 6, 6);
do {
@@ -716,7 +719,7 @@ void wait_cons_dev(void)
if (!cio_tpi())
cpu_relax();
spin_lock(console_subchannel.lock);
- } while (console_subchannel.schib.scsw.actl != 0);
+ } while (console_subchannel.schib.scsw.cmd.actl != 0);
/*
* restore previous isc value
*/
@@ -761,7 +764,6 @@ cio_get_console_sch_no(void)
/* unlike in 2.4, we cannot autoprobe here, since
* the channel subsystem is not fully initialized.
* With some luck, the HWC console can take over */
- printk(KERN_WARNING "cio: No ccw console found!\n");
return -1;
}
return console_irq;
@@ -778,6 +780,7 @@ cio_probe_console(void)
sch_no = cio_get_console_sch_no();
if (sch_no == -1) {
console_subchannel_in_use = 0;
+ printk(KERN_WARNING "cio: No ccw console found!\n");
return ERR_PTR(-ENODEV);
}
memset(&console_subchannel, 0, sizeof(struct subchannel));
@@ -790,15 +793,15 @@ cio_probe_console(void)
}
/*
- * enable console I/O-interrupt subclass 1
+ * enable console I/O-interrupt subclass
*/
- ctl_set_bit(6, 30);
- console_subchannel.isc = 1;
- console_subchannel.schib.pmcw.isc = 1;
+ isc_register(CONSOLE_ISC);
+ console_subchannel.schib.pmcw.isc = CONSOLE_ISC;
console_subchannel.schib.pmcw.intparm =
(u32)(addr_t)&console_subchannel;
ret = cio_modify(&console_subchannel);
if (ret) {
+ isc_unregister(CONSOLE_ISC);
console_subchannel_in_use = 0;
return ERR_PTR(ret);
}
@@ -810,7 +813,7 @@ cio_release_console(void)
{
console_subchannel.schib.pmcw.intparm = 0;
cio_modify(&console_subchannel);
- ctl_clear_bit(6, 24);
+ isc_unregister(CONSOLE_ISC);
console_subchannel_in_use = 0;
}
@@ -831,6 +834,12 @@ cio_get_console_subchannel(void)
return &console_subchannel;
}
+const char *cio_get_console_sch_name(struct subchannel_id schid)
+{
+ snprintf(console_sch_name, 10, "0.%x.%04x", schid.ssid, schid.sch_no);
+ return (const char *)console_sch_name;
+}
+
#endif
static int
__disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
@@ -850,21 +859,8 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
return -EBUSY; /* uhm... */
}
-/* we can't use the normal udelay here, since it enables external interrupts */
-
-static void udelay_reset(unsigned long usecs)
-{
- uint64_t start_cc, end_cc;
-
- asm volatile ("STCK %0" : "=m" (start_cc));
- do {
- cpu_relax();
- asm volatile ("STCK %0" : "=m" (end_cc));
- } while (((end_cc - start_cc)/4096) < usecs);
-}
-
static int
-__clear_subchannel_easy(struct subchannel_id schid)
+__clear_io_subchannel_easy(struct subchannel_id schid)
{
int retry;
@@ -878,11 +874,17 @@ __clear_subchannel_easy(struct subchannel_id schid)
if (schid_equal(&ti.schid, &schid))
return 0;
}
- udelay_reset(100);
+ udelay_simple(100);
}
return -EBUSY;
}
+static void __clear_chsc_subchannel_easy(void)
+{
+ /* It seems we can only wait for a bit here :/ */
+ udelay_simple(100);
+}
+
static int pgm_check_occured;
static void cio_reset_pgm_check_handler(void)
@@ -890,7 +892,7 @@ static void cio_reset_pgm_check_handler(void)
pgm_check_occured = 1;
}
-static int stsch_reset(struct subchannel_id schid, volatile struct schib *addr)
+static int stsch_reset(struct subchannel_id schid, struct schib *addr)
{
int rc;
@@ -921,11 +923,22 @@ static int __shutdown_subchannel_easy(struct subchannel_id schid, void *data)
case -ENODEV:
break;
default: /* -EBUSY */
- if (__clear_subchannel_easy(schid))
- break; /* give up... */
+ switch (schib.pmcw.st) {
+ case SUBCHANNEL_TYPE_IO:
+ if (__clear_io_subchannel_easy(schid))
+ goto out; /* give up... */
+ break;
+ case SUBCHANNEL_TYPE_CHSC:
+ __clear_chsc_subchannel_easy();
+ break;
+ default:
+ /* No default clear strategy */
+ break;
+ }
stsch(schid, &schib);
__disable_subchannel_easy(schid, &schib);
}
+out:
return 0;
}
@@ -1068,3 +1081,61 @@ int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
iplinfo->is_qdio = schib.pmcw.qf;
return 0;
}
+
+/**
+ * cio_tm_start_key - perform start function
+ * @sch: subchannel on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @lpm: mask of paths to use
+ * @key: storage key to use for storage access
+ *
+ * Start the tcw on the given subchannel. Return zero on success, non-zero
+ * otherwise.
+ */
+int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key)
+{
+ int cc;
+ union orb *orb = &to_io_private(sch)->orb;
+
+ memset(orb, 0, sizeof(union orb));
+ orb->tm.intparm = (u32) (addr_t) sch;
+ orb->tm.key = key >> 4;
+ orb->tm.b = 1;
+ orb->tm.lpm = lpm ? lpm : sch->lpm;
+ orb->tm.tcw = (u32) (addr_t) tcw;
+ cc = ssch(sch->schid, orb);
+ switch (cc) {
+ case 0:
+ return 0;
+ case 1:
+ case 2:
+ return -EBUSY;
+ default:
+ return cio_start_handle_notoper(sch, lpm);
+ }
+}
+
+/**
+ * cio_tm_intrg - perform interrogate function
+ * @sch - subchannel on which to perform the interrogate function
+ *
+ * If the specified subchannel is running in transport-mode, perform the
+ * interrogate function. Return zero on success, non-zero otherwie.
+ */
+int cio_tm_intrg(struct subchannel *sch)
+{
+ int cc;
+
+ if (!to_io_private(sch)->orb.tm.b)
+ return -EINVAL;
+ cc = xsch(sch->schid);
+ switch (cc) {
+ case 0:
+ case 2:
+ return 0;
+ case 1:
+ return -EBUSY;
+ default:
+ return -ENODEV;
+ }
+}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 6e933aebe01..0fb24784e92 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -3,9 +3,12 @@
#include <linux/mutex.h>
#include <linux/device.h>
+#include <linux/mod_devicetable.h>
#include <asm/chpid.h>
+#include <asm/cio.h>
+#include <asm/fcx.h>
+#include <asm/schid.h>
#include "chsc.h"
-#include "schid.h"
/*
* path management control word
@@ -13,7 +16,7 @@
struct pmcw {
u32 intparm; /* interruption parameter */
u32 qf : 1; /* qdio facility */
- u32 res0 : 1; /* reserved zeros */
+ u32 w : 1;
u32 isc : 3; /* interruption sublass */
u32 res5 : 3; /* reserved zeros */
u32 ena : 1; /* enabled */
@@ -47,7 +50,7 @@ struct pmcw {
*/
struct schib {
struct pmcw pmcw; /* path management control word */
- struct scsw scsw; /* subchannel status word */
+ union scsw scsw; /* subchannel status word */
__u64 mba; /* measurement block address */
__u8 mda[4]; /* model dependent area */
} __attribute__ ((packed,aligned(4)));
@@ -99,8 +102,11 @@ extern int cio_set_options (struct subchannel *, int);
extern int cio_get_options (struct subchannel *);
extern int cio_modify (struct subchannel *);
+int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
+int cio_tm_intrg(struct subchannel *sch);
+
int cio_create_sch_lock(struct subchannel *);
-void do_adapter_IO(void);
+void do_adapter_IO(u8 isc);
void do_IRQ(struct pt_regs *);
/* Use with care. */
@@ -111,11 +117,15 @@ extern int cio_is_console(struct subchannel_id);
extern struct subchannel *cio_get_console_subchannel(void);
extern spinlock_t * cio_get_console_lock(void);
extern void *cio_get_console_priv(void);
+extern const char *cio_get_console_sch_name(struct subchannel_id schid);
+extern const char *cio_get_console_cdev_name(struct subchannel *sch);
#else
#define cio_is_console(schid) 0
#define cio_get_console_subchannel() NULL
#define cio_get_console_lock() NULL
#define cio_get_console_priv() NULL
+#define cio_get_console_sch_name(schid) NULL
+#define cio_get_console_cdev_name(sch) NULL
#endif
#endif
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 2808b6833b9..a90b28c0be5 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -341,12 +341,12 @@ static int cmf_copy_block(struct ccw_device *cdev)
if (stsch(sch->schid, &sch->schib))
return -ENODEV;
- if (sch->schib.scsw.fctl & SCSW_FCTL_START_FUNC) {
+ if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) {
/* Don't copy if a start function is in progress. */
- if ((!(sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED)) &&
- (sch->schib.scsw.actl &
+ if ((!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_SUSPENDED)) &&
+ (scsw_actl(&sch->schib.scsw) &
(SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) &&
- (!(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)))
+ (!(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_SEC_STATUS)))
return -EBUSY;
}
cmb_data = cdev->private->cmb;
@@ -612,9 +612,6 @@ static int alloc_cmb(struct ccw_device *cdev)
free_pages((unsigned long)mem, get_order(size));
} else if (!mem) {
/* no luck */
- printk(KERN_WARNING "cio: failed to allocate area "
- "for measuring %d subchannels\n",
- cmb_area.num_channels);
ret = -ENOMEM;
goto out;
} else {
@@ -1230,13 +1227,9 @@ static ssize_t cmb_enable_store(struct device *dev,
switch (val) {
case 0:
ret = disable_cmf(cdev);
- if (ret)
- dev_info(&cdev->dev, "disable_cmf failed (%d)\n", ret);
break;
case 1:
ret = enable_cmf(cdev);
- if (ret && ret != -EBUSY)
- dev_info(&cdev->dev, "enable_cmf failed (%d)\n", ret);
break;
}
@@ -1344,8 +1337,7 @@ static int __init init_cmf(void)
* to basic mode.
*/
if (format == CMF_AUTODETECT) {
- if (!css_characteristics_avail ||
- !css_general_characteristics.ext_mb) {
+ if (!css_general_characteristics.ext_mb) {
format = CMF_BASIC;
} else {
format = CMF_EXTENDED;
@@ -1365,8 +1357,6 @@ static int __init init_cmf(void)
cmbops = &cmbops_extended;
break;
default:
- printk(KERN_ERR "cio: Invalid format %d for channel "
- "measurement facility\n", format);
return 1;
}
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index a76956512b2..76bbb1e74c2 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -2,8 +2,7 @@
* drivers/s390/cio/css.c
* driver for channel subsystem
*
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
+ * Copyright IBM Corp. 2002,2008
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
*/
@@ -14,7 +13,9 @@
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/reboot.h>
+#include <asm/isc.h>
+#include "../s390mach.h"
#include "css.h"
#include "cio.h"
#include "cio_debug.h"
@@ -30,8 +31,6 @@ static int max_ssid = 0;
struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1];
-int css_characteristics_avail = 0;
-
int
for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
{
@@ -121,25 +120,6 @@ css_alloc_subchannel(struct subchannel_id schid)
kfree(sch);
return ERR_PTR(ret);
}
-
- if (sch->st != SUBCHANNEL_TYPE_IO) {
- /* For now we ignore all non-io subchannels. */
- kfree(sch);
- return ERR_PTR(-EINVAL);
- }
-
- /*
- * Set intparm to subchannel address.
- * This is fine even on 64bit since the subchannel is always located
- * under 2G.
- */
- sch->schib.pmcw.intparm = (u32)(addr_t)sch;
- ret = cio_modify(sch);
- if (ret) {
- kfree(sch->lock);
- kfree(sch);
- return ERR_PTR(ret);
- }
return sch;
}
@@ -177,12 +157,18 @@ static int css_sch_device_register(struct subchannel *sch)
return ret;
}
+/**
+ * css_sch_device_unregister - unregister a subchannel
+ * @sch: subchannel to be unregistered
+ */
void css_sch_device_unregister(struct subchannel *sch)
{
mutex_lock(&sch->reg_mutex);
- device_unregister(&sch->dev);
+ if (device_is_registered(&sch->dev))
+ device_unregister(&sch->dev);
mutex_unlock(&sch->reg_mutex);
}
+EXPORT_SYMBOL_GPL(css_sch_device_unregister);
static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
{
@@ -229,6 +215,41 @@ void css_update_ssd_info(struct subchannel *sch)
}
}
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct subchannel *sch = to_subchannel(dev);
+
+ return sprintf(buf, "%01x\n", sch->st);
+}
+
+static DEVICE_ATTR(type, 0444, type_show, NULL);
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct subchannel *sch = to_subchannel(dev);
+
+ return sprintf(buf, "css:t%01X\n", sch->st);
+}
+
+static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
+
+static struct attribute *subch_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_modalias.attr,
+ NULL,
+};
+
+static struct attribute_group subch_attr_group = {
+ .attrs = subch_attrs,
+};
+
+static struct attribute_group *default_subch_attr_groups[] = {
+ &subch_attr_group,
+ NULL,
+};
+
static int css_register_subchannel(struct subchannel *sch)
{
int ret;
@@ -237,16 +258,17 @@ static int css_register_subchannel(struct subchannel *sch)
sch->dev.parent = &channel_subsystems[0]->device;
sch->dev.bus = &css_bus_type;
sch->dev.release = &css_subchannel_release;
- sch->dev.groups = subch_attr_groups;
+ sch->dev.groups = default_subch_attr_groups;
/*
* We don't want to generate uevents for I/O subchannels that don't
* have a working ccw device behind them since they will be
* unregistered before they can be used anyway, so we delay the add
* uevent until after device recognition was successful.
+ * Note that we suppress the uevent for all subchannel types;
+ * the subchannel driver can decide itself when it wants to inform
+ * userspace of its existence.
*/
- if (!cio_is_console(sch->schid))
- /* Console is special, no need to suppress. */
- sch->dev.uevent_suppress = 1;
+ sch->dev.uevent_suppress = 1;
css_update_ssd_info(sch);
/* make it known to the system */
ret = css_sch_device_register(sch);
@@ -255,10 +277,19 @@ static int css_register_subchannel(struct subchannel *sch)
sch->schid.ssid, sch->schid.sch_no, ret);
return ret;
}
+ if (!sch->driver) {
+ /*
+ * No driver matched. Generate the uevent now so that
+ * a fitting driver module may be loaded based on the
+ * modalias.
+ */
+ sch->dev.uevent_suppress = 0;
+ kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
+ }
return ret;
}
-static int css_probe_device(struct subchannel_id schid)
+int css_probe_device(struct subchannel_id schid)
{
int ret;
struct subchannel *sch;
@@ -301,116 +332,12 @@ int css_sch_is_valid(struct schib *schib)
{
if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv)
return 0;
+ if ((schib->pmcw.st == SUBCHANNEL_TYPE_MSG) && !schib->pmcw.w)
+ return 0;
return 1;
}
EXPORT_SYMBOL_GPL(css_sch_is_valid);
-static int css_get_subchannel_status(struct subchannel *sch)
-{
- struct schib schib;
-
- if (stsch(sch->schid, &schib))
- return CIO_GONE;
- if (!css_sch_is_valid(&schib))
- return CIO_GONE;
- if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
- return CIO_REVALIDATE;
- if (!sch->lpm)
- return CIO_NO_PATH;
- return CIO_OPER;
-}
-
-static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
-{
- int event, ret, disc;
- unsigned long flags;
- enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
-
- spin_lock_irqsave(sch->lock, flags);
- disc = device_is_disconnected(sch);
- if (disc && slow) {
- /* Disconnected devices are evaluated directly only.*/
- spin_unlock_irqrestore(sch->lock, flags);
- return 0;
- }
- /* No interrupt after machine check - kill pending timers. */
- device_kill_pending_timer(sch);
- if (!disc && !slow) {
- /* Non-disconnected devices are evaluated on the slow path. */
- spin_unlock_irqrestore(sch->lock, flags);
- return -EAGAIN;
- }
- event = css_get_subchannel_status(sch);
- CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
- sch->schid.ssid, sch->schid.sch_no, event,
- disc ? "disconnected" : "normal",
- slow ? "slow" : "fast");
- /* Analyze subchannel status. */
- action = NONE;
- switch (event) {
- case CIO_NO_PATH:
- if (disc) {
- /* Check if paths have become available. */
- action = REPROBE;
- break;
- }
- /* fall through */
- case CIO_GONE:
- /* Prevent unwanted effects when opening lock. */
- cio_disable_subchannel(sch);
- device_set_disconnected(sch);
- /* Ask driver what to do with device. */
- action = UNREGISTER;
- if (sch->driver && sch->driver->notify) {
- spin_unlock_irqrestore(sch->lock, flags);
- ret = sch->driver->notify(sch, event);
- spin_lock_irqsave(sch->lock, flags);
- if (ret)
- action = NONE;
- }
- break;
- case CIO_REVALIDATE:
- /* Device will be removed, so no notify necessary. */
- if (disc)
- /* Reprobe because immediate unregister might block. */
- action = REPROBE;
- else
- action = UNREGISTER_PROBE;
- break;
- case CIO_OPER:
- if (disc)
- /* Get device operational again. */
- action = REPROBE;
- break;
- }
- /* Perform action. */
- ret = 0;
- switch (action) {
- case UNREGISTER:
- case UNREGISTER_PROBE:
- /* Unregister device (will use subchannel lock). */
- spin_unlock_irqrestore(sch->lock, flags);
- css_sch_device_unregister(sch);
- spin_lock_irqsave(sch->lock, flags);
-
- /* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
- break;
- case REPROBE:
- device_trigger_reprobe(sch);
- break;
- default:
- break;
- }
- spin_unlock_irqrestore(sch->lock, flags);
- /* Probe if necessary. */
- if (action == UNREGISTER_PROBE)
- ret = css_probe_device(sch->schid);
-
- return ret;
-}
-
static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
{
struct schib schib;
@@ -429,6 +356,21 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
return css_probe_device(schid);
}
+static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
+{
+ int ret = 0;
+
+ if (sch->driver) {
+ if (sch->driver->sch_event)
+ ret = sch->driver->sch_event(sch, slow);
+ else
+ dev_dbg(&sch->dev,
+ "Got subchannel machine check but "
+ "no sch_event handler provided.\n");
+ }
+ return ret;
+}
+
static void css_evaluate_subchannel(struct subchannel_id schid, int slow)
{
struct subchannel *sch;
@@ -535,7 +477,6 @@ void css_schedule_eval_all(void)
void css_wait_for_slow_path(void)
{
- flush_workqueue(ccw_device_notify_work);
flush_workqueue(slow_path_wq);
}
@@ -596,18 +537,29 @@ EXPORT_SYMBOL_GPL(css_schedule_reprobe);
/*
* Called from the machine check handler for subchannel report words.
*/
-void css_process_crw(int rsid1, int rsid2)
+static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
{
struct subchannel_id mchk_schid;
- CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n",
- rsid1, rsid2);
+ if (overflow) {
+ css_schedule_eval_all();
+ return;
+ }
+ CIO_CRW_EVENT(2, "CRW0 reports slct=%d, oflw=%d, "
+ "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+ crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
+ crw0->erc, crw0->rsid);
+ if (crw1)
+ CIO_CRW_EVENT(2, "CRW1 reports slct=%d, oflw=%d, "
+ "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+ crw1->slct, crw1->oflw, crw1->chn, crw1->rsc,
+ crw1->anc, crw1->erc, crw1->rsid);
init_subchannel_id(&mchk_schid);
- mchk_schid.sch_no = rsid1;
- if (rsid2 != 0)
- mchk_schid.ssid = (rsid2 >> 8) & 3;
+ mchk_schid.sch_no = crw0->rsid;
+ if (crw1)
+ mchk_schid.ssid = (crw1->rsid >> 8) & 3;
- /*
+ /*
* 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
* or gone.
@@ -658,7 +610,7 @@ __init_channel_subsystem(struct subchannel_id schid, void *data)
static void __init
css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
{
- if (css_characteristics_avail && css_general_characteristics.mcss) {
+ if (css_general_characteristics.mcss) {
css->global_pgid.pgid_high.ext_cssid.version = 0x80;
css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid;
} else {
@@ -681,6 +633,11 @@ channel_subsystem_release(struct device *dev)
css = to_css(dev);
mutex_destroy(&css->mutex);
+ if (css->pseudo_subchannel) {
+ /* Implies that it has been generated but never registered. */
+ css_subchannel_release(&css->pseudo_subchannel->dev);
+ css->pseudo_subchannel = NULL;
+ }
kfree(css);
}
@@ -741,7 +698,7 @@ static int __init setup_css(int nr)
return -ENOMEM;
css->pseudo_subchannel->dev.parent = &css->device;
css->pseudo_subchannel->dev.release = css_subchannel_release;
- sprintf(css->pseudo_subchannel->dev.bus_id, "defunct");
+ dev_set_name(&css->pseudo_subchannel->dev, "defunct");
ret = cio_create_sch_lock(css->pseudo_subchannel);
if (ret) {
kfree(css->pseudo_subchannel);
@@ -750,7 +707,7 @@ static int __init setup_css(int nr)
mutex_init(&css->mutex);
css->valid = 1;
css->cssid = nr;
- sprintf(css->device.bus_id, "css%x", nr);
+ dev_set_name(&css->device, "css%x", nr);
css->device.release = channel_subsystem_release;
tod_high = (u32) (get_clock() >> 32);
css_generate_pgid(css, tod_high);
@@ -795,8 +752,6 @@ init_channel_subsystem (void)
ret = chsc_determine_css_characteristics();
if (ret == -ENOMEM)
goto out; /* No need to continue. */
- if (ret == 0)
- css_characteristics_avail = 1;
ret = chsc_alloc_sei_area();
if (ret)
@@ -806,6 +761,10 @@ init_channel_subsystem (void)
if (ret)
goto out;
+ ret = s390_register_crw_handler(CRW_RSC_SCH, css_process_crw);
+ if (ret)
+ goto out;
+
if ((ret = bus_register(&css_bus_type)))
goto out;
@@ -831,13 +790,16 @@ init_channel_subsystem (void)
}
channel_subsystems[i] = css;
ret = setup_css(i);
- if (ret)
- goto out_free;
+ if (ret) {
+ kfree(channel_subsystems[i]);
+ goto out_unregister;
+ }
ret = device_register(&css->device);
- if (ret)
- goto out_free_all;
- if (css_characteristics_avail &&
- css_chsc_characteristics.secm) {
+ if (ret) {
+ put_device(&css->device);
+ goto out_unregister;
+ }
+ if (css_chsc_characteristics.secm) {
ret = device_create_file(&css->device,
&dev_attr_cm_enable);
if (ret)
@@ -849,25 +811,20 @@ init_channel_subsystem (void)
}
ret = register_reboot_notifier(&css_reboot_notifier);
if (ret)
- goto out_pseudo;
+ goto out_unregister;
css_init_done = 1;
- ctl_set_bit(6, 28);
+ /* Enable default isc for I/O subchannels. */
+ isc_register(IO_SCH_ISC);
for_each_subchannel(__init_channel_subsystem, NULL);
return 0;
-out_pseudo:
- device_unregister(&channel_subsystems[i]->pseudo_subchannel->dev);
out_file:
- device_remove_file(&channel_subsystems[i]->device,
- &dev_attr_cm_enable);
+ if (css_chsc_characteristics.secm)
+ device_remove_file(&channel_subsystems[i]->device,
+ &dev_attr_cm_enable);
out_device:
device_unregister(&channel_subsystems[i]->device);
-out_free_all:
- kfree(channel_subsystems[i]->pseudo_subchannel->lock);
- kfree(channel_subsystems[i]->pseudo_subchannel);
-out_free:
- kfree(channel_subsystems[i]);
out_unregister:
while (i > 0) {
struct channel_subsystem *css;
@@ -875,7 +832,8 @@ out_unregister:
i--;
css = channel_subsystems[i];
device_unregister(&css->pseudo_subchannel->dev);
- if (css_characteristics_avail && css_chsc_characteristics.secm)
+ css->pseudo_subchannel = NULL;
+ if (css_chsc_characteristics.secm)
device_remove_file(&css->device,
&dev_attr_cm_enable);
device_unregister(&css->device);
@@ -883,6 +841,7 @@ out_unregister:
out_bus:
bus_unregister(&css_bus_type);
out:
+ s390_unregister_crw_handler(CRW_RSC_CSS);
chsc_free_sei_area();
kfree(slow_subchannel_set);
printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n",
@@ -895,19 +854,16 @@ int sch_is_pseudo_sch(struct subchannel *sch)
return sch == to_css(sch->dev.parent)->pseudo_subchannel;
}
-/*
- * find a driver for a subchannel. They identify by the subchannel
- * type with the exception that the console subchannel driver has its own
- * subchannel type although the device is an i/o subchannel
- */
-static int
-css_bus_match (struct device *dev, struct device_driver *drv)
+static int css_bus_match(struct device *dev, struct device_driver *drv)
{
struct subchannel *sch = to_subchannel(dev);
struct css_driver *driver = to_cssdriver(drv);
+ struct css_device_id *id;
- if (sch->st == driver->subchannel_type)
- return 1;
+ for (id = driver->subchannel_type; id->match_flags; id++) {
+ if (sch->st == id->type)
+ return 1;
+ }
return 0;
}
@@ -945,12 +901,25 @@ static void css_shutdown(struct device *dev)
sch->driver->shutdown(sch);
}
+static int css_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct subchannel *sch = to_subchannel(dev);
+ int ret;
+
+ ret = add_uevent_var(env, "ST=%01X", sch->st);
+ if (ret)
+ return ret;
+ ret = add_uevent_var(env, "MODALIAS=css:t%01X", sch->st);
+ return ret;
+}
+
struct bus_type css_bus_type = {
.name = "css",
.match = css_bus_match,
.probe = css_probe,
.remove = css_remove,
.shutdown = css_shutdown,
+ .uevent = css_uevent,
};
/**
@@ -985,4 +954,3 @@ subsys_initcall(init_channel_subsystem);
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(css_bus_type);
-EXPORT_SYMBOL_GPL(css_characteristics_avail);
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index e1913518f35..57ebf120f82 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -9,8 +9,7 @@
#include <asm/cio.h>
#include <asm/chpid.h>
-
-#include "schid.h"
+#include <asm/schid.h>
/*
* path grouping stuff
@@ -58,20 +57,28 @@ struct pgid {
__u32 tod_high; /* high word TOD clock */
} __attribute__ ((packed));
-/*
- * A css driver handles all subchannels of one type.
- * Currently, we only care about I/O subchannels (type 0), these
- * have a ccw_device connected to them.
- */
struct subchannel;
+struct chp_link;
+/**
+ * struct css_driver - device driver for subchannels
+ * @owner: owning module
+ * @subchannel_type: subchannel type supported by this driver
+ * @drv: embedded device driver structure
+ * @irq: called on interrupts
+ * @chp_event: called for events affecting a channel path
+ * @sch_event: called for events affecting the subchannel
+ * @probe: function called on probe
+ * @remove: function called on remove
+ * @shutdown: called at device shutdown
+ * @name: name of the device driver
+ */
struct css_driver {
struct module *owner;
- unsigned int subchannel_type;
+ struct css_device_id *subchannel_type;
struct device_driver drv;
void (*irq)(struct subchannel *);
- int (*notify)(struct subchannel *, int);
- void (*verify)(struct subchannel *);
- void (*termination)(struct subchannel *);
+ int (*chp_event)(struct subchannel *, struct chp_link *, int);
+ int (*sch_event)(struct subchannel *, int);
int (*probe)(struct subchannel *);
int (*remove)(struct subchannel *);
void (*shutdown)(struct subchannel *);
@@ -89,13 +96,13 @@ extern int css_driver_register(struct css_driver *);
extern void css_driver_unregister(struct css_driver *);
extern void css_sch_device_unregister(struct subchannel *);
-extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
+extern int css_probe_device(struct subchannel_id);
+extern struct subchannel *get_subchannel_by_schid(struct subchannel_id);
extern int css_init_done;
int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
int (*fn_unknown)(struct subchannel_id,
void *), void *data);
extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
-extern void css_process_crw(int, int);
extern void css_reiterate_subchannels(void);
void css_update_ssd_info(struct subchannel *sch);
@@ -121,20 +128,6 @@ struct channel_subsystem {
extern struct bus_type css_bus_type;
extern struct channel_subsystem *channel_subsystems[];
-/* Some helper functions for disconnected state. */
-int device_is_disconnected(struct subchannel *);
-void device_set_disconnected(struct subchannel *);
-void device_trigger_reprobe(struct subchannel *);
-
-/* Helper functions for vary on/off. */
-int device_is_online(struct subchannel *);
-void device_kill_io(struct subchannel *);
-void device_set_intretry(struct subchannel *sch);
-int device_trigger_verify(struct subchannel *sch);
-
-/* Machine check helper function. */
-void device_kill_pending_timer(struct subchannel *);
-
/* Helper functions to build lists for the slow path. */
void css_schedule_eval(struct subchannel_id schid);
void css_schedule_eval_all(void);
@@ -145,6 +138,4 @@ int css_sch_is_valid(struct schib *);
extern struct workqueue_struct *slow_path_wq;
void css_wait_for_slow_path(void);
-
-extern struct attribute_group *subch_attr_groups[];
#endif
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index e22813db74a..4e78c82194b 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -2,8 +2,7 @@
* drivers/s390/cio/device.c
* bus driver for ccw devices
*
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
+ * Copyright IBM Corp. 2002,2008
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
@@ -23,13 +22,16 @@
#include <asm/cio.h>
#include <asm/param.h> /* HZ */
#include <asm/cmb.h>
+#include <asm/isc.h>
+#include "chp.h"
#include "cio.h"
#include "cio_debug.h"
#include "css.h"
#include "device.h"
#include "ioasm.h"
#include "io_sch.h"
+#include "blacklist.h"
static struct timer_list recovery_timer;
static DEFINE_SPINLOCK(recovery_lock);
@@ -125,26 +127,30 @@ struct bus_type ccw_bus_type;
static void io_subchannel_irq(struct subchannel *);
static int io_subchannel_probe(struct subchannel *);
static int io_subchannel_remove(struct subchannel *);
-static int io_subchannel_notify(struct subchannel *, int);
-static void io_subchannel_verify(struct subchannel *);
-static void io_subchannel_ioterm(struct subchannel *);
static void io_subchannel_shutdown(struct subchannel *);
+static int io_subchannel_sch_event(struct subchannel *, int);
+static int io_subchannel_chp_event(struct subchannel *, struct chp_link *,
+ int);
+
+static struct css_device_id io_subchannel_ids[] = {
+ { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(css, io_subchannel_ids);
static struct css_driver io_subchannel_driver = {
.owner = THIS_MODULE,
- .subchannel_type = SUBCHANNEL_TYPE_IO,
+ .subchannel_type = io_subchannel_ids,
.name = "io_subchannel",
.irq = io_subchannel_irq,
- .notify = io_subchannel_notify,
- .verify = io_subchannel_verify,
- .termination = io_subchannel_ioterm,
+ .sch_event = io_subchannel_sch_event,
+ .chp_event = io_subchannel_chp_event,
.probe = io_subchannel_probe,
.remove = io_subchannel_remove,
.shutdown = io_subchannel_shutdown,
};
struct workqueue_struct *ccw_device_work;
-struct workqueue_struct *ccw_device_notify_work;
wait_queue_head_t ccw_device_init_wq;
atomic_t ccw_device_init_count;
@@ -162,11 +168,6 @@ init_ccw_bus_type (void)
ccw_device_work = create_singlethread_workqueue("cio");
if (!ccw_device_work)
return -ENOMEM; /* FIXME: better errno ? */
- ccw_device_notify_work = create_singlethread_workqueue("cio_notify");
- if (!ccw_device_notify_work) {
- ret = -ENOMEM; /* FIXME: better errno ? */
- goto out_err;
- }
slow_path_wq = create_singlethread_workqueue("kslowcrw");
if (!slow_path_wq) {
ret = -ENOMEM; /* FIXME: better errno ? */
@@ -186,8 +187,6 @@ init_ccw_bus_type (void)
out_err:
if (ccw_device_work)
destroy_workqueue(ccw_device_work);
- if (ccw_device_notify_work)
- destroy_workqueue(ccw_device_notify_work);
if (slow_path_wq)
destroy_workqueue(slow_path_wq);
return ret;
@@ -198,7 +197,6 @@ cleanup_ccw_bus_type (void)
{
css_driver_unregister(&io_subchannel_driver);
bus_unregister(&ccw_bus_type);
- destroy_workqueue(ccw_device_notify_work);
destroy_workqueue(ccw_device_work);
}
@@ -299,36 +297,33 @@ static void ccw_device_unregister(struct ccw_device *cdev)
device_del(&cdev->dev);
}
-static void ccw_device_remove_orphan_cb(struct device *dev)
+static void ccw_device_remove_orphan_cb(struct work_struct *work)
{
- struct ccw_device *cdev = to_ccwdev(dev);
+ struct ccw_device_private *priv;
+ struct ccw_device *cdev;
+ priv = container_of(work, struct ccw_device_private, kick_work);
+ cdev = priv->cdev;
ccw_device_unregister(cdev);
put_device(&cdev->dev);
+ /* Release cdev reference for workqueue processing. */
+ put_device(&cdev->dev);
}
-static void ccw_device_remove_sch_cb(struct device *dev)
-{
- struct subchannel *sch;
-
- sch = to_subchannel(dev);
- css_sch_device_unregister(sch);
- /* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
- put_device(&sch->dev);
-}
+static void ccw_device_call_sch_unregister(struct work_struct *work);
static void
ccw_device_remove_disconnected(struct ccw_device *cdev)
{
unsigned long flags;
- int rc;
/*
* Forced offline in disconnected state means
* 'throw away device'.
*/
+ /* Get cdev reference for workqueue processing. */
+ if (!get_device(&cdev->dev))
+ return;
if (ccw_device_is_orphan(cdev)) {
/*
* Deregister ccw device.
@@ -338,23 +333,13 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
spin_lock_irqsave(cdev->ccwlock, flags);
cdev->private->state = DEV_STATE_NOT_OPER;
spin_unlock_irqrestore(cdev->ccwlock, flags);
- rc = device_schedule_callback(&cdev->dev,
- ccw_device_remove_orphan_cb);
- if (rc)
- CIO_MSG_EVENT(0, "Couldn't unregister orphan "
- "0.%x.%04x\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno);
- return;
- }
- /* Deregister subchannel, which will kill the ccw device. */
- rc = device_schedule_callback(cdev->dev.parent,
- ccw_device_remove_sch_cb);
- if (rc)
- CIO_MSG_EVENT(0, "Couldn't unregister disconnected device "
- "0.%x.%04x\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno);
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_remove_orphan_cb);
+ } else
+ /* Deregister subchannel, which will kill the ccw device. */
+ PREPARE_WORK(&cdev->private->kick_work,
+ ccw_device_call_sch_unregister);
+ queue_work(slow_path_wq, &cdev->private->kick_work);
}
/**
@@ -487,25 +472,22 @@ static int online_store_recog_and_online(struct ccw_device *cdev)
ccw_device_set_online(cdev);
return 0;
}
-static void online_store_handle_online(struct ccw_device *cdev, int force)
+static int online_store_handle_online(struct ccw_device *cdev, int force)
{
int ret;
ret = online_store_recog_and_online(cdev);
if (ret)
- return;
+ return ret;
if (force && cdev->private->state == DEV_STATE_BOXED) {
ret = ccw_device_stlck(cdev);
- if (ret) {
- dev_warn(&cdev->dev,
- "ccw_device_stlck returned %d!\n", ret);
- return;
- }
+ if (ret)
+ return ret;
if (cdev->id.cu_type == 0)
cdev->private->state = DEV_STATE_NOT_OPER;
online_store_recog_and_online(cdev);
}
-
+ return 0;
}
static ssize_t online_store (struct device *dev, struct device_attribute *attr,
@@ -538,8 +520,9 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr,
ret = count;
break;
case 1:
- online_store_handle_online(cdev, force);
- ret = count;
+ ret = online_store_handle_online(cdev, force);
+ if (!ret)
+ ret = count;
break;
default:
ret = -EINVAL;
@@ -584,19 +567,14 @@ static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
static DEVICE_ATTR(online, 0644, online_show, online_store);
static DEVICE_ATTR(availability, 0444, available_show, NULL);
-static struct attribute * subch_attrs[] = {
+static struct attribute *io_subchannel_attrs[] = {
&dev_attr_chpids.attr,
&dev_attr_pimpampom.attr,
NULL,
};
-static struct attribute_group subch_attr_group = {
- .attrs = subch_attrs,
-};
-
-struct attribute_group *subch_attr_groups[] = {
- &subch_attr_group,
- NULL,
+static struct attribute_group io_subchannel_attr_group = {
+ .attrs = io_subchannel_attrs,
};
static struct attribute * ccwdev_attrs[] = {
@@ -790,7 +768,7 @@ static void sch_attach_device(struct subchannel *sch,
sch_set_cdev(sch, cdev);
cdev->private->schid = sch->schid;
cdev->ccwlock = sch->lock;
- device_trigger_reprobe(sch);
+ ccw_device_trigger_reprobe(cdev);
spin_unlock_irq(sch->lock);
}
@@ -980,12 +958,17 @@ static void ccw_device_call_sch_unregister(struct work_struct *work)
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
+ /* Get subchannel reference for local processing. */
+ if (!get_device(cdev->dev.parent))
+ return;
sch = to_subchannel(cdev->dev.parent);
css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
+ /* Release cdev reference for workqueue processing.*/
put_device(&cdev->dev);
+ /* Release subchannel reference for local processing. */
put_device(&sch->dev);
}
@@ -1011,6 +994,8 @@ io_subchannel_recog_done(struct ccw_device *cdev)
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_call_sch_unregister);
queue_work(slow_path_wq, &cdev->private->kick_work);
+ /* Release subchannel reference for asynchronous recognition. */
+ put_device(&sch->dev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
break;
@@ -1037,7 +1022,6 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
struct ccw_device_private *priv;
sch_set_cdev(sch, cdev);
- sch->driver = &io_subchannel_driver;
cdev->ccwlock = sch->lock;
/* Init private data. */
@@ -1051,8 +1035,11 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
init_timer(&priv->timer);
/* Set an initial name for the device. */
- snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
- sch->schid.ssid, sch->schib.pmcw.dev);
+ if (cio_is_console(sch->schid))
+ cdev->dev.init_name = cio_get_console_cdev_name(sch);
+ else
+ dev_set_name(&cdev->dev, "0.%x.%04x",
+ sch->schid.ssid, sch->schib.pmcw.dev);
/* Increase counter of devices currently in recognition. */
atomic_inc(&ccw_device_init_count);
@@ -1117,13 +1104,38 @@ static void io_subchannel_irq(struct subchannel *sch)
cdev = sch_get_cdev(sch);
CIO_TRACE_EVENT(3, "IRQ");
- CIO_TRACE_EVENT(3, sch->dev.bus_id);
+ CIO_TRACE_EVENT(3, dev_name(&sch->dev));
if (cdev)
dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
}
-static int
-io_subchannel_probe (struct subchannel *sch)
+static void io_subchannel_init_fields(struct subchannel *sch)
+{
+ if (cio_is_console(sch->schid))
+ sch->opm = 0xff;
+ else
+ sch->opm = chp_get_sch_opm(sch);
+ sch->lpm = sch->schib.pmcw.pam & sch->opm;
+ sch->isc = cio_is_console(sch->schid) ? CONSOLE_ISC : IO_SCH_ISC;
+
+ CIO_MSG_EVENT(6, "Detected device %04x on subchannel 0.%x.%04X"
+ " - PIM = %02X, PAM = %02X, POM = %02X\n",
+ sch->schib.pmcw.dev, sch->schid.ssid,
+ sch->schid.sch_no, sch->schib.pmcw.pim,
+ sch->schib.pmcw.pam, sch->schib.pmcw.pom);
+ /* Initially set up some fields in the pmcw. */
+ sch->schib.pmcw.ena = 0;
+ sch->schib.pmcw.csense = 1; /* concurrent sense */
+ if ((sch->lpm & (sch->lpm - 1)) != 0)
+ sch->schib.pmcw.mp = 1; /* multipath mode */
+ /* clean up possible residual cmf stuff */
+ sch->schib.pmcw.mme = 0;
+ sch->schib.pmcw.mbfc = 0;
+ sch->schib.pmcw.mbi = 0;
+ sch->schib.mba = 0;
+}
+
+static int io_subchannel_probe(struct subchannel *sch)
{
struct ccw_device *cdev;
int rc;
@@ -1132,11 +1144,21 @@ io_subchannel_probe (struct subchannel *sch)
cdev = sch_get_cdev(sch);
if (cdev) {
+ rc = sysfs_create_group(&sch->dev.kobj,
+ &io_subchannel_attr_group);
+ if (rc)
+ CIO_MSG_EVENT(0, "Failed to create io subchannel "
+ "attributes for subchannel "
+ "0.%x.%04x (rc=%d)\n",
+ sch->schid.ssid, sch->schid.sch_no, rc);
/*
* This subchannel already has an associated ccw_device.
- * Register it and exit. This happens for all early
- * device, e.g. the console.
+ * Throw the delayed uevent for the subchannel, register
+ * the ccw_device and exit. This happens for all early
+ * devices, e.g. the console.
*/
+ sch->dev.uevent_suppress = 0;
+ kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
cdev->dev.groups = ccwdev_attr_groups;
device_initialize(&cdev->dev);
ccw_device_register(cdev);
@@ -1152,17 +1174,24 @@ io_subchannel_probe (struct subchannel *sch)
get_device(&cdev->dev);
return 0;
}
+ io_subchannel_init_fields(sch);
/*
* First check if a fitting device may be found amongst the
* disconnected devices or in the orphanage.
*/
dev_id.devno = sch->schib.pmcw.dev;
dev_id.ssid = sch->schid.ssid;
+ rc = sysfs_create_group(&sch->dev.kobj,
+ &io_subchannel_attr_group);
+ if (rc)
+ return rc;
/* Allocate I/O subchannel private data. */
sch->private = kzalloc(sizeof(struct io_subchannel_private),
GFP_KERNEL | GFP_DMA);
- if (!sch->private)
- return -ENOMEM;
+ if (!sch->private) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
if (!cdev)
cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
@@ -1181,8 +1210,8 @@ io_subchannel_probe (struct subchannel *sch)
}
cdev = io_subchannel_create_ccwdev(sch);
if (IS_ERR(cdev)) {
- kfree(sch->private);
- return PTR_ERR(cdev);
+ rc = PTR_ERR(cdev);
+ goto out_err;
}
rc = io_subchannel_recog(cdev, sch);
if (rc) {
@@ -1191,9 +1220,12 @@ io_subchannel_probe (struct subchannel *sch)
spin_unlock_irqrestore(sch->lock, flags);
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
- kfree(sch->private);
+ goto out_err;
}
-
+ return 0;
+out_err:
+ kfree(sch->private);
+ sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
return rc;
}
@@ -1214,6 +1246,7 @@ io_subchannel_remove (struct subchannel *sch)
ccw_device_unregister(cdev);
put_device(&cdev->dev);
kfree(sch->private);
+ sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
return 0;
}
@@ -1224,11 +1257,7 @@ static int io_subchannel_notify(struct subchannel *sch, int event)
cdev = sch_get_cdev(sch);
if (!cdev)
return 0;
- if (!cdev->drv)
- return 0;
- if (!cdev->online)
- return 0;
- return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
+ return ccw_device_notify(cdev, event);
}
static void io_subchannel_verify(struct subchannel *sch)
@@ -1240,22 +1269,96 @@ static void io_subchannel_verify(struct subchannel *sch)
dev_fsm_event(cdev, DEV_EVENT_VERIFY);
}
-static void io_subchannel_ioterm(struct subchannel *sch)
+static int check_for_io_on_path(struct subchannel *sch, int mask)
{
- struct ccw_device *cdev;
+ int cc;
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return;
- /* Internal I/O will be retried by the interrupt handler. */
- if (cdev->private->flags.intretry)
+ cc = stsch(sch->schid, &sch->schib);
+ if (cc)
+ return 0;
+ if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask)
+ return 1;
+ return 0;
+}
+
+static void terminate_internal_io(struct subchannel *sch,
+ struct ccw_device *cdev)
+{
+ if (cio_clear(sch)) {
+ /* Recheck device in case clear failed. */
+ sch->lpm = 0;
+ if (cdev->online)
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+ else
+ css_schedule_eval(sch->schid);
return;
+ }
cdev->private->state = DEV_STATE_CLEAR_VERIFY;
+ /* Request retry of internal operation. */
+ cdev->private->flags.intretry = 1;
+ /* Call handler. */
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
ERR_PTR(-EIO));
}
+static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask)
+{
+ struct ccw_device *cdev;
+
+ cdev = sch_get_cdev(sch);
+ if (!cdev)
+ return;
+ if (check_for_io_on_path(sch, mask)) {
+ if (cdev->private->state == DEV_STATE_ONLINE)
+ ccw_device_kill_io(cdev);
+ else {
+ terminate_internal_io(sch, cdev);
+ /* Re-start path verification. */
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+ }
+ } else
+ /* trigger path verification. */
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+
+}
+
+static int io_subchannel_chp_event(struct subchannel *sch,
+ struct chp_link *link, int event)
+{
+ int mask;
+
+ mask = chp_ssd_get_mask(&sch->ssd_info, link);
+ if (!mask)
+ return 0;
+ switch (event) {
+ case CHP_VARY_OFF:
+ sch->opm &= ~mask;
+ sch->lpm &= ~mask;
+ io_subchannel_terminate_path(sch, mask);
+ break;
+ case CHP_VARY_ON:
+ sch->opm |= mask;
+ sch->lpm |= mask;
+ io_subchannel_verify(sch);
+ break;
+ case CHP_OFFLINE:
+ if (stsch(sch->schid, &sch->schib))
+ return -ENXIO;
+ if (!css_sch_is_valid(&sch->schib))
+ return -ENODEV;
+ io_subchannel_terminate_path(sch, mask);
+ break;
+ case CHP_ONLINE:
+ if (stsch(sch->schid, &sch->schib))
+ return -ENXIO;
+ sch->lpm |= mask & sch->opm;
+ io_subchannel_verify(sch);
+ break;
+ }
+ return 0;
+}
+
static void
io_subchannel_shutdown(struct subchannel *sch)
{
@@ -1285,8 +1388,247 @@ io_subchannel_shutdown(struct subchannel *sch)
cio_disable_subchannel(sch);
}
+static int io_subchannel_get_status(struct subchannel *sch)
+{
+ struct schib schib;
+
+ if (stsch(sch->schid, &schib) || !schib.pmcw.dnv)
+ return CIO_GONE;
+ if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
+ return CIO_REVALIDATE;
+ if (!sch->lpm)
+ return CIO_NO_PATH;
+ return CIO_OPER;
+}
+
+static int device_is_disconnected(struct ccw_device *cdev)
+{
+ if (!cdev)
+ return 0;
+ return (cdev->private->state == DEV_STATE_DISCONNECTED ||
+ cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
+}
+
+static int recovery_check(struct device *dev, void *data)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ int *redo = data;
+
+ spin_lock_irq(cdev->ccwlock);
+ switch (cdev->private->state) {
+ case DEV_STATE_DISCONNECTED:
+ CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n",
+ cdev->private->dev_id.ssid,
+ cdev->private->dev_id.devno);
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+ *redo = 1;
+ break;
+ case DEV_STATE_DISCONNECTED_SENSE_ID:
+ *redo = 1;
+ break;
+ }
+ spin_unlock_irq(cdev->ccwlock);
+
+ return 0;
+}
+
+static void recovery_work_func(struct work_struct *unused)
+{
+ int redo = 0;
+
+ bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check);
+ if (redo) {
+ spin_lock_irq(&recovery_lock);
+ if (!timer_pending(&recovery_timer)) {
+ if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1)
+ recovery_phase++;
+ mod_timer(&recovery_timer, jiffies +
+ recovery_delay[recovery_phase] * HZ);
+ }
+ spin_unlock_irq(&recovery_lock);
+ } else
+ CIO_MSG_EVENT(4, "recovery: end\n");
+}
+
+static DECLARE_WORK(recovery_work, recovery_work_func);
+
+static void recovery_func(unsigned long data)
+{
+ /*
+ * We can't do our recovery in softirq context and it's not
+ * performance critical, so we schedule it.
+ */
+ schedule_work(&recovery_work);
+}
+
+static void ccw_device_schedule_recovery(void)
+{
+ unsigned long flags;
+
+ CIO_MSG_EVENT(4, "recovery: schedule\n");
+ spin_lock_irqsave(&recovery_lock, flags);
+ if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) {
+ recovery_phase = 0;
+ mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ);
+ }
+ spin_unlock_irqrestore(&recovery_lock, flags);
+}
+
+static int purge_fn(struct device *dev, void *data)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct ccw_device_private *priv = cdev->private;
+ int unreg;
+
+ spin_lock_irq(cdev->ccwlock);
+ unreg = is_blacklisted(priv->dev_id.ssid, priv->dev_id.devno) &&
+ (priv->state == DEV_STATE_OFFLINE);
+ spin_unlock_irq(cdev->ccwlock);
+ if (!unreg)
+ goto out;
+ if (!get_device(&cdev->dev))
+ goto out;
+ CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid,
+ priv->dev_id.devno);
+ PREPARE_WORK(&cdev->private->kick_work, ccw_device_call_sch_unregister);
+ queue_work(slow_path_wq, &cdev->private->kick_work);
+
+out:
+ /* Abort loop in case of pending signal. */
+ if (signal_pending(current))
+ return -EINTR;
+
+ return 0;
+}
+
+/**
+ * ccw_purge_blacklisted - purge unused, blacklisted devices
+ *
+ * Unregister all ccw devices that are offline and on the blacklist.
+ */
+int ccw_purge_blacklisted(void)
+{
+ CIO_MSG_EVENT(2, "ccw: purging blacklisted devices\n");
+ bus_for_each_dev(&ccw_bus_type, NULL, NULL, purge_fn);
+ return 0;
+}
+
+static void device_set_disconnected(struct ccw_device *cdev)
+{
+ if (!cdev)
+ return;
+ ccw_device_set_timeout(cdev, 0);
+ cdev->private->flags.fake_irb = 0;
+ cdev->private->state = DEV_STATE_DISCONNECTED;
+ if (cdev->online)
+ ccw_device_schedule_recovery();
+}
+
+void ccw_device_set_notoper(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+ CIO_TRACE_EVENT(2, "notoper");
+ CIO_TRACE_EVENT(2, dev_name(&sch->dev));
+ ccw_device_set_timeout(cdev, 0);
+ cio_disable_subchannel(sch);
+ cdev->private->state = DEV_STATE_NOT_OPER;
+}
+
+static int io_subchannel_sch_event(struct subchannel *sch, int slow)
+{
+ int event, ret, disc;
+ unsigned long flags;
+ enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE, DISC } action;
+ struct ccw_device *cdev;
+
+ spin_lock_irqsave(sch->lock, flags);
+ cdev = sch_get_cdev(sch);
+ disc = device_is_disconnected(cdev);
+ if (disc && slow) {
+ /* Disconnected devices are evaluated directly only.*/
+ spin_unlock_irqrestore(sch->lock, flags);
+ return 0;
+ }
+ /* No interrupt after machine check - kill pending timers. */
+ if (cdev)
+ ccw_device_set_timeout(cdev, 0);
+ if (!disc && !slow) {
+ /* Non-disconnected devices are evaluated on the slow path. */
+ spin_unlock_irqrestore(sch->lock, flags);
+ return -EAGAIN;
+ }
+ event = io_subchannel_get_status(sch);
+ CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
+ sch->schid.ssid, sch->schid.sch_no, event,
+ disc ? "disconnected" : "normal",
+ slow ? "slow" : "fast");
+ /* Analyze subchannel status. */
+ action = NONE;
+ switch (event) {
+ case CIO_NO_PATH:
+ if (disc) {
+ /* Check if paths have become available. */
+ action = REPROBE;
+ break;
+ }
+ /* fall through */
+ case CIO_GONE:
+ /* Ask driver what to do with device. */
+ if (io_subchannel_notify(sch, event))
+ action = DISC;
+ else
+ action = UNREGISTER;
+ break;
+ case CIO_REVALIDATE:
+ /* Device will be removed, so no notify necessary. */
+ if (disc)
+ /* Reprobe because immediate unregister might block. */
+ action = REPROBE;
+ else
+ action = UNREGISTER_PROBE;
+ break;
+ case CIO_OPER:
+ if (disc)
+ /* Get device operational again. */
+ action = REPROBE;
+ break;
+ }
+ /* Perform action. */
+ ret = 0;
+ switch (action) {
+ case UNREGISTER:
+ case UNREGISTER_PROBE:
+ ccw_device_set_notoper(cdev);
+ /* Unregister device (will use subchannel lock). */
+ spin_unlock_irqrestore(sch->lock, flags);
+ css_sch_device_unregister(sch);
+ spin_lock_irqsave(sch->lock, flags);
+
+ /* Reset intparm to zeroes. */
+ sch->schib.pmcw.intparm = 0;
+ cio_modify(sch);
+ break;
+ case REPROBE:
+ ccw_device_trigger_reprobe(cdev);
+ break;
+ case DISC:
+ device_set_disconnected(cdev);
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(sch->lock, flags);
+ /* Probe if necessary. */
+ if (action == UNREGISTER_PROBE)
+ ret = css_probe_device(sch->schid);
+
+ return ret;
+}
+
#ifdef CONFIG_CCW_CONSOLE
static struct ccw_device console_cdev;
+static char console_cdev_name[10] = "0.x.xxxx";
static struct ccw_device_private console_private;
static int console_cdev_in_use;
@@ -1297,14 +1639,16 @@ spinlock_t * cio_get_console_lock(void)
return &ccw_console_lock;
}
-static int
-ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
+static int ccw_device_console_enable(struct ccw_device *cdev,
+ struct subchannel *sch)
{
int rc;
/* Attach subchannel private data. */
sch->private = cio_get_console_priv();
memset(sch->private, 0, sizeof(struct io_subchannel_private));
+ io_subchannel_init_fields(sch);
+ sch->driver = &io_subchannel_driver;
/* Initialize the ccw_device structure. */
cdev->dev.parent= &sch->dev;
rc = io_subchannel_recog(cdev, sch);
@@ -1355,6 +1699,14 @@ ccw_device_probe_console(void)
console_cdev.online = 1;
return &console_cdev;
}
+
+
+const char *cio_get_console_cdev_name(struct subchannel *sch)
+{
+ snprintf(console_cdev_name, 10, "0.%x.%04x",
+ sch->schid.ssid, sch->schib.pmcw.dev);
+ return (const char *)console_cdev_name;
+}
#endif
/*
@@ -1367,7 +1719,7 @@ __ccwdev_check_busid(struct device *dev, void *id)
bus_id = id;
- return (strncmp(bus_id, dev->bus_id, BUS_ID_SIZE) == 0);
+ return (strncmp(bus_id, dev_name(dev), BUS_ID_SIZE) == 0);
}
@@ -1515,71 +1867,6 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev)
return sch->schid;
}
-static int recovery_check(struct device *dev, void *data)
-{
- struct ccw_device *cdev = to_ccwdev(dev);
- int *redo = data;
-
- spin_lock_irq(cdev->ccwlock);
- switch (cdev->private->state) {
- case DEV_STATE_DISCONNECTED:
- CIO_MSG_EVENT(4, "recovery: trigger 0.%x.%04x\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno);
- dev_fsm_event(cdev, DEV_EVENT_VERIFY);
- *redo = 1;
- break;
- case DEV_STATE_DISCONNECTED_SENSE_ID:
- *redo = 1;
- break;
- }
- spin_unlock_irq(cdev->ccwlock);
-
- return 0;
-}
-
-static void recovery_work_func(struct work_struct *unused)
-{
- int redo = 0;
-
- bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check);
- if (redo) {
- spin_lock_irq(&recovery_lock);
- if (!timer_pending(&recovery_timer)) {
- if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1)
- recovery_phase++;
- mod_timer(&recovery_timer, jiffies +
- recovery_delay[recovery_phase] * HZ);
- }
- spin_unlock_irq(&recovery_lock);
- } else
- CIO_MSG_EVENT(4, "recovery: end\n");
-}
-
-static DECLARE_WORK(recovery_work, recovery_work_func);
-
-static void recovery_func(unsigned long data)
-{
- /*
- * We can't do our recovery in softirq context and it's not
- * performance critical, so we schedule it.
- */
- schedule_work(&recovery_work);
-}
-
-void ccw_device_schedule_recovery(void)
-{
- unsigned long flags;
-
- CIO_MSG_EVENT(4, "recovery: schedule\n");
- spin_lock_irqsave(&recovery_lock, flags);
- if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) {
- recovery_phase = 0;
- mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ);
- }
- spin_unlock_irqrestore(&recovery_lock, flags);
-}
-
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_online);
EXPORT_SYMBOL(ccw_device_set_offline);
@@ -1588,5 +1875,4 @@ EXPORT_SYMBOL(ccw_driver_unregister);
EXPORT_SYMBOL(get_ccwdev_by_busid);
EXPORT_SYMBOL(ccw_bus_type);
EXPORT_SYMBOL(ccw_device_work);
-EXPORT_SYMBOL(ccw_device_notify_work);
EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index cb08092be39..104ed669db4 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -72,7 +72,6 @@ dev_fsm_final_state(struct ccw_device *cdev)
}
extern struct workqueue_struct *ccw_device_work;
-extern struct workqueue_struct *ccw_device_notify_work;
extern wait_queue_head_t ccw_device_init_wq;
extern atomic_t ccw_device_init_count;
@@ -87,8 +86,7 @@ int ccw_device_is_orphan(struct ccw_device *);
int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
int ccw_device_offline(struct ccw_device *);
-
-void ccw_device_schedule_recovery(void);
+int ccw_purge_blacklisted(void);
/* Function prototypes for device status and basic sense stuff. */
void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
@@ -118,6 +116,12 @@ int ccw_device_call_handler(struct ccw_device *);
int ccw_device_stlck(struct ccw_device *);
+/* Helper function for machine check handling. */
+void ccw_device_trigger_reprobe(struct ccw_device *);
+void ccw_device_kill_io(struct ccw_device *);
+int ccw_device_notify(struct ccw_device *, int);
+void ccw_device_set_notoper(struct ccw_device *cdev);
+
/* qdio needs this. */
void ccw_device_set_timeout(struct ccw_device *, int);
extern struct subchannel_id ccw_device_get_subchannel_id(struct ccw_device *);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index e268d5a77c1..10bc03940fb 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -2,8 +2,7 @@
* drivers/s390/cio/device_fsm.c
* finite state machine for device handling
*
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
+ * Copyright IBM Corp. 2002,2008
* Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
@@ -27,65 +26,6 @@
static int timeout_log_enabled;
-int
-device_is_online(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return 0;
- return (cdev->private->state == DEV_STATE_ONLINE);
-}
-
-int
-device_is_disconnected(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return 0;
- return (cdev->private->state == DEV_STATE_DISCONNECTED ||
- cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
-}
-
-void
-device_set_disconnected(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return;
- ccw_device_set_timeout(cdev, 0);
- cdev->private->flags.fake_irb = 0;
- cdev->private->state = DEV_STATE_DISCONNECTED;
- if (cdev->online)
- ccw_device_schedule_recovery();
-}
-
-void device_set_intretry(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return;
- cdev->private->flags.intretry = 1;
-}
-
-int device_trigger_verify(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev || !cdev->online)
- return -EINVAL;
- dev_fsm_event(cdev, DEV_EVENT_VERIFY);
- return 0;
-}
-
static int __init ccw_timeout_log_setup(char *unused)
{
timeout_log_enabled = 1;
@@ -99,31 +39,45 @@ static void ccw_timeout_log(struct ccw_device *cdev)
struct schib schib;
struct subchannel *sch;
struct io_subchannel_private *private;
+ union orb *orb;
int cc;
sch = to_subchannel(cdev->dev.parent);
private = to_io_private(sch);
+ orb = &private->orb;
cc = stsch(sch->schid, &schib);
printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, "
"device information:\n", get_clock());
printk(KERN_WARNING "cio: orb:\n");
print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
- &private->orb, sizeof(private->orb), 0);
- printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id);
- printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id);
+ orb, sizeof(*orb), 0);
+ printk(KERN_WARNING "cio: ccw device bus id: %s\n",
+ dev_name(&cdev->dev));
+ printk(KERN_WARNING "cio: subchannel bus id: %s\n",
+ dev_name(&sch->dev));
printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, "
"vpm: %02x\n", sch->lpm, sch->opm, sch->vpm);
- if ((void *)(addr_t)private->orb.cpa == &private->sense_ccw ||
- (void *)(addr_t)private->orb.cpa == cdev->private->iccws)
- printk(KERN_WARNING "cio: last channel program (intern):\n");
- else
- printk(KERN_WARNING "cio: last channel program:\n");
-
- print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
- (void *)(addr_t)private->orb.cpa,
- sizeof(struct ccw1), 0);
+ if (orb->tm.b) {
+ printk(KERN_WARNING "cio: orb indicates transport mode\n");
+ printk(KERN_WARNING "cio: last tcw:\n");
+ print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
+ (void *)(addr_t)orb->tm.tcw,
+ sizeof(struct tcw), 0);
+ } else {
+ printk(KERN_WARNING "cio: orb indicates command mode\n");
+ if ((void *)(addr_t)orb->cmd.cpa == &private->sense_ccw ||
+ (void *)(addr_t)orb->cmd.cpa == cdev->private->iccws)
+ printk(KERN_WARNING "cio: last channel program "
+ "(intern):\n");
+ else
+ printk(KERN_WARNING "cio: last channel program:\n");
+
+ print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
+ (void *)(addr_t)orb->cmd.cpa,
+ sizeof(struct ccw1), 0);
+ }
printk(KERN_WARNING "cio: ccw device state: %d\n",
cdev->private->state);
printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc);
@@ -171,18 +125,6 @@ ccw_device_set_timeout(struct ccw_device *cdev, int expires)
add_timer(&cdev->private->timer);
}
-/* Kill any pending timers after machine check. */
-void
-device_kill_pending_timer(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return;
- ccw_device_set_timeout(cdev, 0);
-}
-
/*
* Cancel running i/o. This is called repeatedly since halt/clear are
* asynchronous operations. We do one try with cio_cancel, two tries
@@ -205,15 +147,18 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
/* Not operational -> done. */
return 0;
/* Stage 1: cancel io. */
- if (!(sch->schib.scsw.actl & SCSW_ACTL_HALT_PEND) &&
- !(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) {
- ret = cio_cancel(sch);
- if (ret != -EINVAL)
- return ret;
- /* cancel io unsuccessful. From now on it is asynchronous. */
+ if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_HALT_PEND) &&
+ !(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
+ if (!scsw_is_tm(&sch->schib.scsw)) {
+ ret = cio_cancel(sch);
+ if (ret != -EINVAL)
+ return ret;
+ }
+ /* cancel io unsuccessful or not applicable (transport mode).
+ * Continue with asynchronous instructions. */
cdev->private->iretry = 3; /* 3 halt retries. */
}
- if (!(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) {
+ if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
/* Stage 2: halt io. */
if (cdev->private->iretry) {
cdev->private->iretry--;
@@ -388,36 +333,40 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err)
}
}
-static void
-ccw_device_oper_notify(struct work_struct *work)
+int ccw_device_notify(struct ccw_device *cdev, int event)
+{
+ if (!cdev->drv)
+ return 0;
+ if (!cdev->online)
+ return 0;
+ CIO_MSG_EVENT(2, "notify called for 0.%x.%04x, event=%d\n",
+ cdev->private->dev_id.ssid, cdev->private->dev_id.devno,
+ event);
+ return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
+}
+
+static void cmf_reenable_delayed(struct work_struct *work)
{
struct ccw_device_private *priv;
struct ccw_device *cdev;
- struct subchannel *sch;
- int ret;
- unsigned long flags;
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
- spin_lock_irqsave(cdev->ccwlock, flags);
- sch = to_subchannel(cdev->dev.parent);
- if (sch->driver && sch->driver->notify) {
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- ret = sch->driver->notify(sch, CIO_OPER);
- spin_lock_irqsave(cdev->ccwlock, flags);
- } else
- ret = 0;
- if (ret) {
+ cmf_reenable(cdev);
+}
+
+static void ccw_device_oper_notify(struct ccw_device *cdev)
+{
+ if (ccw_device_notify(cdev, CIO_OPER)) {
/* Reenable channel measurements, if needed. */
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- cmf_reenable(cdev);
- spin_lock_irqsave(cdev->ccwlock, flags);
- wake_up(&cdev->private->wait_q);
+ PREPARE_WORK(&cdev->private->kick_work, cmf_reenable_delayed);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
+ return;
}
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- if (!ret)
- /* Driver doesn't want device back. */
- ccw_device_do_unreg_rereg(work);
+ /* Driver doesn't want device back. */
+ ccw_device_set_notoper(cdev);
+ PREPARE_WORK(&cdev->private->kick_work, ccw_device_do_unreg_rereg);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
}
/*
@@ -447,8 +396,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
if (cdev->private->flags.donotify) {
cdev->private->flags.donotify = 0;
- PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify);
- queue_work(ccw_device_notify_work, &cdev->private->kick_work);
+ ccw_device_oper_notify(cdev);
}
wake_up(&cdev->private->wait_q);
@@ -621,10 +569,11 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
/* Deliver fake irb to device driver, if needed. */
if (cdev->private->flags.fake_irb) {
memset(&cdev->private->irb, 0, sizeof(struct irb));
- cdev->private->irb.scsw.cc = 1;
- cdev->private->irb.scsw.fctl = SCSW_FCTL_START_FUNC;
- cdev->private->irb.scsw.actl = SCSW_ACTL_START_PEND;
- cdev->private->irb.scsw.stctl = SCSW_STCTL_STATUS_PEND;
+ cdev->private->irb.scsw.cmd.cc = 1;
+ cdev->private->irb.scsw.cmd.fctl = SCSW_FCTL_START_FUNC;
+ cdev->private->irb.scsw.cmd.actl = SCSW_ACTL_START_PEND;
+ cdev->private->irb.scsw.cmd.stctl =
+ SCSW_STCTL_STATUS_PEND;
cdev->private->flags.fake_irb = 0;
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
@@ -711,6 +660,13 @@ ccw_device_offline(struct ccw_device *cdev)
{
struct subchannel *sch;
+ /* Allow ccw_device_offline while disconnected. */
+ if (cdev->private->state == DEV_STATE_DISCONNECTED ||
+ cdev->private->state == DEV_STATE_NOT_OPER) {
+ cdev->private->flags.donotify = 0;
+ ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+ return 0;
+ }
if (ccw_device_is_orphan(cdev)) {
ccw_device_done(cdev, DEV_STATE_OFFLINE);
return 0;
@@ -718,13 +674,10 @@ ccw_device_offline(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
return -ENODEV;
- if (cdev->private->state != DEV_STATE_ONLINE) {
- if (sch->schib.scsw.actl != 0)
- return -EBUSY;
- return -EINVAL;
- }
- if (sch->schib.scsw.actl != 0)
+ if (scsw_actl(&sch->schib.scsw) != 0)
return -EBUSY;
+ if (cdev->private->state != DEV_STATE_ONLINE)
+ return -EINVAL;
/* Are we doing path grouping? */
if (!cdev->private->options.pgroup) {
/* No, set state offline immediately. */
@@ -799,9 +752,9 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
*/
stsch(sch->schid, &sch->schib);
- if (sch->schib.scsw.actl != 0 ||
- (sch->schib.scsw.stctl & SCSW_STCTL_STATUS_PEND) ||
- (cdev->private->irb.scsw.stctl & SCSW_STCTL_STATUS_PEND)) {
+ if (scsw_actl(&sch->schib.scsw) != 0 ||
+ (scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
+ (scsw_stctl(&cdev->private->irb.scsw) & SCSW_STCTL_STATUS_PEND)) {
/*
* No final status yet or final status not yet delivered
* to the device driver. Can't do path verfication now,
@@ -823,13 +776,13 @@ static void
ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
{
struct irb *irb;
+ int is_cmd;
irb = (struct irb *) __LC_IRB;
+ is_cmd = !scsw_is_tm(&irb->scsw);
/* Check for unsolicited interrupt. */
- if ((irb->scsw.stctl ==
- (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS))
- && (!irb->scsw.cc)) {
- if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
+ if (!scsw_is_solicited(&irb->scsw)) {
+ if (is_cmd && (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
!irb->esw.esw0.erw.cons) {
/* Unit check but no sense data. Need basic sense. */
if (ccw_device_do_sense(cdev, irb) != 0)
@@ -848,7 +801,7 @@ call_handler_unsol:
}
/* Accumulate status and find out if a basic sense is needed. */
ccw_device_accumulate_irb(cdev, irb);
- if (cdev->private->flags.dosense) {
+ if (is_cmd && cdev->private->flags.dosense) {
if (ccw_device_do_sense(cdev, irb) == 0) {
cdev->private->state = DEV_STATE_W4SENSE;
}
@@ -892,9 +845,9 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
irb = (struct irb *) __LC_IRB;
/* Check for unsolicited interrupt. */
- if (irb->scsw.stctl ==
- (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if (irb->scsw.cc == 1)
+ if (scsw_stctl(&irb->scsw) ==
+ (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
+ if (scsw_cc(&irb->scsw) == 1)
/* Basic sense hasn't started. Try again. */
ccw_device_do_sense(cdev, irb);
else {
@@ -912,7 +865,8 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
* only deliver the halt/clear interrupt to the device driver as if it
* had killed the original request.
*/
- if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
+ if (scsw_fctl(&irb->scsw) &
+ (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
/* Retry Basic Sense if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
@@ -986,12 +940,10 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event)
ERR_PTR(-EIO));
}
-void device_kill_io(struct subchannel *sch)
+void ccw_device_kill_io(struct ccw_device *cdev)
{
int ret;
- struct ccw_device *cdev;
- cdev = sch_get_cdev(sch);
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, 3*HZ);
@@ -1021,9 +973,9 @@ ccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event)
case DEV_EVENT_INTERRUPT:
irb = (struct irb *) __LC_IRB;
/* Check for unsolicited interrupt. */
- if ((irb->scsw.stctl ==
+ if ((scsw_stctl(&irb->scsw) ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
- (!irb->scsw.cc))
+ (!scsw_cc(&irb->scsw)))
/* FIXME: we should restart stlck here, but this
* is extremely unlikely ... */
goto out_wakeup;
@@ -1055,17 +1007,14 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
ccw_device_sense_id_start(cdev);
}
-void
-device_trigger_reprobe(struct subchannel *sch)
+void ccw_device_trigger_reprobe(struct ccw_device *cdev)
{
- struct ccw_device *cdev;
+ struct subchannel *sch;
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return;
if (cdev->private->state != DEV_STATE_DISCONNECTED)
return;
+ sch = to_subchannel(cdev->dev.parent);
/* Update some values. */
if (stsch(sch->schid, &sch->schib))
return;
@@ -1081,7 +1030,6 @@ device_trigger_reprobe(struct subchannel *sch)
sch->schib.pmcw.ena = 0;
if ((sch->lpm & (sch->lpm - 1)) != 0)
sch->schib.pmcw.mp = 1;
- sch->schib.pmcw.intparm = (u32)(addr_t)sch;
/* We should also udate ssd info, but this has to wait. */
/* Check if this is another device which appeared on the same sch. */
if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c
index cba7020517e..1bdaa614e34 100644
--- a/drivers/s390/cio/device_id.c
+++ b/drivers/s390/cio/device_id.c
@@ -196,7 +196,7 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
irb = &cdev->private->irb;
/* Check the error cases. */
- if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry Sense ID if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
@@ -234,10 +234,10 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
irb->ecw[6], irb->ecw[7]);
return -EAGAIN;
}
- if (irb->scsw.cc == 3) {
+ if (irb->scsw.cmd.cc == 3) {
u8 lpm;
- lpm = to_io_private(sch)->orb.lpm;
+ lpm = to_io_private(sch)->orb.cmd.lpm;
if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x "
"on subchannel 0.%x.%04x is "
@@ -248,9 +248,9 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
}
/* Did we get a proper answer ? */
- if (irb->scsw.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF &&
+ if (irb->scsw.cmd.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF &&
cdev->private->senseid.reserved == 0xFF) {
- if (irb->scsw.count < sizeof(struct senseid) - 8)
+ if (irb->scsw.cmd.count < sizeof(struct senseid) - 8)
cdev->private->flags.esid = 1;
return 0; /* Success */
}
@@ -260,7 +260,7 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
"subchannel 0.%x.%04x returns status %02X%02X\n",
cdev->private->dev_id.devno, sch->schid.ssid,
sch->schid.sch_no,
- irb->scsw.dstat, irb->scsw.cstat);
+ irb->scsw.cmd.dstat, irb->scsw.cmd.cstat);
return -EAGAIN;
}
@@ -277,9 +277,9 @@ ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event)
sch = to_subchannel(cdev->dev.parent);
irb = (struct irb *) __LC_IRB;
/* Retry sense id, if needed. */
- if (irb->scsw.stctl ==
+ if (irb->scsw.cmd.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if ((irb->scsw.cc == 1) || !irb->scsw.actl) {
+ if ((irb->scsw.cmd.cc == 1) || !irb->scsw.cmd.actl) {
ret = __ccw_device_sense_id_start(cdev);
if (ret && ret != -EBUSY)
ccw_device_sense_id_done(cdev, ret);
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index f308ad55a6d..eabcc42d63d 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -17,6 +17,7 @@
#include <asm/ccwdev.h>
#include <asm/idals.h>
#include <asm/chpid.h>
+#include <asm/fcx.h>
#include "cio.h"
#include "cio_debug.h"
@@ -179,8 +180,8 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
return -EBUSY;
}
if (cdev->private->state != DEV_STATE_ONLINE ||
- ((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&
- !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) ||
+ ((sch->schib.scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) &&
+ !(sch->schib.scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS)) ||
cdev->private->flags.doverify)
return -EBUSY;
ret = cio_set_options (sch, flags);
@@ -379,7 +380,7 @@ int ccw_device_resume(struct ccw_device *cdev)
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE ||
- !(sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED))
+ !(sch->schib.scsw.cmd.actl & SCSW_ACTL_SUSPENDED))
return -EINVAL;
return cio_resume(sch);
}
@@ -404,7 +405,7 @@ ccw_device_call_handler(struct ccw_device *cdev)
* - fast notification was requested (primary status)
* - unsolicited interrupts
*/
- stctl = cdev->private->irb.scsw.stctl;
+ stctl = scsw_stctl(&cdev->private->irb.scsw);
ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
(stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
(stctl == SCSW_STCTL_STATUS_PEND);
@@ -497,7 +498,7 @@ ccw_device_stlck(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
CIO_TRACE_EVENT(2, "stl lock");
- CIO_TRACE_EVENT(2, cdev->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&cdev->dev));
buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);
if (!buf)
@@ -528,14 +529,15 @@ ccw_device_stlck(struct ccw_device *cdev)
cio_disable_subchannel(sch); //FIXME: return code?
goto out_unlock;
}
- cdev->private->irb.scsw.actl |= SCSW_ACTL_START_PEND;
+ cdev->private->irb.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
spin_unlock_irqrestore(sch->lock, flags);
- wait_event(cdev->private->wait_q, cdev->private->irb.scsw.actl == 0);
+ wait_event(cdev->private->wait_q,
+ cdev->private->irb.scsw.cmd.actl == 0);
spin_lock_irqsave(sch->lock, flags);
cio_disable_subchannel(sch); //FIXME: return code?
- if ((cdev->private->irb.scsw.dstat !=
+ if ((cdev->private->irb.scsw.cmd.dstat !=
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
- (cdev->private->irb.scsw.cstat != 0))
+ (cdev->private->irb.scsw.cmd.cstat != 0))
ret = -EIO;
/* Clear irb. */
memset(&cdev->private->irb, 0, sizeof(struct irb));
@@ -568,6 +570,122 @@ void ccw_device_get_id(struct ccw_device *cdev, struct ccw_dev_id *dev_id)
}
EXPORT_SYMBOL(ccw_device_get_id);
+/**
+ * ccw_device_tm_start_key - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ * @key: storage key to use for storage access
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw,
+ unsigned long intparm, u8 lpm, u8 key)
+{
+ struct subchannel *sch;
+ int rc;
+
+ sch = to_subchannel(cdev->dev.parent);
+ if (cdev->private->state != DEV_STATE_ONLINE)
+ return -EIO;
+ /* Adjust requested path mask to excluded varied off paths. */
+ if (lpm) {
+ lpm &= sch->opm;
+ if (lpm == 0)
+ return -EACCES;
+ }
+ rc = cio_tm_start_key(sch, tcw, lpm, key);
+ if (rc == 0)
+ cdev->private->intparm = intparm;
+ return rc;
+}
+EXPORT_SYMBOL(ccw_device_tm_start_key);
+
+/**
+ * ccw_device_tm_start_timeout_key - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ * @key: storage key to use for storage access
+ * @expires: time span in jiffies after which to abort request
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start_timeout_key(struct ccw_device *cdev, struct tcw *tcw,
+ unsigned long intparm, u8 lpm, u8 key,
+ int expires)
+{
+ int ret;
+
+ ccw_device_set_timeout(cdev, expires);
+ ret = ccw_device_tm_start_key(cdev, tcw, intparm, lpm, key);
+ if (ret != 0)
+ ccw_device_set_timeout(cdev, 0);
+ return ret;
+}
+EXPORT_SYMBOL(ccw_device_tm_start_timeout_key);
+
+/**
+ * ccw_device_tm_start - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start(struct ccw_device *cdev, struct tcw *tcw,
+ unsigned long intparm, u8 lpm)
+{
+ return ccw_device_tm_start_key(cdev, tcw, intparm, lpm,
+ PAGE_DEFAULT_KEY);
+}
+EXPORT_SYMBOL(ccw_device_tm_start);
+
+/**
+ * ccw_device_tm_start_timeout - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ * @expires: time span in jiffies after which to abort request
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start_timeout(struct ccw_device *cdev, struct tcw *tcw,
+ unsigned long intparm, u8 lpm, int expires)
+{
+ return ccw_device_tm_start_timeout_key(cdev, tcw, intparm, lpm,
+ PAGE_DEFAULT_KEY, expires);
+}
+EXPORT_SYMBOL(ccw_device_tm_start_timeout);
+
+/**
+ * ccw_device_tm_intrg - perform interrogate function
+ * @cdev: ccw device on which to perform the interrogate function
+ *
+ * Perform an interrogate function on the given ccw device. Return zero on
+ * success, non-zero otherwise.
+ */
+int ccw_device_tm_intrg(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+ if (cdev->private->state != DEV_STATE_ONLINE)
+ return -EIO;
+ if (!scsw_is_tm(&sch->schib.scsw) ||
+ !(scsw_actl(&sch->schib.scsw) | SCSW_ACTL_START_PEND))
+ return -EINVAL;
+ return cio_tm_intrg(sch);
+}
+EXPORT_SYMBOL(ccw_device_tm_intrg);
+
// FIXME: these have to go:
int
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 5cf7be008e9..86bc94eb607 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -28,13 +28,13 @@
* Helper function called from interrupt context to decide whether an
* operation should be tried again.
*/
-static int __ccw_device_should_retry(struct scsw *scsw)
+static int __ccw_device_should_retry(union scsw *scsw)
{
/* CC is only valid if start function bit is set. */
- if ((scsw->fctl & SCSW_FCTL_START_FUNC) && scsw->cc == 1)
+ if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1)
return 1;
/* No more activity. For sense and set PGID we stubbornly try again. */
- if (!scsw->actl)
+ if (!scsw->cmd.actl)
return 1;
return 0;
}
@@ -125,7 +125,7 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
irb = &cdev->private->irb;
- if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry Sense PGID if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
@@ -155,10 +155,10 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
irb->ecw[6], irb->ecw[7]);
return -EAGAIN;
}
- if (irb->scsw.cc == 3) {
+ if (irb->scsw.cmd.cc == 3) {
u8 lpm;
- lpm = to_io_private(sch)->orb.lpm;
+ lpm = to_io_private(sch)->orb.cmd.lpm;
CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x,"
" lpm %02X, became 'not operational'\n",
cdev->private->dev_id.devno, sch->schid.ssid,
@@ -188,7 +188,7 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
irb = (struct irb *) __LC_IRB;
- if (irb->scsw.stctl ==
+ if (irb->scsw.cmd.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
if (__ccw_device_should_retry(&irb->scsw)) {
ret = __ccw_device_sense_pgid_start(cdev);
@@ -331,7 +331,7 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
irb = &cdev->private->irb;
- if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry Set PGID if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
@@ -355,7 +355,7 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
irb->ecw[6], irb->ecw[7]);
return -EAGAIN;
}
- if (irb->scsw.cc == 3) {
+ if (irb->scsw.cmd.cc == 3) {
CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x,"
" lpm %02X, became 'not operational'\n",
cdev->private->dev_id.devno, sch->schid.ssid,
@@ -376,7 +376,7 @@ static int __ccw_device_check_nop(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
irb = &cdev->private->irb;
- if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry NOP if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
@@ -384,7 +384,7 @@ static int __ccw_device_check_nop(struct ccw_device *cdev)
}
return -ETIME;
}
- if (irb->scsw.cc == 3) {
+ if (irb->scsw.cmd.cc == 3) {
CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x,"
" lpm %02X, became 'not operational'\n",
cdev->private->dev_id.devno, sch->schid.ssid,
@@ -438,7 +438,7 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
irb = (struct irb *) __LC_IRB;
- if (irb->scsw.stctl ==
+ if (irb->scsw.cmd.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
if (__ccw_device_should_retry(&irb->scsw))
__ccw_device_verify_start(cdev);
@@ -544,7 +544,7 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
irb = (struct irb *) __LC_IRB;
- if (irb->scsw.stctl ==
+ if (irb->scsw.cmd.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
if (__ccw_device_should_retry(&irb->scsw))
__ccw_device_disband_start(cdev);
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c
index 4a38993000f..1b03c5423be 100644
--- a/drivers/s390/cio/device_status.c
+++ b/drivers/s390/cio/device_status.c
@@ -29,9 +29,11 @@
static void
ccw_device_msg_control_check(struct ccw_device *cdev, struct irb *irb)
{
- if (!(irb->scsw.cstat & (SCHN_STAT_CHN_DATA_CHK |
- SCHN_STAT_CHN_CTRL_CHK |
- SCHN_STAT_INTF_CTRL_CHK)))
+ char dbf_text[15];
+
+ if (!scsw_is_valid_cstat(&irb->scsw) ||
+ !(scsw_cstat(&irb->scsw) & (SCHN_STAT_CHN_DATA_CHK |
+ SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK)))
return;
CIO_MSG_EVENT(0, "Channel-Check or Interface-Control-Check "
"received"
@@ -39,15 +41,10 @@ ccw_device_msg_control_check(struct ccw_device *cdev, struct irb *irb)
": %02X sch_stat : %02X\n",
cdev->private->dev_id.devno, cdev->private->schid.ssid,
cdev->private->schid.sch_no,
- irb->scsw.dstat, irb->scsw.cstat);
-
- if (irb->scsw.cc != 3) {
- char dbf_text[15];
-
- sprintf(dbf_text, "chk%x", cdev->private->schid.sch_no);
- CIO_TRACE_EVENT(0, dbf_text);
- CIO_HEX_EVENT(0, irb, sizeof (struct irb));
- }
+ scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw));
+ sprintf(dbf_text, "chk%x", cdev->private->schid.sch_no);
+ CIO_TRACE_EVENT(0, dbf_text);
+ CIO_HEX_EVENT(0, irb, sizeof(struct irb));
}
/*
@@ -81,12 +78,12 @@ ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb)
* are condition that have to be met for the extended control
* bit to have meaning. Sick.
*/
- cdev->private->irb.scsw.ectl = 0;
- if ((irb->scsw.stctl & SCSW_STCTL_ALERT_STATUS) &&
- !(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS))
- cdev->private->irb.scsw.ectl = irb->scsw.ectl;
+ cdev->private->irb.scsw.cmd.ectl = 0;
+ if ((irb->scsw.cmd.stctl & SCSW_STCTL_ALERT_STATUS) &&
+ !(irb->scsw.cmd.stctl & SCSW_STCTL_INTER_STATUS))
+ cdev->private->irb.scsw.cmd.ectl = irb->scsw.cmd.ectl;
/* Check if extended control word is valid. */
- if (!cdev->private->irb.scsw.ectl)
+ if (!cdev->private->irb.scsw.cmd.ectl)
return;
/* Copy concurrent sense / model dependent information. */
memcpy (&cdev->private->irb.ecw, irb->ecw, sizeof (irb->ecw));
@@ -98,11 +95,12 @@ ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb)
static int
ccw_device_accumulate_esw_valid(struct irb *irb)
{
- if (!irb->scsw.eswf && irb->scsw.stctl == SCSW_STCTL_STATUS_PEND)
+ if (!irb->scsw.cmd.eswf &&
+ (irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND))
return 0;
- if (irb->scsw.stctl ==
- (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) &&
- !(irb->scsw.actl & SCSW_ACTL_SUSPENDED))
+ if (irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) &&
+ !(irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED))
return 0;
return 1;
}
@@ -125,7 +123,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb)
cdev_irb->esw.esw1.lpum = irb->esw.esw1.lpum;
/* Copy subchannel logout information if esw is of format 0. */
- if (irb->scsw.eswf) {
+ if (irb->scsw.cmd.eswf) {
cdev_sublog = &cdev_irb->esw.esw0.sublog;
sublog = &irb->esw.esw0.sublog;
/* Copy extended status flags. */
@@ -134,7 +132,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb)
* Copy fields that have a meaning for channel data check
* channel control check and interface control check.
*/
- if (irb->scsw.cstat & (SCHN_STAT_CHN_DATA_CHK |
+ if (irb->scsw.cmd.cstat & (SCHN_STAT_CHN_DATA_CHK |
SCHN_STAT_CHN_CTRL_CHK |
SCHN_STAT_INTF_CTRL_CHK)) {
/* Copy ancillary report bit. */
@@ -155,7 +153,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb)
/* Copy i/o-error alert. */
cdev_sublog->ioerr = sublog->ioerr;
/* Copy channel path timeout bit. */
- if (irb->scsw.cstat & SCHN_STAT_INTF_CTRL_CHK)
+ if (irb->scsw.cmd.cstat & SCHN_STAT_INTF_CTRL_CHK)
cdev_irb->esw.esw0.erw.cpt = irb->esw.esw0.erw.cpt;
/* Copy failing storage address validity flag. */
cdev_irb->esw.esw0.erw.fsavf = irb->esw.esw0.erw.fsavf;
@@ -200,24 +198,24 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb)
* If not, the remaining bit have no meaning and we must ignore them.
* The esw is not meaningful as well...
*/
- if (!(irb->scsw.stctl & SCSW_STCTL_STATUS_PEND))
+ if (!(scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))
return;
/* Check for channel checks and interface control checks. */
ccw_device_msg_control_check(cdev, irb);
/* Check for path not operational. */
- if (irb->scsw.pno && irb->scsw.fctl != 0 &&
- (!(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS) ||
- (irb->scsw.actl & SCSW_ACTL_SUSPENDED)))
+ if (scsw_is_valid_pno(&irb->scsw) && scsw_pno(&irb->scsw))
ccw_device_path_notoper(cdev);
-
+ /* No irb accumulation for transport mode irbs. */
+ if (scsw_is_tm(&irb->scsw)) {
+ memcpy(&cdev->private->irb, irb, sizeof(struct irb));
+ return;
+ }
/*
* Don't accumulate unsolicited interrupts.
*/
- if ((irb->scsw.stctl ==
- (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
- (!irb->scsw.cc))
+ if (!scsw_is_solicited(&irb->scsw))
return;
cdev_irb = &cdev->private->irb;
@@ -227,62 +225,63 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb)
* status at the subchannel has been cleared and we must not pass
* intermediate accumulated status to the device driver.
*/
- if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC)
+ if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC)
memset(&cdev->private->irb, 0, sizeof(struct irb));
/* Copy bits which are valid only for the start function. */
- if (irb->scsw.fctl & SCSW_FCTL_START_FUNC) {
+ if (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) {
/* Copy key. */
- cdev_irb->scsw.key = irb->scsw.key;
+ cdev_irb->scsw.cmd.key = irb->scsw.cmd.key;
/* Copy suspend control bit. */
- cdev_irb->scsw.sctl = irb->scsw.sctl;
+ cdev_irb->scsw.cmd.sctl = irb->scsw.cmd.sctl;
/* Accumulate deferred condition code. */
- cdev_irb->scsw.cc |= irb->scsw.cc;
+ cdev_irb->scsw.cmd.cc |= irb->scsw.cmd.cc;
/* Copy ccw format bit. */
- cdev_irb->scsw.fmt = irb->scsw.fmt;
+ cdev_irb->scsw.cmd.fmt = irb->scsw.cmd.fmt;
/* Copy prefetch bit. */
- cdev_irb->scsw.pfch = irb->scsw.pfch;
+ cdev_irb->scsw.cmd.pfch = irb->scsw.cmd.pfch;
/* Copy initial-status-interruption-control. */
- cdev_irb->scsw.isic = irb->scsw.isic;
+ cdev_irb->scsw.cmd.isic = irb->scsw.cmd.isic;
/* Copy address limit checking control. */
- cdev_irb->scsw.alcc = irb->scsw.alcc;
+ cdev_irb->scsw.cmd.alcc = irb->scsw.cmd.alcc;
/* Copy suppress suspend bit. */
- cdev_irb->scsw.ssi = irb->scsw.ssi;
+ cdev_irb->scsw.cmd.ssi = irb->scsw.cmd.ssi;
}
/* Take care of the extended control bit and extended control word. */
ccw_device_accumulate_ecw(cdev, irb);
/* Accumulate function control. */
- cdev_irb->scsw.fctl |= irb->scsw.fctl;
+ cdev_irb->scsw.cmd.fctl |= irb->scsw.cmd.fctl;
/* Copy activity control. */
- cdev_irb->scsw.actl= irb->scsw.actl;
+ cdev_irb->scsw.cmd.actl = irb->scsw.cmd.actl;
/* Accumulate status control. */
- cdev_irb->scsw.stctl |= irb->scsw.stctl;
+ cdev_irb->scsw.cmd.stctl |= irb->scsw.cmd.stctl;
/*
* Copy ccw address if it is valid. This is a bit simplified
* but should be close enough for all practical purposes.
*/
- if ((irb->scsw.stctl & SCSW_STCTL_PRIM_STATUS) ||
- ((irb->scsw.stctl ==
+ if ((irb->scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) ||
+ ((irb->scsw.cmd.stctl ==
(SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND)) &&
- (irb->scsw.actl & SCSW_ACTL_DEVACT) &&
- (irb->scsw.actl & SCSW_ACTL_SCHACT)) ||
- (irb->scsw.actl & SCSW_ACTL_SUSPENDED))
- cdev_irb->scsw.cpa = irb->scsw.cpa;
+ (irb->scsw.cmd.actl & SCSW_ACTL_DEVACT) &&
+ (irb->scsw.cmd.actl & SCSW_ACTL_SCHACT)) ||
+ (irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED))
+ cdev_irb->scsw.cmd.cpa = irb->scsw.cmd.cpa;
/* Accumulate device status, but not the device busy flag. */
- cdev_irb->scsw.dstat &= ~DEV_STAT_BUSY;
+ cdev_irb->scsw.cmd.dstat &= ~DEV_STAT_BUSY;
/* dstat is not always valid. */
- if (irb->scsw.stctl &
+ if (irb->scsw.cmd.stctl &
(SCSW_STCTL_PRIM_STATUS | SCSW_STCTL_SEC_STATUS
| SCSW_STCTL_INTER_STATUS | SCSW_STCTL_ALERT_STATUS))
- cdev_irb->scsw.dstat |= irb->scsw.dstat;
+ cdev_irb->scsw.cmd.dstat |= irb->scsw.cmd.dstat;
/* Accumulate subchannel status. */
- cdev_irb->scsw.cstat |= irb->scsw.cstat;
+ cdev_irb->scsw.cmd.cstat |= irb->scsw.cmd.cstat;
/* Copy residual count if it is valid. */
- if ((irb->scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&
- (irb->scsw.cstat & ~(SCHN_STAT_PCI | SCHN_STAT_INCORR_LEN)) == 0)
- cdev_irb->scsw.count = irb->scsw.count;
+ if ((irb->scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) &&
+ (irb->scsw.cmd.cstat & ~(SCHN_STAT_PCI | SCHN_STAT_INCORR_LEN))
+ == 0)
+ cdev_irb->scsw.cmd.count = irb->scsw.cmd.count;
/* Take care of bits in the extended status word. */
ccw_device_accumulate_esw(cdev, irb);
@@ -299,7 +298,7 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb)
* sense facility available/supported when enabling the
* concurrent sense facility.
*/
- if ((cdev_irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
+ if ((cdev_irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
!(cdev_irb->esw.esw0.erw.cons))
cdev->private->flags.dosense = 1;
}
@@ -317,7 +316,7 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
sch = to_subchannel(cdev->dev.parent);
/* A sense is required, can we do it now ? */
- if ((irb->scsw.actl & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0)
+ if (scsw_actl(&irb->scsw) & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT))
/*
* we received an Unit Check but we have no final
* status yet, therefore we must delay the SENSE
@@ -355,20 +354,18 @@ ccw_device_accumulate_basic_sense(struct ccw_device *cdev, struct irb *irb)
* If not, the remaining bit have no meaning and we must ignore them.
* The esw is not meaningful as well...
*/
- if (!(irb->scsw.stctl & SCSW_STCTL_STATUS_PEND))
+ if (!(scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))
return;
/* Check for channel checks and interface control checks. */
ccw_device_msg_control_check(cdev, irb);
/* Check for path not operational. */
- if (irb->scsw.pno && irb->scsw.fctl != 0 &&
- (!(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS) ||
- (irb->scsw.actl & SCSW_ACTL_SUSPENDED)))
+ if (scsw_is_valid_pno(&irb->scsw) && scsw_pno(&irb->scsw))
ccw_device_path_notoper(cdev);
- if (!(irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
- (irb->scsw.dstat & DEV_STAT_CHN_END)) {
+ if (!(irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
+ (irb->scsw.cmd.dstat & DEV_STAT_CHN_END)) {
cdev->private->irb.esw.esw0.erw.cons = 1;
cdev->private->flags.dosense = 0;
}
@@ -386,11 +383,11 @@ int
ccw_device_accumulate_and_sense(struct ccw_device *cdev, struct irb *irb)
{
ccw_device_accumulate_irb(cdev, irb);
- if ((irb->scsw.actl & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0)
+ if ((irb->scsw.cmd.actl & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0)
return -EBUSY;
/* Check for basic sense. */
if (cdev->private->flags.dosense &&
- !(irb->scsw.dstat & DEV_STAT_UNIT_CHECK)) {
+ !(irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)) {
cdev->private->irb.esw.esw0.erw.cons = 1;
cdev->private->flags.dosense = 0;
return 0;
diff --git a/drivers/s390/cio/fcx.c b/drivers/s390/cio/fcx.c
new file mode 100644
index 00000000000..61677dfbdc9
--- /dev/null
+++ b/drivers/s390/cio/fcx.c
@@ -0,0 +1,350 @@
+/*
+ * Functions for assembling fcx enabled I/O control blocks.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <asm/fcx.h>
+#include "cio.h"
+
+/**
+ * tcw_get_intrg - return pointer to associated interrogate tcw
+ * @tcw: pointer to the original tcw
+ *
+ * Return a pointer to the interrogate tcw associated with the specified tcw
+ * or %NULL if there is no associated interrogate tcw.
+ */
+struct tcw *tcw_get_intrg(struct tcw *tcw)
+{
+ return (struct tcw *) ((addr_t) tcw->intrg);
+}
+EXPORT_SYMBOL(tcw_get_intrg);
+
+/**
+ * tcw_get_data - return pointer to input/output data associated with tcw
+ * @tcw: pointer to the tcw
+ *
+ * Return the input or output data address specified in the tcw depending
+ * on whether the r-bit or the w-bit is set. If neither bit is set, return
+ * %NULL.
+ */
+void *tcw_get_data(struct tcw *tcw)
+{
+ if (tcw->r)
+ return (void *) ((addr_t) tcw->input);
+ if (tcw->w)
+ return (void *) ((addr_t) tcw->output);
+ return NULL;
+}
+EXPORT_SYMBOL(tcw_get_data);
+
+/**
+ * tcw_get_tccb - return pointer to tccb associated with tcw
+ * @tcw: pointer to the tcw
+ *
+ * Return pointer to the tccb associated with this tcw.
+ */
+struct tccb *tcw_get_tccb(struct tcw *tcw)
+{
+ return (struct tccb *) ((addr_t) tcw->tccb);
+}
+EXPORT_SYMBOL(tcw_get_tccb);
+
+/**
+ * tcw_get_tsb - return pointer to tsb associated with tcw
+ * @tcw: pointer to the tcw
+ *
+ * Return pointer to the tsb associated with this tcw.
+ */
+struct tsb *tcw_get_tsb(struct tcw *tcw)
+{
+ return (struct tsb *) ((addr_t) tcw->tsb);
+}
+EXPORT_SYMBOL(tcw_get_tsb);
+
+/**
+ * tcw_init - initialize tcw data structure
+ * @tcw: pointer to the tcw to be initialized
+ * @r: initial value of the r-bit
+ * @w: initial value of the w-bit
+ *
+ * Initialize all fields of the specified tcw data structure with zero and
+ * fill in the format, flags, r and w fields.
+ */
+void tcw_init(struct tcw *tcw, int r, int w)
+{
+ memset(tcw, 0, sizeof(struct tcw));
+ tcw->format = TCW_FORMAT_DEFAULT;
+ tcw->flags = TCW_FLAGS_TIDAW_FORMAT(TCW_TIDAW_FORMAT_DEFAULT);
+ if (r)
+ tcw->r = 1;
+ if (w)
+ tcw->w = 1;
+}
+EXPORT_SYMBOL(tcw_init);
+
+static inline size_t tca_size(struct tccb *tccb)
+{
+ return tccb->tcah.tcal - 12;
+}
+
+static u32 calc_dcw_count(struct tccb *tccb)
+{
+ int offset;
+ struct dcw *dcw;
+ u32 count = 0;
+ size_t size;
+
+ size = tca_size(tccb);
+ for (offset = 0; offset < size;) {
+ dcw = (struct dcw *) &tccb->tca[offset];
+ count += dcw->count;
+ if (!(dcw->flags & DCW_FLAGS_CC))
+ break;
+ offset += sizeof(struct dcw) + ALIGN((int) dcw->cd_count, 4);
+ }
+ return count;
+}
+
+static u32 calc_cbc_size(struct tidaw *tidaw, int num)
+{
+ int i;
+ u32 cbc_data;
+ u32 cbc_count = 0;
+ u64 data_count = 0;
+
+ for (i = 0; i < num; i++) {
+ if (tidaw[i].flags & TIDAW_FLAGS_LAST)
+ break;
+ /* TODO: find out if padding applies to total of data
+ * transferred or data transferred by this tidaw. Assumption:
+ * applies to total. */
+ data_count += tidaw[i].count;
+ if (tidaw[i].flags & TIDAW_FLAGS_INSERT_CBC) {
+ cbc_data = 4 + ALIGN(data_count, 4) - data_count;
+ cbc_count += cbc_data;
+ data_count += cbc_data;
+ }
+ }
+ return cbc_count;
+}
+
+/**
+ * tcw_finalize - finalize tcw length fields and tidaw list
+ * @tcw: pointer to the tcw
+ * @num_tidaws: the number of tidaws used to address input/output data or zero
+ * if no tida is used
+ *
+ * Calculate the input-/output-count and tccbl field in the tcw, add a
+ * tcat the tccb and terminate the data tidaw list if used.
+ *
+ * Note: in case input- or output-tida is used, the tidaw-list must be stored
+ * in contiguous storage (no ttic). The tcal field in the tccb must be
+ * up-to-date.
+ */
+void tcw_finalize(struct tcw *tcw, int num_tidaws)
+{
+ struct tidaw *tidaw;
+ struct tccb *tccb;
+ struct tccb_tcat *tcat;
+ u32 count;
+
+ /* Terminate tidaw list. */
+ tidaw = tcw_get_data(tcw);
+ if (num_tidaws > 0)
+ tidaw[num_tidaws - 1].flags |= TIDAW_FLAGS_LAST;
+ /* Add tcat to tccb. */
+ tccb = tcw_get_tccb(tcw);
+ tcat = (struct tccb_tcat *) &tccb->tca[tca_size(tccb)];
+ memset(tcat, 0, sizeof(tcat));
+ /* Calculate tcw input/output count and tcat transport count. */
+ count = calc_dcw_count(tccb);
+ if (tcw->w && (tcw->flags & TCW_FLAGS_OUTPUT_TIDA))
+ count += calc_cbc_size(tidaw, num_tidaws);
+ if (tcw->r)
+ tcw->input_count = count;
+ else if (tcw->w)
+ tcw->output_count = count;
+ tcat->count = ALIGN(count, 4) + 4;
+ /* Calculate tccbl. */
+ tcw->tccbl = (sizeof(struct tccb) + tca_size(tccb) +
+ sizeof(struct tccb_tcat) - 20) >> 2;
+}
+EXPORT_SYMBOL(tcw_finalize);
+
+/**
+ * tcw_set_intrg - set the interrogate tcw address of a tcw
+ * @tcw: the tcw address
+ * @intrg_tcw: the address of the interrogate tcw
+ *
+ * Set the address of the interrogate tcw in the specified tcw.
+ */
+void tcw_set_intrg(struct tcw *tcw, struct tcw *intrg_tcw)
+{
+ tcw->intrg = (u32) ((addr_t) intrg_tcw);
+}
+EXPORT_SYMBOL(tcw_set_intrg);
+
+/**
+ * tcw_set_data - set data address and tida flag of a tcw
+ * @tcw: the tcw address
+ * @data: the data address
+ * @use_tidal: zero of the data address specifies a contiguous block of data,
+ * non-zero if it specifies a list if tidaws.
+ *
+ * Set the input/output data address of a tcw (depending on the value of the
+ * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag
+ * is set as well.
+ */
+void tcw_set_data(struct tcw *tcw, void *data, int use_tidal)
+{
+ if (tcw->r) {
+ tcw->input = (u64) ((addr_t) data);
+ if (use_tidal)
+ tcw->flags |= TCW_FLAGS_INPUT_TIDA;
+ } else if (tcw->w) {
+ tcw->output = (u64) ((addr_t) data);
+ if (use_tidal)
+ tcw->flags |= TCW_FLAGS_OUTPUT_TIDA;
+ }
+}
+EXPORT_SYMBOL(tcw_set_data);
+
+/**
+ * tcw_set_tccb - set tccb address of a tcw
+ * @tcw: the tcw address
+ * @tccb: the tccb address
+ *
+ * Set the address of the tccb in the specified tcw.
+ */
+void tcw_set_tccb(struct tcw *tcw, struct tccb *tccb)
+{
+ tcw->tccb = (u64) ((addr_t) tccb);
+}
+EXPORT_SYMBOL(tcw_set_tccb);
+
+/**
+ * tcw_set_tsb - set tsb address of a tcw
+ * @tcw: the tcw address
+ * @tsb: the tsb address
+ *
+ * Set the address of the tsb in the specified tcw.
+ */
+void tcw_set_tsb(struct tcw *tcw, struct tsb *tsb)
+{
+ tcw->tsb = (u64) ((addr_t) tsb);
+}
+EXPORT_SYMBOL(tcw_set_tsb);
+
+/**
+ * tccb_init - initialize tccb
+ * @tccb: the tccb address
+ * @size: the maximum size of the tccb
+ * @sac: the service-action-code to be user
+ *
+ * Initialize the header of the specified tccb by resetting all values to zero
+ * and filling in defaults for format, sac and initial tcal fields.
+ */
+void tccb_init(struct tccb *tccb, size_t size, u32 sac)
+{
+ memset(tccb, 0, size);
+ tccb->tcah.format = TCCB_FORMAT_DEFAULT;
+ tccb->tcah.sac = sac;
+ tccb->tcah.tcal = 12;
+}
+EXPORT_SYMBOL(tccb_init);
+
+/**
+ * tsb_init - initialize tsb
+ * @tsb: the tsb address
+ *
+ * Initialize the specified tsb by resetting all values to zero.
+ */
+void tsb_init(struct tsb *tsb)
+{
+ memset(tsb, 0, sizeof(tsb));
+}
+EXPORT_SYMBOL(tsb_init);
+
+/**
+ * tccb_add_dcw - add a dcw to the tccb
+ * @tccb: the tccb address
+ * @tccb_size: the maximum tccb size
+ * @cmd: the dcw command
+ * @flags: flags for the dcw
+ * @cd: pointer to control data for this dcw or NULL if none is required
+ * @cd_count: number of control data bytes for this dcw
+ * @count: number of data bytes for this dcw
+ *
+ * Add a new dcw to the specified tccb by writing the dcw information specified
+ * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return
+ * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw
+ * would exceed the available space as defined by @tccb_size.
+ *
+ * Note: the tcal field of the tccb header will be updates to reflect added
+ * content.
+ */
+struct dcw *tccb_add_dcw(struct tccb *tccb, size_t tccb_size, u8 cmd, u8 flags,
+ void *cd, u8 cd_count, u32 count)
+{
+ struct dcw *dcw;
+ int size;
+ int tca_offset;
+
+ /* Check for space. */
+ tca_offset = tca_size(tccb);
+ size = ALIGN(sizeof(struct dcw) + cd_count, 4);
+ if (sizeof(struct tccb_tcah) + tca_offset + size +
+ sizeof(struct tccb_tcat) > tccb_size)
+ return ERR_PTR(-ENOSPC);
+ /* Add dcw to tca. */
+ dcw = (struct dcw *) &tccb->tca[tca_offset];
+ memset(dcw, 0, size);
+ dcw->cmd = cmd;
+ dcw->flags = flags;
+ dcw->count = count;
+ dcw->cd_count = cd_count;
+ if (cd)
+ memcpy(&dcw->cd[0], cd, cd_count);
+ tccb->tcah.tcal += size;
+ return dcw;
+}
+EXPORT_SYMBOL(tccb_add_dcw);
+
+/**
+ * tcw_add_tidaw - add a tidaw to a tcw
+ * @tcw: the tcw address
+ * @num_tidaws: the current number of tidaws
+ * @flags: flags for the new tidaw
+ * @addr: address value for the new tidaw
+ * @count: count value for the new tidaw
+ *
+ * Add a new tidaw to the input/output data tidaw-list of the specified tcw
+ * (depending on the value of the r-flag and w-flag) and return a pointer to
+ * the new tidaw.
+ *
+ * Note: the tidaw-list is assumed to be contiguous with no ttics. The caller
+ * must ensure that there is enough space for the new tidaw. The last-tidaw
+ * flag for the last tidaw in the list will be set by tcw_finalize.
+ */
+struct tidaw *tcw_add_tidaw(struct tcw *tcw, int num_tidaws, u8 flags,
+ void *addr, u32 count)
+{
+ struct tidaw *tidaw;
+
+ /* Add tidaw to tidaw-list. */
+ tidaw = ((struct tidaw *) tcw_get_data(tcw)) + num_tidaws;
+ memset(tidaw, 0, sizeof(struct tidaw));
+ tidaw->flags = flags;
+ tidaw->count = count;
+ tidaw->addr = (u64) ((addr_t) addr);
+ return tidaw;
+}
+EXPORT_SYMBOL(tcw_add_tidaw);
diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c
index ef7bc0a125e..cf8f24a4b5e 100644
--- a/drivers/s390/cio/idset.c
+++ b/drivers/s390/cio/idset.c
@@ -5,7 +5,7 @@
* Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
-#include <linux/slab.h>
+#include <linux/vmalloc.h>
#include <linux/bitops.h>
#include "idset.h"
#include "css.h"
@@ -25,18 +25,18 @@ static struct idset *idset_new(int num_ssid, int num_id)
{
struct idset *set;
- set = kzalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id),
- GFP_KERNEL);
+ set = vmalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id));
if (set) {
set->num_ssid = num_ssid;
set->num_id = num_id;
+ memset(set->bitmap, 0, bitmap_size(num_ssid, num_id));
}
return set;
}
void idset_free(struct idset *set)
{
- kfree(set);
+ vfree(set);
}
void idset_clear(struct idset *set)
diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h
index 144466ab8c1..528065cb502 100644
--- a/drivers/s390/cio/idset.h
+++ b/drivers/s390/cio/idset.h
@@ -8,7 +8,7 @@
#ifndef S390_IDSET_H
#define S390_IDSET_H S390_IDSET_H
-#include "schid.h"
+#include <asm/schid.h>
struct idset;
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index 8c613160bfc..c4f3e7c9a85 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -1,12 +1,12 @@
#ifndef S390_IO_SCH_H
#define S390_IO_SCH_H
-#include "schid.h"
+#include <asm/schid.h>
/*
- * operation request block
+ * command-mode operation request block
*/
-struct orb {
+struct cmd_orb {
u32 intparm; /* interruption parameter */
u32 key : 4; /* flags, like key, suspend control, etc. */
u32 spnd : 1; /* suspend control */
@@ -28,8 +28,36 @@ struct orb {
u32 cpa; /* channel program address */
} __attribute__ ((packed, aligned(4)));
+/*
+ * transport-mode operation request block
+ */
+struct tm_orb {
+ u32 intparm;
+ u32 key:4;
+ u32 :9;
+ u32 b:1;
+ u32 :2;
+ u32 lpm:8;
+ u32 :7;
+ u32 x:1;
+ u32 tcw;
+ u32 prio:8;
+ u32 :8;
+ u32 rsvpgm:8;
+ u32 :8;
+ u32 :32;
+ u32 :32;
+ u32 :32;
+ u32 :32;
+} __attribute__ ((packed, aligned(4)));
+
+union orb {
+ struct cmd_orb cmd;
+ struct tm_orb tm;
+} __attribute__ ((packed, aligned(4)));
+
struct io_subchannel_private {
- struct orb orb; /* operation request block */
+ union orb orb; /* operation request block */
struct ccw1 sense_ccw; /* static ccw for sense command */
} __attribute__ ((aligned(8)));
@@ -95,16 +123,20 @@ struct ccw_device_private {
void *cmb_wait; /* deferred cmb enable/disable */
};
-static inline int ssch(struct subchannel_id schid, volatile struct orb *addr)
+static inline int ssch(struct subchannel_id schid, union orb *addr)
{
register struct subchannel_id reg1 asm("1") = schid;
- int ccode;
+ int ccode = -EIO;
asm volatile(
" ssch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (ccode)
+ : "d" (reg1), "a" (addr), "m" (*addr)
+ : "cc", "memory");
return ccode;
}
@@ -117,7 +149,9 @@ static inline int rsch(struct subchannel_id schid)
" rsch\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc", "memory");
return ccode;
}
@@ -130,7 +164,9 @@ static inline int csch(struct subchannel_id schid)
" csch\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc");
return ccode;
}
@@ -143,7 +179,9 @@ static inline int hsch(struct subchannel_id schid)
" hsch\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc");
return ccode;
}
@@ -156,7 +194,9 @@ static inline int xsch(struct subchannel_id schid)
" .insn rre,0xb2760000,%1,0\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc");
return ccode;
}
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 652ea3625f9..75926279263 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -2,7 +2,7 @@
#define S390_CIO_IOASM_H
#include <asm/chpid.h>
-#include "schid.h"
+#include <asm/schid.h>
/*
* TPI info structure
@@ -23,38 +23,39 @@ struct tpi_info {
* Some S390 specific IO instructions as inline
*/
-static inline int stsch(struct subchannel_id schid,
- volatile struct schib *addr)
+static inline int stsch(struct subchannel_id schid, struct schib *addr)
{
register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
asm volatile(
- " stsch 0(%2)\n"
+ " stsch 0(%3)\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ : "=d" (ccode), "=m" (*addr)
+ : "d" (reg1), "a" (addr)
+ : "cc");
return ccode;
}
-static inline int stsch_err(struct subchannel_id schid,
- volatile struct schib *addr)
+static inline int stsch_err(struct subchannel_id schid, struct schib *addr)
{
register struct subchannel_id reg1 asm ("1") = schid;
int ccode = -EIO;
asm volatile(
- " stsch 0(%2)\n"
+ " stsch 0(%3)\n"
"0: ipm %0\n"
" srl %0,28\n"
"1:\n"
EX_TABLE(0b,1b)
- : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ : "+d" (ccode), "=m" (*addr)
+ : "d" (reg1), "a" (addr)
+ : "cc");
return ccode;
}
-static inline int msch(struct subchannel_id schid,
- volatile struct schib *addr)
+static inline int msch(struct subchannel_id schid, struct schib *addr)
{
register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
@@ -63,12 +64,13 @@ static inline int msch(struct subchannel_id schid,
" msch 0(%2)\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ : "=d" (ccode)
+ : "d" (reg1), "a" (addr), "m" (*addr)
+ : "cc");
return ccode;
}
-static inline int msch_err(struct subchannel_id schid,
- volatile struct schib *addr)
+static inline int msch_err(struct subchannel_id schid, struct schib *addr)
{
register struct subchannel_id reg1 asm ("1") = schid;
int ccode = -EIO;
@@ -79,33 +81,38 @@ static inline int msch_err(struct subchannel_id schid,
" srl %0,28\n"
"1:\n"
EX_TABLE(0b,1b)
- : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ : "+d" (ccode)
+ : "d" (reg1), "a" (addr), "m" (*addr)
+ : "cc");
return ccode;
}
-static inline int tsch(struct subchannel_id schid,
- volatile struct irb *addr)
+static inline int tsch(struct subchannel_id schid, struct irb *addr)
{
register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
asm volatile(
- " tsch 0(%2)\n"
+ " tsch 0(%3)\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ : "=d" (ccode), "=m" (*addr)
+ : "d" (reg1), "a" (addr)
+ : "cc");
return ccode;
}
-static inline int tpi( volatile struct tpi_info *addr)
+static inline int tpi(struct tpi_info *addr)
{
int ccode;
asm volatile(
- " tpi 0(%1)\n"
+ " tpi 0(%2)\n"
" ipm %0\n"
" srl %0,28"
- : "=d" (ccode) : "a" (addr), "m" (*addr) : "cc");
+ : "=d" (ccode), "=m" (*addr)
+ : "a" (addr)
+ : "cc");
return ccode;
}
diff --git a/drivers/s390/cio/isc.c b/drivers/s390/cio/isc.c
new file mode 100644
index 00000000000..c592087be0f
--- /dev/null
+++ b/drivers/s390/cio/isc.c
@@ -0,0 +1,68 @@
+/*
+ * Functions for registration of I/O interruption subclasses on s390.
+ *
+ * Copyright IBM Corp. 2008
+ * Authors: Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <asm/isc.h>
+
+static unsigned int isc_refs[MAX_ISC + 1];
+static DEFINE_SPINLOCK(isc_ref_lock);
+
+
+/**
+ * isc_register - register an I/O interruption subclass.
+ * @isc: I/O interruption subclass to register
+ *
+ * The number of users for @isc is increased. If this is the first user to
+ * register @isc, the corresponding I/O interruption subclass mask is enabled.
+ *
+ * Context:
+ * This function must not be called in interrupt context.
+ */
+void isc_register(unsigned int isc)
+{
+ if (isc > MAX_ISC) {
+ WARN_ON(1);
+ return;
+ }
+
+ spin_lock(&isc_ref_lock);
+ if (isc_refs[isc] == 0)
+ ctl_set_bit(6, 31 - isc);
+ isc_refs[isc]++;
+ spin_unlock(&isc_ref_lock);
+}
+EXPORT_SYMBOL_GPL(isc_register);
+
+/**
+ * isc_unregister - unregister an I/O interruption subclass.
+ * @isc: I/O interruption subclass to unregister
+ *
+ * The number of users for @isc is decreased. If this is the last user to
+ * unregister @isc, the corresponding I/O interruption subclass mask is
+ * disabled.
+ * Note: This function must not be called if isc_register() hasn't been called
+ * before by the driver for @isc.
+ *
+ * Context:
+ * This function must not be called in interrupt context.
+ */
+void isc_unregister(unsigned int isc)
+{
+ spin_lock(&isc_ref_lock);
+ /* check for misuse */
+ if (isc > MAX_ISC || isc_refs[isc] == 0) {
+ WARN_ON(1);
+ goto out_unlock;
+ }
+ if (isc_refs[isc] == 1)
+ ctl_clear_bit(6, 31 - isc);
+ isc_refs[isc]--;
+out_unlock:
+ spin_unlock(&isc_ref_lock);
+}
+EXPORT_SYMBOL_GPL(isc_unregister);
diff --git a/drivers/s390/cio/itcw.c b/drivers/s390/cio/itcw.c
new file mode 100644
index 00000000000..17da9ab932e
--- /dev/null
+++ b/drivers/s390/cio/itcw.c
@@ -0,0 +1,327 @@
+/*
+ * Functions for incremental construction of fcx enabled I/O control blocks.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <asm/fcx.h>
+#include <asm/itcw.h>
+
+/**
+ * struct itcw - incremental tcw helper data type
+ *
+ * This structure serves as a handle for the incremental construction of a
+ * tcw and associated tccb, tsb, data tidaw-list plus an optional interrogate
+ * tcw and associated data. The data structures are contained inside a single
+ * contiguous buffer provided by the user.
+ *
+ * The itcw construction functions take care of overall data integrity:
+ * - reset unused fields to zero
+ * - fill in required pointers
+ * - ensure required alignment for data structures
+ * - prevent data structures to cross 4k-byte boundary where required
+ * - calculate tccb-related length fields
+ * - optionally provide ready-made interrogate tcw and associated structures
+ *
+ * Restrictions apply to the itcws created with these construction functions:
+ * - tida only supported for data address, not for tccb
+ * - only contiguous tidaw-lists (no ttic)
+ * - total number of bytes required per itcw may not exceed 4k bytes
+ * - either read or write operation (may not work with r=0 and w=0)
+ *
+ * Example:
+ * struct itcw *itcw;
+ * void *buffer;
+ * size_t size;
+ *
+ * size = itcw_calc_size(1, 2, 0);
+ * buffer = kmalloc(size, GFP_DMA);
+ * if (!buffer)
+ * return -ENOMEM;
+ * itcw = itcw_init(buffer, size, ITCW_OP_READ, 1, 2, 0);
+ * if (IS_ERR(itcw))
+ * return PTR_ER(itcw);
+ * itcw_add_dcw(itcw, 0x2, 0, NULL, 0, 72);
+ * itcw_add_tidaw(itcw, 0, 0x30000, 20);
+ * itcw_add_tidaw(itcw, 0, 0x40000, 52);
+ * itcw_finalize(itcw);
+ *
+ */
+struct itcw {
+ struct tcw *tcw;
+ struct tcw *intrg_tcw;
+ int num_tidaws;
+ int max_tidaws;
+ int intrg_num_tidaws;
+ int intrg_max_tidaws;
+};
+
+/**
+ * itcw_get_tcw - return pointer to tcw associated with the itcw
+ * @itcw: address of the itcw
+ *
+ * Return pointer to the tcw associated with the itcw.
+ */
+struct tcw *itcw_get_tcw(struct itcw *itcw)
+{
+ return itcw->tcw;
+}
+EXPORT_SYMBOL(itcw_get_tcw);
+
+/**
+ * itcw_calc_size - return the size of an itcw with the given parameters
+ * @intrg: if non-zero, add an interrogate tcw
+ * @max_tidaws: maximum number of tidaws to be used for data addressing or zero
+ * if no tida is to be used.
+ * @intrg_max_tidaws: maximum number of tidaws to be used for data addressing
+ * by the interrogate tcw, if specified
+ *
+ * Calculate and return the number of bytes required to hold an itcw with the
+ * given parameters and assuming tccbs with maximum size.
+ *
+ * Note that the resulting size also contains bytes needed for alignment
+ * padding as well as padding to ensure that data structures don't cross a
+ * 4k-boundary where required.
+ */
+size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws)
+{
+ size_t len;
+
+ /* Main data. */
+ len = sizeof(struct itcw);
+ len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE +
+ /* TSB */ sizeof(struct tsb) +
+ /* TIDAL */ max_tidaws * sizeof(struct tidaw);
+ /* Interrogate data. */
+ if (intrg) {
+ len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE +
+ /* 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;
+ return len;
+}
+EXPORT_SYMBOL(itcw_calc_size);
+
+#define CROSS4K(x, l) (((x) & ~4095) != ((x + l) & ~4095))
+
+static inline void *fit_chunk(addr_t *start, addr_t end, size_t len,
+ int align, int check_4k)
+{
+ addr_t addr;
+
+ addr = ALIGN(*start, align);
+ if (check_4k && CROSS4K(addr, len)) {
+ addr = ALIGN(addr, 4096);
+ addr = ALIGN(addr, align);
+ }
+ if (addr + len > end)
+ return ERR_PTR(-ENOSPC);
+ *start = addr + len;
+ return (void *) addr;
+}
+
+/**
+ * itcw_init - initialize incremental tcw data structure
+ * @buffer: address of buffer to use for data structures
+ * @size: number of bytes in buffer
+ * @op: %ITCW_OP_READ for a read operation tcw, %ITCW_OP_WRITE for a write
+ * operation tcw
+ * @intrg: if non-zero, add and initialize an interrogate tcw
+ * @max_tidaws: maximum number of tidaws to be used for data addressing or zero
+ * if no tida is to be used.
+ * @intrg_max_tidaws: maximum number of tidaws to be used for data addressing
+ * by the interrogate tcw, if specified
+ *
+ * Prepare the specified buffer to be used as an incremental tcw, i.e. a
+ * helper data structure that can be used to construct a valid tcw by
+ * successive calls to other helper functions. Note: the buffer needs to be
+ * located below the 2G address limit. The resulting tcw has the following
+ * restrictions:
+ * - no tccb tidal
+ * - input/output tidal is contiguous (no ttic)
+ * - total data should not exceed 4k
+ * - tcw specifies either read or write operation
+ *
+ * On success, return pointer to the resulting incremental tcw data structure,
+ * ERR_PTR otherwise.
+ */
+struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg,
+ int max_tidaws, int intrg_max_tidaws)
+{
+ struct itcw *itcw;
+ void *chunk;
+ addr_t start;
+ addr_t end;
+
+ /* Check for 2G limit. */
+ start = (addr_t) buffer;
+ end = start + size;
+ if (end > (1 << 31))
+ return ERR_PTR(-EINVAL);
+ memset(buffer, 0, size);
+ /* ITCW. */
+ chunk = fit_chunk(&start, end, sizeof(struct itcw), 1, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ itcw = chunk;
+ itcw->max_tidaws = max_tidaws;
+ itcw->intrg_max_tidaws = intrg_max_tidaws;
+ /* Main TCW. */
+ chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ itcw->tcw = chunk;
+ tcw_init(itcw->tcw, (op == ITCW_OP_READ) ? 1 : 0,
+ (op == ITCW_OP_WRITE) ? 1 : 0);
+ /* Interrogate TCW. */
+ if (intrg) {
+ chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ itcw->intrg_tcw = chunk;
+ tcw_init(itcw->intrg_tcw, 1, 0);
+ tcw_set_intrg(itcw->tcw, itcw->intrg_tcw);
+ }
+ /* Data TIDAL. */
+ if (max_tidaws > 0) {
+ chunk = fit_chunk(&start, end, sizeof(struct tidaw) *
+ max_tidaws, 16, 1);
+ if (IS_ERR(chunk))
+ return chunk;
+ tcw_set_data(itcw->tcw, chunk, 1);
+ }
+ /* Interrogate data TIDAL. */
+ if (intrg && (intrg_max_tidaws > 0)) {
+ chunk = fit_chunk(&start, end, sizeof(struct tidaw) *
+ intrg_max_tidaws, 16, 1);
+ if (IS_ERR(chunk))
+ return chunk;
+ tcw_set_data(itcw->intrg_tcw, chunk, 1);
+ }
+ /* TSB. */
+ chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ tsb_init(chunk);
+ tcw_set_tsb(itcw->tcw, chunk);
+ /* Interrogate TSB. */
+ if (intrg) {
+ chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ tsb_init(chunk);
+ tcw_set_tsb(itcw->intrg_tcw, chunk);
+ }
+ /* TCCB. */
+ chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ tccb_init(chunk, TCCB_MAX_SIZE, TCCB_SAC_DEFAULT);
+ tcw_set_tccb(itcw->tcw, chunk);
+ /* Interrogate TCCB. */
+ if (intrg) {
+ chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ tccb_init(chunk, TCCB_MAX_SIZE, TCCB_SAC_INTRG);
+ tcw_set_tccb(itcw->intrg_tcw, chunk);
+ tccb_add_dcw(chunk, TCCB_MAX_SIZE, DCW_CMD_INTRG, 0, NULL,
+ sizeof(struct dcw_intrg_data), 0);
+ tcw_finalize(itcw->intrg_tcw, 0);
+ }
+ return itcw;
+}
+EXPORT_SYMBOL(itcw_init);
+
+/**
+ * itcw_add_dcw - add a dcw to the itcw
+ * @itcw: address of the itcw
+ * @cmd: the dcw command
+ * @flags: flags for the dcw
+ * @cd: address of control data for this dcw or NULL if none is required
+ * @cd_count: number of control data bytes for this dcw
+ * @count: number of data bytes for this dcw
+ *
+ * Add a new dcw to the specified itcw by writing the dcw information specified
+ * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return
+ * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw
+ * would exceed the available space.
+ *
+ * Note: the tcal field of the tccb header will be updated to reflect added
+ * content.
+ */
+struct dcw *itcw_add_dcw(struct itcw *itcw, u8 cmd, u8 flags, void *cd,
+ u8 cd_count, u32 count)
+{
+ return tccb_add_dcw(tcw_get_tccb(itcw->tcw), TCCB_MAX_SIZE, cmd,
+ flags, cd, cd_count, count);
+}
+EXPORT_SYMBOL(itcw_add_dcw);
+
+/**
+ * itcw_add_tidaw - add a tidaw to the itcw
+ * @itcw: address of the itcw
+ * @flags: flags for the new tidaw
+ * @addr: address value for the new tidaw
+ * @count: count value for the new tidaw
+ *
+ * Add a new tidaw to the input/output data tidaw-list of the specified itcw
+ * (depending on the value of the r-flag and w-flag). Return a pointer to
+ * 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.
+ */
+struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count)
+{
+ 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);
+
+/**
+ * itcw_set_data - set data address and tida flag of the itcw
+ * @itcw: address of the itcw
+ * @addr: the data address
+ * @use_tidal: zero of the data address specifies a contiguous block of data,
+ * non-zero if it specifies a list if tidaws.
+ *
+ * Set the input/output data address of the itcw (depending on the value of the
+ * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag
+ * is set as well.
+ */
+void itcw_set_data(struct itcw *itcw, void *addr, int use_tidal)
+{
+ tcw_set_data(itcw->tcw, addr, use_tidal);
+}
+EXPORT_SYMBOL(itcw_set_data);
+
+/**
+ * itcw_finalize - calculate length and count fields of the itcw
+ * @itcw: address of the itcw
+ *
+ * Calculate tcw input-/output-count and tccbl fields and add a tcat the tccb.
+ * In case input- or output-tida is used, the tidaw-list must be stored in
+ * continuous storage (no ttic). The tcal field in the tccb must be
+ * up-to-date.
+ */
+void itcw_finalize(struct itcw *itcw)
+{
+ tcw_finalize(itcw->tcw, itcw->num_tidaws);
+}
+EXPORT_SYMBOL(itcw_finalize);
diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c
deleted file mode 100644
index 445cf364e46..00000000000
--- a/drivers/s390/cio/qdio.c
+++ /dev/null
@@ -1,3934 +0,0 @@
-/*
- *
- * linux/drivers/s390/cio/qdio.c
- *
- * Linux for S/390 QDIO base support, Hipersocket base support
- * version 2
- *
- * Copyright 2000,2002 IBM Corporation
- * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
- * 2.6 cio integration by Cornelia Huck <cornelia.huck@de.ibm.com>
- *
- * Restriction: only 63 iqdio subchannels would have its own indicator,
- * after that, subsequent subchannels share one indicator
- *
- *
- *
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/proc_fs.h>
-#include <linux/timer.h>
-#include <linux/mempool.h>
-#include <linux/semaphore.h>
-
-#include <asm/ccwdev.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
-#include <asm/timex.h>
-
-#include <asm/debug.h>
-#include <asm/s390_rdev.h>
-#include <asm/qdio.h>
-#include <asm/airq.h>
-
-#include "cio.h"
-#include "css.h"
-#include "device.h"
-#include "qdio.h"
-#include "ioasm.h"
-#include "chsc.h"
-
-/****************** MODULE PARAMETER VARIABLES ********************/
-MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
-MODULE_DESCRIPTION("QDIO base support version 2, " \
- "Copyright 2000 IBM Corporation");
-MODULE_LICENSE("GPL");
-
-/******************** HERE WE GO ***********************************/
-
-static const char version[] = "QDIO base support version 2";
-
-static int qdio_performance_stats = 0;
-static int proc_perf_file_registration;
-static struct qdio_perf_stats perf_stats;
-
-static int hydra_thinints;
-static int is_passthrough = 0;
-static int omit_svs;
-
-static int indicator_used[INDICATORS_PER_CACHELINE];
-static __u32 * volatile indicators;
-static __u32 volatile spare_indicator;
-static atomic_t spare_indicator_usecount;
-#define QDIO_MEMPOOL_SCSSC_ELEMENTS 2
-static mempool_t *qdio_mempool_scssc;
-static struct kmem_cache *qdio_q_cache;
-
-static debug_info_t *qdio_dbf_setup;
-static debug_info_t *qdio_dbf_sbal;
-static debug_info_t *qdio_dbf_trace;
-static debug_info_t *qdio_dbf_sense;
-#ifdef CONFIG_QDIO_DEBUG
-static debug_info_t *qdio_dbf_slsb_out;
-static debug_info_t *qdio_dbf_slsb_in;
-#endif /* CONFIG_QDIO_DEBUG */
-
-/* iQDIO stuff: */
-static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change
- during a while loop */
-static DEFINE_SPINLOCK(ttiq_list_lock);
-static void *tiqdio_ind;
-static void tiqdio_tl(unsigned long);
-static DECLARE_TASKLET(tiqdio_tasklet,tiqdio_tl,0);
-
-/* not a macro, as one of the arguments is atomic_read */
-static inline int
-qdio_min(int a,int b)
-{
- if (a<b)
- return a;
- else
- return b;
-}
-
-/***************** SCRUBBER HELPER ROUTINES **********************/
-#ifdef CONFIG_64BIT
-static inline void qdio_perf_stat_inc(atomic64_t *count)
-{
- if (qdio_performance_stats)
- atomic64_inc(count);
-}
-
-static inline void qdio_perf_stat_dec(atomic64_t *count)
-{
- if (qdio_performance_stats)
- atomic64_dec(count);
-}
-#else /* CONFIG_64BIT */
-static inline void qdio_perf_stat_inc(atomic_t *count)
-{
- if (qdio_performance_stats)
- atomic_inc(count);
-}
-
-static inline void qdio_perf_stat_dec(atomic_t *count)
-{
- if (qdio_performance_stats)
- atomic_dec(count);
-}
-#endif /* CONFIG_64BIT */
-
-static inline __u64
-qdio_get_micros(void)
-{
- return (get_clock() >> 12); /* time>>12 is microseconds */
-}
-
-/*
- * unfortunately, we can't just xchg the values; in do_QDIO we want to reserve
- * the q in any case, so that we'll not be interrupted when we are in
- * qdio_mark_tiq... shouldn't have a really bad impact, as reserving almost
- * ever works (last famous words)
- */
-static inline int
-qdio_reserve_q(struct qdio_q *q)
-{
- return atomic_add_return(1,&q->use_count) - 1;
-}
-
-static inline void
-qdio_release_q(struct qdio_q *q)
-{
- atomic_dec(&q->use_count);
-}
-
-/*check ccq */
-static int
-qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
-{
- char dbf_text[15];
-
- if (ccq == 0 || ccq == 32)
- return 0;
- if (ccq == 96 || ccq == 97)
- return 1;
- /*notify devices immediately*/
- sprintf(dbf_text,"%d", ccq);
- QDIO_DBF_TEXT2(1,trace,dbf_text);
- return -EIO;
-}
-/* EQBS: extract buffer states */
-static int
-qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
- unsigned int *start, unsigned int *cnt)
-{
- struct qdio_irq *irq;
- unsigned int tmp_cnt, q_no, ccq;
- int rc ;
- char dbf_text[15];
-
- ccq = 0;
- tmp_cnt = *cnt;
- irq = (struct qdio_irq*)q->irq_ptr;
- q_no = q->q_no;
- if(!q->is_input_q)
- q_no += irq->no_input_qs;
-again:
- ccq = do_eqbs(irq->sch_token, state, q_no, start, cnt);
- rc = qdio_check_ccq(q, ccq);
- if ((ccq == 96) && (tmp_cnt != *cnt))
- rc = 0;
- if (rc == 1) {
- QDIO_DBF_TEXT5(1,trace,"eqAGAIN");
- goto again;
- }
- if (rc < 0) {
- QDIO_DBF_TEXT2(1,trace,"eqberr");
- sprintf(dbf_text,"%2x,%2x,%d,%d",tmp_cnt, *cnt, ccq, q_no);
- QDIO_DBF_TEXT2(1,trace,dbf_text);
- q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION|
- QDIO_STATUS_LOOK_FOR_ERROR,
- 0, 0, 0, -1, -1, q->int_parm);
- return 0;
- }
- return (tmp_cnt - *cnt);
-}
-
-/* SQBS: set buffer states */
-static int
-qdio_do_sqbs(struct qdio_q *q, unsigned char state,
- unsigned int *start, unsigned int *cnt)
-{
- struct qdio_irq *irq;
- unsigned int tmp_cnt, q_no, ccq;
- int rc;
- char dbf_text[15];
-
- ccq = 0;
- tmp_cnt = *cnt;
- irq = (struct qdio_irq*)q->irq_ptr;
- q_no = q->q_no;
- if(!q->is_input_q)
- q_no += irq->no_input_qs;
-again:
- ccq = do_sqbs(irq->sch_token, state, q_no, start, cnt);
- rc = qdio_check_ccq(q, ccq);
- if (rc == 1) {
- QDIO_DBF_TEXT5(1,trace,"sqAGAIN");
- goto again;
- }
- if (rc < 0) {
- QDIO_DBF_TEXT3(1,trace,"sqberr");
- sprintf(dbf_text,"%2x,%2x",tmp_cnt,*cnt);
- QDIO_DBF_TEXT3(1,trace,dbf_text);
- sprintf(dbf_text,"%d,%d",ccq,q_no);
- QDIO_DBF_TEXT3(1,trace,dbf_text);
- q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION|
- QDIO_STATUS_LOOK_FOR_ERROR,
- 0, 0, 0, -1, -1, q->int_parm);
- return 0;
- }
- return (tmp_cnt - *cnt);
-}
-
-static inline int
-qdio_set_slsb(struct qdio_q *q, unsigned int *bufno,
- unsigned char state, unsigned int *count)
-{
- volatile char *slsb;
- struct qdio_irq *irq;
-
- irq = (struct qdio_irq*)q->irq_ptr;
- if (!irq->is_qebsm) {
- slsb = (char *)&q->slsb.acc.val[(*bufno)];
- xchg(slsb, state);
- return 1;
- }
- return qdio_do_sqbs(q, state, bufno, count);
-}
-
-#ifdef CONFIG_QDIO_DEBUG
-static inline void
-qdio_trace_slsb(struct qdio_q *q)
-{
- if (q->queue_type==QDIO_TRACE_QTYPE) {
- if (q->is_input_q)
- QDIO_DBF_HEX2(0,slsb_in,&q->slsb,
- QDIO_MAX_BUFFERS_PER_Q);
- else
- QDIO_DBF_HEX2(0,slsb_out,&q->slsb,
- QDIO_MAX_BUFFERS_PER_Q);
- }
-}
-#endif
-
-static inline int
-set_slsb(struct qdio_q *q, unsigned int *bufno,
- unsigned char state, unsigned int *count)
-{
- int rc;
-#ifdef CONFIG_QDIO_DEBUG
- qdio_trace_slsb(q);
-#endif
- rc = qdio_set_slsb(q, bufno, state, count);
-#ifdef CONFIG_QDIO_DEBUG
- qdio_trace_slsb(q);
-#endif
- return rc;
-}
-static inline int
-qdio_siga_sync(struct qdio_q *q, unsigned int gpr2,
- unsigned int gpr3)
-{
- int cc;
-
- QDIO_DBF_TEXT4(0,trace,"sigasync");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- qdio_perf_stat_inc(&perf_stats.siga_syncs);
-
- cc = do_siga_sync(q->schid, gpr2, gpr3);
- if (cc)
- QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*));
-
- return cc;
-}
-
-static inline int
-qdio_siga_sync_q(struct qdio_q *q)
-{
- if (q->is_input_q)
- return qdio_siga_sync(q, 0, q->mask);
- return qdio_siga_sync(q, q->mask, 0);
-}
-
-static int
-__do_siga_output(struct qdio_q *q, unsigned int *busy_bit)
-{
- struct qdio_irq *irq;
- unsigned int fc = 0;
- unsigned long schid;
-
- irq = (struct qdio_irq *) q->irq_ptr;
- if (!irq->is_qebsm)
- schid = *((u32 *)&q->schid);
- else {
- schid = irq->sch_token;
- fc |= 0x80;
- }
- return do_siga_output(schid, q->mask, busy_bit, fc);
-}
-
-/*
- * returns QDIO_SIGA_ERROR_ACCESS_EXCEPTION as cc, when SIGA returns
- * an access exception
- */
-static int
-qdio_siga_output(struct qdio_q *q)
-{
- int cc;
- __u32 busy_bit;
- __u64 start_time=0;
-
- qdio_perf_stat_inc(&perf_stats.siga_outs);
-
- QDIO_DBF_TEXT4(0,trace,"sigaout");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- for (;;) {
- cc = __do_siga_output(q, &busy_bit);
-//QDIO_PRINT_ERR("cc=%x, busy=%x\n",cc,busy_bit);
- if ((cc==2) && (busy_bit) && (q->is_iqdio_q)) {
- if (!start_time)
- start_time=NOW;
- if ((NOW-start_time)>QDIO_BUSY_BIT_PATIENCE)
- break;
- } else
- break;
- }
-
- if ((cc==2) && (busy_bit))
- cc |= QDIO_SIGA_ERROR_B_BIT_SET;
-
- if (cc)
- QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*));
-
- return cc;
-}
-
-static int
-qdio_siga_input(struct qdio_q *q)
-{
- int cc;
-
- QDIO_DBF_TEXT4(0,trace,"sigain");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- qdio_perf_stat_inc(&perf_stats.siga_ins);
-
- cc = do_siga_input(q->schid, q->mask);
-
- if (cc)
- QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*));
-
- return cc;
-}
-
-/* locked by the locks in qdio_activate and qdio_cleanup */
-static __u32 *
-qdio_get_indicator(void)
-{
- int i;
-
- for (i = 0; i < INDICATORS_PER_CACHELINE; i++)
- if (!indicator_used[i]) {
- indicator_used[i]=1;
- return indicators+i;
- }
- atomic_inc(&spare_indicator_usecount);
- return (__u32 * volatile) &spare_indicator;
-}
-
-/* locked by the locks in qdio_activate and qdio_cleanup */
-static void
-qdio_put_indicator(__u32 *addr)
-{
- int i;
-
- if ( (addr) && (addr!=&spare_indicator) ) {
- i=addr-indicators;
- indicator_used[i]=0;
- }
- if (addr == &spare_indicator)
- atomic_dec(&spare_indicator_usecount);
-}
-
-static inline void
-tiqdio_clear_summary_bit(__u32 *location)
-{
- QDIO_DBF_TEXT5(0,trace,"clrsummb");
- QDIO_DBF_HEX5(0,trace,&location,sizeof(void*));
-
- xchg(location,0);
-}
-
-static inline void
-tiqdio_set_summary_bit(__u32 *location)
-{
- QDIO_DBF_TEXT5(0,trace,"setsummb");
- QDIO_DBF_HEX5(0,trace,&location,sizeof(void*));
-
- xchg(location,-1);
-}
-
-static inline void
-tiqdio_sched_tl(void)
-{
- tasklet_hi_schedule(&tiqdio_tasklet);
-}
-
-static void
-qdio_mark_tiq(struct qdio_q *q)
-{
- unsigned long flags;
-
- QDIO_DBF_TEXT4(0,trace,"mark iq");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- spin_lock_irqsave(&ttiq_list_lock,flags);
- if (unlikely(atomic_read(&q->is_in_shutdown)))
- goto out_unlock;
-
- if (!q->is_input_q)
- goto out_unlock;
-
- if ((q->list_prev) || (q->list_next))
- goto out_unlock;
-
- if (!tiq_list) {
- tiq_list=q;
- q->list_prev=q;
- q->list_next=q;
- } else {
- q->list_next=tiq_list;
- q->list_prev=tiq_list->list_prev;
- tiq_list->list_prev->list_next=q;
- tiq_list->list_prev=q;
- }
- spin_unlock_irqrestore(&ttiq_list_lock,flags);
-
- tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind);
- tiqdio_sched_tl();
- return;
-out_unlock:
- spin_unlock_irqrestore(&ttiq_list_lock,flags);
- return;
-}
-
-static inline void
-qdio_mark_q(struct qdio_q *q)
-{
- QDIO_DBF_TEXT4(0,trace,"mark q");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- if (unlikely(atomic_read(&q->is_in_shutdown)))
- return;
-
- tasklet_schedule(&q->tasklet);
-}
-
-static int
-qdio_stop_polling(struct qdio_q *q)
-{
-#ifdef QDIO_USE_PROCESSING_STATE
- unsigned int tmp, gsf, count = 1;
- unsigned char state = 0;
- struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;
-
- if (!atomic_xchg(&q->polling,0))
- return 1;
-
- QDIO_DBF_TEXT4(0,trace,"stoppoll");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- /* show the card that we are not polling anymore */
- if (!q->is_input_q)
- return 1;
-
- tmp = gsf = GET_SAVED_FRONTIER(q);
- tmp = ((tmp + QDIO_MAX_BUFFERS_PER_Q-1) & (QDIO_MAX_BUFFERS_PER_Q-1) );
- set_slsb(q, &tmp, SLSB_P_INPUT_NOT_INIT, &count);
-
- /*
- * we don't issue this SYNC_MEMORY, as we trust Rick T and
- * moreover will not use the PROCESSING state under VM, so
- * q->polling was 0 anyway
- */
- /*SYNC_MEMORY;*/
- if (irq->is_qebsm) {
- count = 1;
- qdio_do_eqbs(q, &state, &gsf, &count);
- } else
- state = q->slsb.acc.val[gsf];
- if (state != SLSB_P_INPUT_PRIMED)
- return 1;
- /*
- * set our summary bit again, as otherwise there is a
- * small window we can miss between resetting it and
- * checking for PRIMED state
- */
- if (q->is_thinint_q)
- tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind);
- return 0;
-
-#else /* QDIO_USE_PROCESSING_STATE */
- return 1;
-#endif /* QDIO_USE_PROCESSING_STATE */
-}
-
-/*
- * see the comment in do_QDIO and before qdio_reserve_q about the
- * sophisticated locking outside of unmark_q, so that we don't need to
- * disable the interrupts :-)
-*/
-static void
-qdio_unmark_q(struct qdio_q *q)
-{
- unsigned long flags;
-
- QDIO_DBF_TEXT4(0,trace,"unmark q");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- if ((!q->list_prev)||(!q->list_next))
- return;
-
- if ((q->is_thinint_q)&&(q->is_input_q)) {
- /* iQDIO */
- spin_lock_irqsave(&ttiq_list_lock,flags);
- /* in case cleanup has done this already and simultanously
- * qdio_unmark_q is called from the interrupt handler, we've
- * got to check this in this specific case again */
- if ((!q->list_prev)||(!q->list_next))
- goto out;
- if (q->list_next==q) {
- /* q was the only interesting q */
- tiq_list=NULL;
- q->list_next=NULL;
- q->list_prev=NULL;
- } else {
- q->list_next->list_prev=q->list_prev;
- q->list_prev->list_next=q->list_next;
- tiq_list=q->list_next;
- q->list_next=NULL;
- q->list_prev=NULL;
- }
-out:
- spin_unlock_irqrestore(&ttiq_list_lock,flags);
- }
-}
-
-static inline unsigned long
-tiqdio_clear_global_summary(void)
-{
- unsigned long time;
-
- QDIO_DBF_TEXT5(0,trace,"clrglobl");
-
- time = do_clear_global_summary();
-
- QDIO_DBF_HEX5(0,trace,&time,sizeof(unsigned long));
-
- return time;
-}
-
-
-/************************* OUTBOUND ROUTINES *******************************/
-static int
-qdio_qebsm_get_outbound_buffer_frontier(struct qdio_q *q)
-{
- struct qdio_irq *irq;
- unsigned char state;
- unsigned int cnt, count, ftc;
-
- irq = (struct qdio_irq *) q->irq_ptr;
- if ((!q->is_iqdio_q) && (!q->hydra_gives_outbound_pcis))
- SYNC_MEMORY;
-
- ftc = q->first_to_check;
- count = qdio_min(atomic_read(&q->number_of_buffers_used),
- (QDIO_MAX_BUFFERS_PER_Q-1));
- if (count == 0)
- return q->first_to_check;
- cnt = qdio_do_eqbs(q, &state, &ftc, &count);
- if (cnt == 0)
- return q->first_to_check;
- switch (state) {
- case SLSB_P_OUTPUT_ERROR:
- QDIO_DBF_TEXT3(0,trace,"outperr");
- atomic_sub(cnt , &q->number_of_buffers_used);
- if (q->qdio_error)
- q->error_status_flags |=
- QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR;
- q->qdio_error = SLSB_P_OUTPUT_ERROR;
- q->error_status_flags |= QDIO_STATUS_LOOK_FOR_ERROR;
- q->first_to_check = ftc;
- break;
- case SLSB_P_OUTPUT_EMPTY:
- QDIO_DBF_TEXT5(0,trace,"outpempt");
- atomic_sub(cnt, &q->number_of_buffers_used);
- q->first_to_check = ftc;
- break;
- case SLSB_CU_OUTPUT_PRIMED:
- /* all buffers primed */
- QDIO_DBF_TEXT5(0,trace,"outpprim");
- break;
- default:
- break;
- }
- QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));
- return q->first_to_check;
-}
-
-static int
-qdio_qebsm_get_inbound_buffer_frontier(struct qdio_q *q)
-{
- struct qdio_irq *irq;
- unsigned char state;
- int tmp, ftc, count, cnt;
- char dbf_text[15];
-
-
- irq = (struct qdio_irq *) q->irq_ptr;
- ftc = q->first_to_check;
- count = qdio_min(atomic_read(&q->number_of_buffers_used),
- (QDIO_MAX_BUFFERS_PER_Q-1));
- if (count == 0)
- return q->first_to_check;
- cnt = qdio_do_eqbs(q, &state, &ftc, &count);
- if (cnt == 0)
- return q->first_to_check;
- switch (state) {
- case SLSB_P_INPUT_ERROR :
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT3(1,trace,"inperr");
- sprintf(dbf_text,"%2x,%2x",ftc,count);
- QDIO_DBF_TEXT3(1,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
- if (q->qdio_error)
- q->error_status_flags |=
- QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR;
- q->qdio_error = SLSB_P_INPUT_ERROR;
- q->error_status_flags |= QDIO_STATUS_LOOK_FOR_ERROR;
- atomic_sub(cnt, &q->number_of_buffers_used);
- q->first_to_check = ftc;
- break;
- case SLSB_P_INPUT_PRIMED :
- QDIO_DBF_TEXT3(0,trace,"inptprim");
- sprintf(dbf_text,"%2x,%2x",ftc,count);
- QDIO_DBF_TEXT3(1,trace,dbf_text);
- tmp = 0;
- ftc = q->first_to_check;
-#ifdef QDIO_USE_PROCESSING_STATE
- if (cnt > 1) {
- cnt -= 1;
- tmp = set_slsb(q, &ftc, SLSB_P_INPUT_NOT_INIT, &cnt);
- if (!tmp)
- break;
- }
- cnt = 1;
- tmp += set_slsb(q, &ftc,
- SLSB_P_INPUT_PROCESSING, &cnt);
- atomic_set(&q->polling, 1);
-#else
- tmp = set_slsb(q, &ftc, SLSB_P_INPUT_NOT_INIT, &cnt);
-#endif
- atomic_sub(tmp, &q->number_of_buffers_used);
- q->first_to_check = ftc;
- break;
- case SLSB_CU_INPUT_EMPTY:
- case SLSB_P_INPUT_NOT_INIT:
- case SLSB_P_INPUT_PROCESSING:
- QDIO_DBF_TEXT5(0,trace,"inpnipro");
- break;
- default:
- break;
- }
- QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));
- return q->first_to_check;
-}
-
-static int
-qdio_get_outbound_buffer_frontier(struct qdio_q *q)
-{
- struct qdio_irq *irq;
- volatile char *slsb;
- unsigned int count = 1;
- int first_not_to_check, f, f_mod_no;
- char dbf_text[15];
-
- QDIO_DBF_TEXT4(0,trace,"getobfro");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- irq = (struct qdio_irq *) q->irq_ptr;
- if (irq->is_qebsm)
- return qdio_qebsm_get_outbound_buffer_frontier(q);
-
- slsb=&q->slsb.acc.val[0];
- f_mod_no=f=q->first_to_check;
- /*
- * f points to already processed elements, so f+no_used is correct...
- * ... but: we don't check 128 buffers, as otherwise
- * qdio_has_outbound_q_moved would return 0
- */
- first_not_to_check=f+qdio_min(atomic_read(&q->number_of_buffers_used),
- (QDIO_MAX_BUFFERS_PER_Q-1));
-
- if (((!q->is_iqdio_q) && (!q->hydra_gives_outbound_pcis)) ||
- (q->queue_type == QDIO_IQDIO_QFMT_ASYNCH))
- SYNC_MEMORY;
-
-check_next:
- if (f==first_not_to_check)
- goto out;
-
- switch(slsb[f_mod_no]) {
-
- /* the adapter has not fetched the output yet */
- case SLSB_CU_OUTPUT_PRIMED:
- QDIO_DBF_TEXT5(0,trace,"outpprim");
- break;
-
- /* the adapter got it */
- case SLSB_P_OUTPUT_EMPTY:
- atomic_dec(&q->number_of_buffers_used);
- f++;
- f_mod_no=f&(QDIO_MAX_BUFFERS_PER_Q-1);
- QDIO_DBF_TEXT5(0,trace,"outpempt");
- goto check_next;
-
- case SLSB_P_OUTPUT_ERROR:
- QDIO_DBF_TEXT3(0,trace,"outperr");
- sprintf(dbf_text,"%x-%x-%x",f_mod_no,
- q->sbal[f_mod_no]->element[14].sbalf.value,
- q->sbal[f_mod_no]->element[15].sbalf.value);
- QDIO_DBF_TEXT3(1,trace,dbf_text);
- QDIO_DBF_HEX2(1,sbal,q->sbal[f_mod_no],256);
-
- /* kind of process the buffer */
- set_slsb(q, &f_mod_no, SLSB_P_OUTPUT_NOT_INIT, &count);
-
- /*
- * we increment the frontier, as this buffer
- * was processed obviously
- */
- atomic_dec(&q->number_of_buffers_used);
- f_mod_no=(f_mod_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
-
- if (q->qdio_error)
- q->error_status_flags|=
- QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR;
- q->qdio_error=SLSB_P_OUTPUT_ERROR;
- q->error_status_flags|=QDIO_STATUS_LOOK_FOR_ERROR;
-
- break;
-
- /* no new buffers */
- default:
- QDIO_DBF_TEXT5(0,trace,"outpni");
- }
-out:
- return (q->first_to_check=f_mod_no);
-}
-
-/* all buffers are processed */
-static int
-qdio_is_outbound_q_done(struct qdio_q *q)
-{
- int no_used;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
-
- no_used=atomic_read(&q->number_of_buffers_used);
-
-#ifdef CONFIG_QDIO_DEBUG
- if (no_used) {
- sprintf(dbf_text,"oqisnt%02x",no_used);
- QDIO_DBF_TEXT4(0,trace,dbf_text);
- } else {
- QDIO_DBF_TEXT4(0,trace,"oqisdone");
- }
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-#endif /* CONFIG_QDIO_DEBUG */
- return (no_used==0);
-}
-
-static int
-qdio_has_outbound_q_moved(struct qdio_q *q)
-{
- int i;
-
- i=qdio_get_outbound_buffer_frontier(q);
-
- if ( (i!=GET_SAVED_FRONTIER(q)) ||
- (q->error_status_flags&QDIO_STATUS_LOOK_FOR_ERROR) ) {
- SAVE_FRONTIER(q,i);
- QDIO_DBF_TEXT4(0,trace,"oqhasmvd");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
- return 1;
- } else {
- QDIO_DBF_TEXT4(0,trace,"oqhsntmv");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
- return 0;
- }
-}
-
-static void
-qdio_kick_outbound_q(struct qdio_q *q)
-{
- int result;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-
- QDIO_DBF_TEXT4(0,trace,"kickoutq");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-#endif /* CONFIG_QDIO_DEBUG */
-
- if (!q->siga_out)
- return;
-
- /* here's the story with cc=2 and busy bit set (thanks, Rick):
- * VM's CP could present us cc=2 and busy bit set on SIGA-write
- * during reconfiguration of their Guest LAN (only in HIPERS mode,
- * QDIO mode is asynchronous -- cc=2 and busy bit there will take
- * the queues down immediately; and not being under VM we have a
- * problem on cc=2 and busy bit set right away).
- *
- * Therefore qdio_siga_output will try for a short time constantly,
- * if such a condition occurs. If it doesn't change, it will
- * increase the busy_siga_counter and save the timestamp, and
- * schedule the queue for later processing (via mark_q, using the
- * queue tasklet). __qdio_outbound_processing will check out the
- * counter. If non-zero, it will call qdio_kick_outbound_q as often
- * as the value of the counter. This will attempt further SIGA
- * instructions. For each successful SIGA, the counter is
- * decreased, for failing SIGAs the counter remains the same, after
- * all.
- * After some time of no movement, qdio_kick_outbound_q will
- * finally fail and reflect corresponding error codes to call
- * the upper layer module and have it take the queues down.
- *
- * Note that this is a change from the original HiperSockets design
- * (saying cc=2 and busy bit means take the queues down), but in
- * these days Guest LAN didn't exist... excessive cc=2 with busy bit
- * conditions will still take the queues down, but the threshold is
- * higher due to the Guest LAN environment.
- */
-
-
- result=qdio_siga_output(q);
-
- switch (result) {
- case 0:
- /* went smooth this time, reset timestamp */
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT3(0,trace,"cc2reslv");
- sprintf(dbf_text,"%4x%2x%2x",q->schid.sch_no,q->q_no,
- atomic_read(&q->busy_siga_counter));
- QDIO_DBF_TEXT3(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
- q->timing.busy_start=0;
- break;
- case (2|QDIO_SIGA_ERROR_B_BIT_SET):
- /* cc=2 and busy bit: */
- atomic_inc(&q->busy_siga_counter);
-
- /* if the last siga was successful, save
- * timestamp here */
- if (!q->timing.busy_start)
- q->timing.busy_start=NOW;
-
- /* if we're in time, don't touch error_status_flags
- * and siga_error */
- if (NOW-q->timing.busy_start<QDIO_BUSY_BIT_GIVE_UP) {
- qdio_mark_q(q);
- break;
- }
- QDIO_DBF_TEXT2(0,trace,"cc2REPRT");
-#ifdef CONFIG_QDIO_DEBUG
- sprintf(dbf_text,"%4x%2x%2x",q->schid.sch_no,q->q_no,
- atomic_read(&q->busy_siga_counter));
- QDIO_DBF_TEXT3(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
- /* else fallthrough and report error */
- default:
- /* for plain cc=1, 2 or 3: */
- if (q->siga_error)
- q->error_status_flags|=
- QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR;
- q->error_status_flags|=
- QDIO_STATUS_LOOK_FOR_ERROR;
- q->siga_error=result;
- }
-}
-
-static void
-qdio_kick_outbound_handler(struct qdio_q *q)
-{
- int start, end, real_end, count;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
-
- start = q->first_element_to_kick;
- /* last_move_ftc was just updated */
- real_end = GET_SAVED_FRONTIER(q);
- end = (real_end+QDIO_MAX_BUFFERS_PER_Q-1)&
- (QDIO_MAX_BUFFERS_PER_Q-1);
- count = (end+QDIO_MAX_BUFFERS_PER_Q+1-start)&
- (QDIO_MAX_BUFFERS_PER_Q-1);
-
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0,trace,"kickouth");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- sprintf(dbf_text,"s=%2xc=%2x",start,count);
- QDIO_DBF_TEXT4(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
-
- if (q->state==QDIO_IRQ_STATE_ACTIVE)
- q->handler(q->cdev,QDIO_STATUS_OUTBOUND_INT|
- q->error_status_flags,
- q->qdio_error,q->siga_error,q->q_no,start,count,
- q->int_parm);
-
- /* for the next time: */
- q->first_element_to_kick=real_end;
- q->qdio_error=0;
- q->siga_error=0;
- q->error_status_flags=0;
-}
-
-static void
-__qdio_outbound_processing(struct qdio_q *q)
-{
- int siga_attempts;
-
- QDIO_DBF_TEXT4(0,trace,"qoutproc");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- if (unlikely(qdio_reserve_q(q))) {
- qdio_release_q(q);
- qdio_perf_stat_inc(&perf_stats.outbound_tl_runs_resched);
- /* as we're sissies, we'll check next time */
- if (likely(!atomic_read(&q->is_in_shutdown))) {
- qdio_mark_q(q);
- QDIO_DBF_TEXT4(0,trace,"busy,agn");
- }
- return;
- }
- qdio_perf_stat_inc(&perf_stats.outbound_tl_runs);
- qdio_perf_stat_inc(&perf_stats.tl_runs);
-
- /* see comment in qdio_kick_outbound_q */
- siga_attempts=atomic_read(&q->busy_siga_counter);
- while (siga_attempts) {
- atomic_dec(&q->busy_siga_counter);
- qdio_kick_outbound_q(q);
- siga_attempts--;
- }
-
- if (qdio_has_outbound_q_moved(q))
- qdio_kick_outbound_handler(q);
-
- if (q->queue_type == QDIO_ZFCP_QFMT) {
- if ((!q->hydra_gives_outbound_pcis) &&
- (!qdio_is_outbound_q_done(q)))
- qdio_mark_q(q);
- }
- else if (((!q->is_iqdio_q) && (!q->is_pci_out)) ||
- (q->queue_type == QDIO_IQDIO_QFMT_ASYNCH)) {
- /*
- * make sure buffer switch from PRIMED to EMPTY is noticed
- * and outbound_handler is called
- */
- if (qdio_is_outbound_q_done(q)) {
- del_timer(&q->timer);
- } else {
- if (!timer_pending(&q->timer))
- mod_timer(&q->timer, jiffies +
- QDIO_FORCE_CHECK_TIMEOUT);
- }
- }
-
- qdio_release_q(q);
-}
-
-static void
-qdio_outbound_processing(unsigned long q)
-{
- __qdio_outbound_processing((struct qdio_q *) q);
-}
-
-/************************* INBOUND ROUTINES *******************************/
-
-
-static int
-qdio_get_inbound_buffer_frontier(struct qdio_q *q)
-{
- struct qdio_irq *irq;
- int f,f_mod_no;
- volatile char *slsb;
- unsigned int count = 1;
- int first_not_to_check;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif /* CONFIG_QDIO_DEBUG */
-#ifdef QDIO_USE_PROCESSING_STATE
- int last_position=-1;
-#endif /* QDIO_USE_PROCESSING_STATE */
-
- QDIO_DBF_TEXT4(0,trace,"getibfro");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- irq = (struct qdio_irq *) q->irq_ptr;
- if (irq->is_qebsm)
- return qdio_qebsm_get_inbound_buffer_frontier(q);
-
- slsb=&q->slsb.acc.val[0];
- f_mod_no=f=q->first_to_check;
- /*
- * we don't check 128 buffers, as otherwise qdio_has_inbound_q_moved
- * would return 0
- */
- first_not_to_check=f+qdio_min(atomic_read(&q->number_of_buffers_used),
- (QDIO_MAX_BUFFERS_PER_Q-1));
-
- /*
- * we don't use this one, as a PCI or we after a thin interrupt
- * will sync the queues
- */
- /* SYNC_MEMORY;*/
-
-check_next:
- f_mod_no=f&(QDIO_MAX_BUFFERS_PER_Q-1);
- if (f==first_not_to_check)
- goto out;
- switch (slsb[f_mod_no]) {
-
- /* CU_EMPTY means frontier is reached */
- case SLSB_CU_INPUT_EMPTY:
- QDIO_DBF_TEXT5(0,trace,"inptempt");
- break;
-
- /* P_PRIMED means set slsb to P_PROCESSING and move on */
- case SLSB_P_INPUT_PRIMED:
- QDIO_DBF_TEXT5(0,trace,"inptprim");
-
-#ifdef QDIO_USE_PROCESSING_STATE
- /*
- * as soon as running under VM, polling the input queues will
- * kill VM in terms of CP overhead
- */
- if (q->siga_sync) {
- set_slsb(q, &f_mod_no, SLSB_P_INPUT_NOT_INIT, &count);
- } else {
- /* set the previous buffer to NOT_INIT. The current
- * buffer will be set to PROCESSING at the end of
- * this function to avoid further interrupts. */
- if (last_position>=0)
- set_slsb(q, &last_position,
- SLSB_P_INPUT_NOT_INIT, &count);
- atomic_set(&q->polling,1);
- last_position=f_mod_no;
- }
-#else /* QDIO_USE_PROCESSING_STATE */
- set_slsb(q, &f_mod_no, SLSB_P_INPUT_NOT_INIT, &count);
-#endif /* QDIO_USE_PROCESSING_STATE */
- /*
- * not needed, as the inbound queue will be synced on the next
- * siga-r, resp. tiqdio_is_inbound_q_done will do the siga-s
- */
- /*SYNC_MEMORY;*/
- f++;
- atomic_dec(&q->number_of_buffers_used);
- goto check_next;
-
- case SLSB_P_INPUT_NOT_INIT:
- case SLSB_P_INPUT_PROCESSING:
- QDIO_DBF_TEXT5(0,trace,"inpnipro");
- break;
-
- /* P_ERROR means frontier is reached, break and report error */
- case SLSB_P_INPUT_ERROR:
-#ifdef CONFIG_QDIO_DEBUG
- sprintf(dbf_text,"inperr%2x",f_mod_no);
- QDIO_DBF_TEXT3(1,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
- QDIO_DBF_HEX2(1,sbal,q->sbal[f_mod_no],256);
-
- /* kind of process the buffer */
- set_slsb(q, &f_mod_no, SLSB_P_INPUT_NOT_INIT, &count);
-
- if (q->qdio_error)
- q->error_status_flags|=
- QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR;
- q->qdio_error=SLSB_P_INPUT_ERROR;
- q->error_status_flags|=QDIO_STATUS_LOOK_FOR_ERROR;
-
- /* we increment the frontier, as this buffer
- * was processed obviously */
- f_mod_no=(f_mod_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
- atomic_dec(&q->number_of_buffers_used);
-
-#ifdef QDIO_USE_PROCESSING_STATE
- last_position=-1;
-#endif /* QDIO_USE_PROCESSING_STATE */
-
- break;
-
- /* everything else means frontier not changed (HALTED or so) */
- default:
- break;
- }
-out:
- q->first_to_check=f_mod_no;
-
-#ifdef QDIO_USE_PROCESSING_STATE
- if (last_position>=0)
- set_slsb(q, &last_position, SLSB_P_INPUT_PROCESSING, &count);
-#endif /* QDIO_USE_PROCESSING_STATE */
-
- QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));
-
- return q->first_to_check;
-}
-
-static int
-qdio_has_inbound_q_moved(struct qdio_q *q)
-{
- int i;
-
- i=qdio_get_inbound_buffer_frontier(q);
- if ( (i!=GET_SAVED_FRONTIER(q)) ||
- (q->error_status_flags&QDIO_STATUS_LOOK_FOR_ERROR) ) {
- SAVE_FRONTIER(q,i);
- if ((!q->siga_sync)&&(!q->hydra_gives_outbound_pcis))
- SAVE_TIMESTAMP(q);
-
- QDIO_DBF_TEXT4(0,trace,"inhasmvd");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
- return 1;
- } else {
- QDIO_DBF_TEXT4(0,trace,"inhsntmv");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
- return 0;
- }
-}
-
-/* means, no more buffers to be filled */
-static int
-tiqdio_is_inbound_q_done(struct qdio_q *q)
-{
- int no_used;
- unsigned int start_buf, count;
- unsigned char state = 0;
- struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;
-
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
-
- no_used=atomic_read(&q->number_of_buffers_used);
-
- /* propagate the change from 82 to 80 through VM */
- SYNC_MEMORY;
-
-#ifdef CONFIG_QDIO_DEBUG
- if (no_used) {
- sprintf(dbf_text,"iqisnt%02x",no_used);
- QDIO_DBF_TEXT4(0,trace,dbf_text);
- } else {
- QDIO_DBF_TEXT4(0,trace,"iniqisdo");
- }
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-#endif /* CONFIG_QDIO_DEBUG */
-
- if (!no_used)
- return 1;
- if (irq->is_qebsm) {
- count = 1;
- start_buf = q->first_to_check;
- qdio_do_eqbs(q, &state, &start_buf, &count);
- } else
- state = q->slsb.acc.val[q->first_to_check];
- if (state != SLSB_P_INPUT_PRIMED)
- /*
- * nothing more to do, if next buffer is not PRIMED.
- * note that we did a SYNC_MEMORY before, that there
- * has been a sychnronization.
- * we will return 0 below, as there is nothing to do
- * (stop_polling not necessary, as we have not been
- * using the PROCESSING state
- */
- return 0;
-
- /*
- * ok, the next input buffer is primed. that means, that device state
- * change indicator and adapter local summary are set, so we will find
- * it next time.
- * we will return 0 below, as there is nothing to do, except scheduling
- * ourselves for the next time.
- */
- tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind);
- tiqdio_sched_tl();
- return 0;
-}
-
-static int
-qdio_is_inbound_q_done(struct qdio_q *q)
-{
- int no_used;
- unsigned int start_buf, count;
- unsigned char state = 0;
- struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;
-
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
-
- no_used=atomic_read(&q->number_of_buffers_used);
-
- /*
- * we need that one for synchronization with the adapter, as it
- * does a kind of PCI avoidance
- */
- SYNC_MEMORY;
-
- if (!no_used) {
- QDIO_DBF_TEXT4(0,trace,"inqisdnA");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
- return 1;
- }
- if (irq->is_qebsm) {
- count = 1;
- start_buf = q->first_to_check;
- qdio_do_eqbs(q, &state, &start_buf, &count);
- } else
- state = q->slsb.acc.val[q->first_to_check];
- if (state == SLSB_P_INPUT_PRIMED) {
- /* we got something to do */
- QDIO_DBF_TEXT4(0,trace,"inqisntA");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
- return 0;
- }
-
- /* on VM, we don't poll, so the q is always done here */
- if (q->siga_sync)
- return 1;
- if (q->hydra_gives_outbound_pcis)
- return 1;
-
- /*
- * at this point we know, that inbound first_to_check
- * has (probably) not moved (see qdio_inbound_processing)
- */
- if (NOW>GET_SAVED_TIMESTAMP(q)+q->timing.threshold) {
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0,trace,"inqisdon");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
- sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used);
- QDIO_DBF_TEXT4(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
- return 1;
- } else {
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0,trace,"inqisntd");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
- sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used);
- QDIO_DBF_TEXT4(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
- return 0;
- }
-}
-
-static void
-qdio_kick_inbound_handler(struct qdio_q *q)
-{
- int count, start, end, real_end, i;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
-
- QDIO_DBF_TEXT4(0,trace,"kickinh");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- start=q->first_element_to_kick;
- real_end=q->first_to_check;
- end=(real_end+QDIO_MAX_BUFFERS_PER_Q-1)&(QDIO_MAX_BUFFERS_PER_Q-1);
-
- i=start;
- count=0;
- while (1) {
- count++;
- if (i==end)
- break;
- i=(i+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
- }
-
-#ifdef CONFIG_QDIO_DEBUG
- sprintf(dbf_text,"s=%2xc=%2x",start,count);
- QDIO_DBF_TEXT4(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
-
- if (likely(q->state==QDIO_IRQ_STATE_ACTIVE))
- q->handler(q->cdev,
- QDIO_STATUS_INBOUND_INT|q->error_status_flags,
- q->qdio_error,q->siga_error,q->q_no,start,count,
- q->int_parm);
-
- /* for the next time: */
- q->first_element_to_kick=real_end;
- q->qdio_error=0;
- q->siga_error=0;
- q->error_status_flags=0;
-
- qdio_perf_stat_inc(&perf_stats.inbound_cnt);
-}
-
-static void
-__tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set)
-{
- struct qdio_irq *irq_ptr;
- struct qdio_q *oq;
- int i;
-
- QDIO_DBF_TEXT4(0,trace,"iqinproc");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- /*
- * we first want to reserve the q, so that we know, that we don't
- * interrupt ourselves and call qdio_unmark_q, as is_in_shutdown might
- * be set
- */
- if (unlikely(qdio_reserve_q(q))) {
- qdio_release_q(q);
- qdio_perf_stat_inc(&perf_stats.inbound_thin_tl_runs_resched);
- /*
- * as we might just be about to stop polling, we make
- * sure that we check again at least once more
- */
- tiqdio_sched_tl();
- return;
- }
- qdio_perf_stat_inc(&perf_stats.inbound_thin_tl_runs);
- if (unlikely(atomic_read(&q->is_in_shutdown))) {
- qdio_unmark_q(q);
- goto out;
- }
-
- /*
- * we reset spare_ind_was_set, when the queue does not use the
- * spare indicator
- */
- if (spare_ind_was_set)
- spare_ind_was_set = (q->dev_st_chg_ind == &spare_indicator);
-
- if (!(*(q->dev_st_chg_ind)) && !spare_ind_was_set)
- goto out;
- /*
- * q->dev_st_chg_ind is the indicator, be it shared or not.
- * only clear it, if indicator is non-shared
- */
- if (q->dev_st_chg_ind != &spare_indicator)
- tiqdio_clear_summary_bit((__u32*)q->dev_st_chg_ind);
-
- if (q->hydra_gives_outbound_pcis) {
- if (!q->siga_sync_done_on_thinints) {
- SYNC_MEMORY_ALL;
- } else if (!q->siga_sync_done_on_outb_tis) {
- SYNC_MEMORY_ALL_OUTB;
- }
- } else {
- SYNC_MEMORY;
- }
- /*
- * maybe we have to do work on our outbound queues... at least
- * we have to check the outbound-int-capable thinint-capable
- * queues
- */
- if (q->hydra_gives_outbound_pcis) {
- irq_ptr = (struct qdio_irq*)q->irq_ptr;
- for (i=0;i<irq_ptr->no_output_qs;i++) {
- oq = irq_ptr->output_qs[i];
- if (!qdio_is_outbound_q_done(oq)) {
- qdio_perf_stat_dec(&perf_stats.tl_runs);
- __qdio_outbound_processing(oq);
- }
- }
- }
-
- if (!qdio_has_inbound_q_moved(q))
- goto out;
-
- qdio_kick_inbound_handler(q);
- if (tiqdio_is_inbound_q_done(q))
- if (!qdio_stop_polling(q)) {
- /*
- * we set the flags to get into the stuff next time,
- * see also comment in qdio_stop_polling
- */
- tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind);
- tiqdio_sched_tl();
- }
-out:
- qdio_release_q(q);
-}
-
-static void
-tiqdio_inbound_processing(unsigned long q)
-{
- __tiqdio_inbound_processing((struct qdio_q *) q,
- atomic_read(&spare_indicator_usecount));
-}
-
-static void
-__qdio_inbound_processing(struct qdio_q *q)
-{
- int q_laps=0;
-
- QDIO_DBF_TEXT4(0,trace,"qinproc");
- QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-
- if (unlikely(qdio_reserve_q(q))) {
- qdio_release_q(q);
- qdio_perf_stat_inc(&perf_stats.inbound_tl_runs_resched);
- /* as we're sissies, we'll check next time */
- if (likely(!atomic_read(&q->is_in_shutdown))) {
- qdio_mark_q(q);
- QDIO_DBF_TEXT4(0,trace,"busy,agn");
- }
- return;
- }
- qdio_perf_stat_inc(&perf_stats.inbound_tl_runs);
- qdio_perf_stat_inc(&perf_stats.tl_runs);
-
-again:
- if (qdio_has_inbound_q_moved(q)) {
- qdio_kick_inbound_handler(q);
- if (!qdio_stop_polling(q)) {
- q_laps++;
- if (q_laps<QDIO_Q_LAPS)
- goto again;
- }
- qdio_mark_q(q);
- } else {
- if (!qdio_is_inbound_q_done(q))
- /* means poll time is not yet over */
- qdio_mark_q(q);
- }
-
- qdio_release_q(q);
-}
-
-static void
-qdio_inbound_processing(unsigned long q)
-{
- __qdio_inbound_processing((struct qdio_q *) q);
-}
-
-/************************* MAIN ROUTINES *******************************/
-
-#ifdef QDIO_USE_PROCESSING_STATE
-static int
-tiqdio_reset_processing_state(struct qdio_q *q, int q_laps)
-{
- if (!q) {
- tiqdio_sched_tl();
- return 0;
- }
-
- /*
- * under VM, we have not used the PROCESSING state, so no
- * need to stop polling
- */
- if (q->siga_sync)
- return 2;
-
- if (unlikely(qdio_reserve_q(q))) {
- qdio_release_q(q);
- qdio_perf_stat_inc(&perf_stats.inbound_thin_tl_runs_resched);
- /*
- * as we might just be about to stop polling, we make
- * sure that we check again at least once more
- */
-
- /*
- * sanity -- we'd get here without setting the
- * dev st chg ind
- */
- tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind);
- tiqdio_sched_tl();
- return 0;
- }
- if (qdio_stop_polling(q)) {
- qdio_release_q(q);
- return 2;
- }
- if (q_laps<QDIO_Q_LAPS-1) {
- qdio_release_q(q);
- return 3;
- }
- /*
- * we set the flags to get into the stuff
- * next time, see also comment in qdio_stop_polling
- */
- tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind);
- tiqdio_sched_tl();
- qdio_release_q(q);
- return 1;
-
-}
-#endif /* QDIO_USE_PROCESSING_STATE */
-
-static void
-tiqdio_inbound_checks(void)
-{
- struct qdio_q *q;
- int spare_ind_was_set=0;
-#ifdef QDIO_USE_PROCESSING_STATE
- int q_laps=0;
-#endif /* QDIO_USE_PROCESSING_STATE */
-
- QDIO_DBF_TEXT4(0,trace,"iqdinbck");
- QDIO_DBF_TEXT5(0,trace,"iqlocsum");
-
-#ifdef QDIO_USE_PROCESSING_STATE
-again:
-#endif /* QDIO_USE_PROCESSING_STATE */
-
- /* when the spare indicator is used and set, save that and clear it */
- if ((atomic_read(&spare_indicator_usecount)) && spare_indicator) {
- spare_ind_was_set = 1;
- tiqdio_clear_summary_bit((__u32*)&spare_indicator);
- }
-
- q=(struct qdio_q*)tiq_list;
- do {
- if (!q)
- break;
- __tiqdio_inbound_processing(q, spare_ind_was_set);
- q=(struct qdio_q*)q->list_next;
- } while (q!=(struct qdio_q*)tiq_list);
-
-#ifdef QDIO_USE_PROCESSING_STATE
- q=(struct qdio_q*)tiq_list;
- do {
- int ret;
-
- ret = tiqdio_reset_processing_state(q, q_laps);
- switch (ret) {
- case 0:
- return;
- case 1:
- q_laps++;
- case 2:
- q = (struct qdio_q*)q->list_next;
- break;
- default:
- q_laps++;
- goto again;
- }
- } while (q!=(struct qdio_q*)tiq_list);
-#endif /* QDIO_USE_PROCESSING_STATE */
-}
-
-static void
-tiqdio_tl(unsigned long data)
-{
- QDIO_DBF_TEXT4(0,trace,"iqdio_tl");
-
- qdio_perf_stat_inc(&perf_stats.tl_runs);
-
- tiqdio_inbound_checks();
-}
-
-/********************* GENERAL HELPER_ROUTINES ***********************/
-
-static void
-qdio_release_irq_memory(struct qdio_irq *irq_ptr)
-{
- int i;
- struct qdio_q *q;
-
- for (i = 0; i < QDIO_MAX_QUEUES_PER_IRQ; i++) {
- q = irq_ptr->input_qs[i];
- if (q) {
- free_page((unsigned long) q->slib);
- kmem_cache_free(qdio_q_cache, q);
- }
- q = irq_ptr->output_qs[i];
- if (q) {
- free_page((unsigned long) q->slib);
- kmem_cache_free(qdio_q_cache, q);
- }
- }
- free_page((unsigned long) irq_ptr->qdr);
- free_page((unsigned long) irq_ptr);
-}
-
-static void
-qdio_set_impl_params(struct qdio_irq *irq_ptr,
- unsigned int qib_param_field_format,
- /* pointer to 128 bytes or NULL, if no param field */
- unsigned char *qib_param_field,
- /* pointer to no_queues*128 words of data or NULL */
- unsigned int no_input_qs,
- unsigned int no_output_qs,
- unsigned long *input_slib_elements,
- unsigned long *output_slib_elements)
-{
- int i,j;
-
- if (!irq_ptr)
- return;
-
- irq_ptr->qib.pfmt=qib_param_field_format;
- if (qib_param_field)
- memcpy(irq_ptr->qib.parm,qib_param_field,
- QDIO_MAX_BUFFERS_PER_Q);
-
- if (input_slib_elements)
- for (i=0;i<no_input_qs;i++) {
- for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
- irq_ptr->input_qs[i]->slib->slibe[j].parms=
- input_slib_elements[
- i*QDIO_MAX_BUFFERS_PER_Q+j];
- }
- if (output_slib_elements)
- for (i=0;i<no_output_qs;i++) {
- for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
- irq_ptr->output_qs[i]->slib->slibe[j].parms=
- output_slib_elements[
- i*QDIO_MAX_BUFFERS_PER_Q+j];
- }
-}
-
-static int
-qdio_alloc_qs(struct qdio_irq *irq_ptr,
- int no_input_qs, int no_output_qs)
-{
- int i;
- struct qdio_q *q;
-
- for (i = 0; i < no_input_qs; i++) {
- q = kmem_cache_alloc(qdio_q_cache, GFP_KERNEL);
- if (!q)
- return -ENOMEM;
- memset(q, 0, sizeof(*q));
-
- q->slib = (struct slib *) __get_free_page(GFP_KERNEL);
- if (!q->slib) {
- kmem_cache_free(qdio_q_cache, q);
- return -ENOMEM;
- }
- irq_ptr->input_qs[i]=q;
- }
-
- for (i = 0; i < no_output_qs; i++) {
- q = kmem_cache_alloc(qdio_q_cache, GFP_KERNEL);
- if (!q)
- return -ENOMEM;
- memset(q, 0, sizeof(*q));
-
- q->slib = (struct slib *) __get_free_page(GFP_KERNEL);
- if (!q->slib) {
- kmem_cache_free(qdio_q_cache, q);
- return -ENOMEM;
- }
- irq_ptr->output_qs[i]=q;
- }
- return 0;
-}
-
-static void
-qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev,
- int no_input_qs, int no_output_qs,
- qdio_handler_t *input_handler,
- qdio_handler_t *output_handler,
- unsigned long int_parm,int q_format,
- unsigned long flags,
- void **inbound_sbals_array,
- void **outbound_sbals_array)
-{
- struct qdio_q *q;
- int i,j;
- char dbf_text[20]; /* see qdio_initialize */
- void *ptr;
- int available;
-
- sprintf(dbf_text,"qfqs%4x",cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- for (i=0;i<no_input_qs;i++) {
- q=irq_ptr->input_qs[i];
-
- memset(q,0,((char*)&q->slib)-((char*)q));
- sprintf(dbf_text,"in-q%4x",i);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- QDIO_DBF_HEX0(0,setup,&q,sizeof(void*));
-
- memset(q->slib,0,PAGE_SIZE);
- q->sl=(struct sl*)(((char*)q->slib)+PAGE_SIZE/2);
-
- available=0;
-
- for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
- q->sbal[j]=*(inbound_sbals_array++);
-
- q->queue_type=q_format;
- q->int_parm=int_parm;
- q->schid = irq_ptr->schid;
- q->irq_ptr = irq_ptr;
- q->cdev = cdev;
- q->mask=1<<(31-i);
- q->q_no=i;
- q->is_input_q=1;
- q->first_to_check=0;
- q->last_move_ftc=0;
- q->handler=input_handler;
- q->dev_st_chg_ind=irq_ptr->dev_st_chg_ind;
-
- /* q->is_thinint_q isn't valid at this time, but
- * irq_ptr->is_thinint_irq is
- */
- if (irq_ptr->is_thinint_irq)
- tasklet_init(&q->tasklet, tiqdio_inbound_processing,
- (unsigned long) q);
- else
- tasklet_init(&q->tasklet, qdio_inbound_processing,
- (unsigned long) q);
-
- /* actually this is not used for inbound queues. yet. */
- atomic_set(&q->busy_siga_counter,0);
- q->timing.busy_start=0;
-
-/* for (j=0;j<QDIO_STATS_NUMBER;j++)
- q->timing.last_transfer_times[j]=(qdio_get_micros()/
- QDIO_STATS_NUMBER)*j;
- q->timing.last_transfer_index=QDIO_STATS_NUMBER-1;
-*/
-
- /* fill in slib */
- if (i>0) irq_ptr->input_qs[i-1]->slib->nsliba=
- (unsigned long)(q->slib);
- q->slib->sla=(unsigned long)(q->sl);
- q->slib->slsba=(unsigned long)(&q->slsb.acc.val[0]);
-
- /* fill in sl */
- for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
- q->sl->element[j].sbal=(unsigned long)(q->sbal[j]);
-
- QDIO_DBF_TEXT2(0,setup,"sl-sb-b0");
- ptr=(void*)q->sl;
- QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
- ptr=(void*)&q->slsb;
- QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
- ptr=(void*)q->sbal[0];
- QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
-
- /* fill in slsb */
- if (!irq_ptr->is_qebsm) {
- unsigned int count = 1;
- for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
- set_slsb(q, &j, SLSB_P_INPUT_NOT_INIT, &count);
- }
- }
-
- for (i=0;i<no_output_qs;i++) {
- q=irq_ptr->output_qs[i];
- memset(q,0,((char*)&q->slib)-((char*)q));
-
- sprintf(dbf_text,"outq%4x",i);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- QDIO_DBF_HEX0(0,setup,&q,sizeof(void*));
-
- memset(q->slib,0,PAGE_SIZE);
- q->sl=(struct sl*)(((char*)q->slib)+PAGE_SIZE/2);
-
- available=0;
-
- for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
- q->sbal[j]=*(outbound_sbals_array++);
-
- q->queue_type=q_format;
- if ((q->queue_type == QDIO_IQDIO_QFMT) &&
- (no_output_qs > 1) &&
- (i == no_output_qs-1))
- q->queue_type = QDIO_IQDIO_QFMT_ASYNCH;
- q->int_parm=int_parm;
- q->is_input_q=0;
- q->is_pci_out = 0;
- q->schid = irq_ptr->schid;
- q->cdev = cdev;
- q->irq_ptr = irq_ptr;
- q->mask=1<<(31-i);
- q->q_no=i;
- q->first_to_check=0;
- q->last_move_ftc=0;
- q->handler=output_handler;
-
- tasklet_init(&q->tasklet, qdio_outbound_processing,
- (unsigned long) q);
- setup_timer(&q->timer, qdio_outbound_processing,
- (unsigned long) q);
-
- atomic_set(&q->busy_siga_counter,0);
- q->timing.busy_start=0;
-
- /* fill in slib */
- if (i>0) irq_ptr->output_qs[i-1]->slib->nsliba=
- (unsigned long)(q->slib);
- q->slib->sla=(unsigned long)(q->sl);
- q->slib->slsba=(unsigned long)(&q->slsb.acc.val[0]);
-
- /* fill in sl */
- for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
- q->sl->element[j].sbal=(unsigned long)(q->sbal[j]);
-
- QDIO_DBF_TEXT2(0,setup,"sl-sb-b0");
- ptr=(void*)q->sl;
- QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
- ptr=(void*)&q->slsb;
- QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
- ptr=(void*)q->sbal[0];
- QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
-
- /* fill in slsb */
- if (!irq_ptr->is_qebsm) {
- unsigned int count = 1;
- for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
- set_slsb(q, &j, SLSB_P_OUTPUT_NOT_INIT, &count);
- }
- }
-}
-
-static void
-qdio_fill_thresholds(struct qdio_irq *irq_ptr,
- unsigned int no_input_qs,
- unsigned int no_output_qs,
- unsigned int min_input_threshold,
- unsigned int max_input_threshold,
- unsigned int min_output_threshold,
- unsigned int max_output_threshold)
-{
- int i;
- struct qdio_q *q;
-
- for (i=0;i<no_input_qs;i++) {
- q=irq_ptr->input_qs[i];
- q->timing.threshold=max_input_threshold;
-/* for (j=0;j<QDIO_STATS_CLASSES;j++) {
- q->threshold_classes[j].threshold=
- min_input_threshold+
- (max_input_threshold-min_input_threshold)/
- QDIO_STATS_CLASSES;
- }
- qdio_use_thresholds(q,QDIO_STATS_CLASSES/2);*/
- }
- for (i=0;i<no_output_qs;i++) {
- q=irq_ptr->output_qs[i];
- q->timing.threshold=max_output_threshold;
-/* for (j=0;j<QDIO_STATS_CLASSES;j++) {
- q->threshold_classes[j].threshold=
- min_output_threshold+
- (max_output_threshold-min_output_threshold)/
- QDIO_STATS_CLASSES;
- }
- qdio_use_thresholds(q,QDIO_STATS_CLASSES/2);*/
- }
-}
-
-static void tiqdio_thinint_handler(void *ind, void *drv_data)
-{
- QDIO_DBF_TEXT4(0,trace,"thin_int");
-
- qdio_perf_stat_inc(&perf_stats.thinints);
-
- /* SVS only when needed:
- * issue SVS to benefit from iqdio interrupt avoidance
- * (SVS clears AISOI)*/
- if (!omit_svs)
- tiqdio_clear_global_summary();
-
- tiqdio_inbound_checks();
-}
-
-static void
-qdio_set_state(struct qdio_irq *irq_ptr, enum qdio_irq_states state)
-{
- int i;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-
- QDIO_DBF_TEXT5(0,trace,"newstate");
- sprintf(dbf_text,"%4x%4x",irq_ptr->schid.sch_no,state);
- QDIO_DBF_TEXT5(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
-
- irq_ptr->state=state;
- for (i=0;i<irq_ptr->no_input_qs;i++)
- irq_ptr->input_qs[i]->state=state;
- for (i=0;i<irq_ptr->no_output_qs;i++)
- irq_ptr->output_qs[i]->state=state;
- mb();
-}
-
-static void
-qdio_irq_check_sense(struct subchannel_id schid, struct irb *irb)
-{
- char dbf_text[15];
-
- if (irb->esw.esw0.erw.cons) {
- sprintf(dbf_text,"sens%4x",schid.sch_no);
- QDIO_DBF_TEXT2(1,trace,dbf_text);
- QDIO_DBF_HEX0(0,sense,irb,QDIO_DBF_SENSE_LEN);
-
- QDIO_PRINT_WARN("sense data available on qdio channel.\n");
- QDIO_HEXDUMP16(WARN,"irb: ",irb);
- QDIO_HEXDUMP16(WARN,"sense data: ",irb->ecw);
- }
-
-}
-
-static void
-qdio_handle_pci(struct qdio_irq *irq_ptr)
-{
- int i;
- struct qdio_q *q;
-
- qdio_perf_stat_inc(&perf_stats.pcis);
- for (i=0;i<irq_ptr->no_input_qs;i++) {
- q=irq_ptr->input_qs[i];
- if (q->is_input_q&QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT)
- qdio_mark_q(q);
- else {
- qdio_perf_stat_dec(&perf_stats.tl_runs);
- __qdio_inbound_processing(q);
- }
- }
- if (!irq_ptr->hydra_gives_outbound_pcis)
- return;
- for (i=0;i<irq_ptr->no_output_qs;i++) {
- q=irq_ptr->output_qs[i];
- if (qdio_is_outbound_q_done(q))
- continue;
- qdio_perf_stat_dec(&perf_stats.tl_runs);
- if (!irq_ptr->sync_done_on_outb_pcis)
- SYNC_MEMORY;
- __qdio_outbound_processing(q);
- }
-}
-
-static void qdio_establish_handle_irq(struct ccw_device*, int, int);
-
-static void
-qdio_handle_activate_check(struct ccw_device *cdev, unsigned long intparm,
- int cstat, int dstat)
-{
- struct qdio_irq *irq_ptr;
- struct qdio_q *q;
- char dbf_text[15];
-
- irq_ptr = cdev->private->qdio_data;
-
- QDIO_DBF_TEXT2(1, trace, "ick2");
- sprintf(dbf_text,"%s", cdev->dev.bus_id);
- QDIO_DBF_TEXT2(1,trace,dbf_text);
- QDIO_DBF_HEX2(0,trace,&intparm,sizeof(int));
- QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int));
- QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int));
- QDIO_PRINT_ERR("received check condition on activate " \
- "queues on device %s (cs=x%x, ds=x%x).\n",
- cdev->dev.bus_id, cstat, dstat);
- if (irq_ptr->no_input_qs) {
- q=irq_ptr->input_qs[0];
- } else if (irq_ptr->no_output_qs) {
- q=irq_ptr->output_qs[0];
- } else {
- QDIO_PRINT_ERR("oops... no queue registered for device %s!?\n",
- cdev->dev.bus_id);
- goto omit_handler_call;
- }
- q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION|
- QDIO_STATUS_LOOK_FOR_ERROR,
- 0,0,0,-1,-1,q->int_parm);
-omit_handler_call:
- qdio_set_state(irq_ptr,QDIO_IRQ_STATE_STOPPED);
-
-}
-
-static void
-qdio_call_shutdown(struct work_struct *work)
-{
- struct ccw_device_private *priv;
- struct ccw_device *cdev;
-
- priv = container_of(work, struct ccw_device_private, kick_work);
- cdev = priv->cdev;
- qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
- put_device(&cdev->dev);
-}
-
-static void
-qdio_timeout_handler(struct ccw_device *cdev)
-{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
-
- QDIO_DBF_TEXT2(0, trace, "qtoh");
- sprintf(dbf_text, "%s", cdev->dev.bus_id);
- QDIO_DBF_TEXT2(0, trace, dbf_text);
-
- irq_ptr = cdev->private->qdio_data;
- sprintf(dbf_text, "state:%d", irq_ptr->state);
- QDIO_DBF_TEXT2(0, trace, dbf_text);
-
- switch (irq_ptr->state) {
- case QDIO_IRQ_STATE_INACTIVE:
- QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: timed out\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1,setup,"eq:timeo");
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
- break;
- case QDIO_IRQ_STATE_CLEANUP:
- QDIO_PRINT_INFO("Did not get interrupt on cleanup, "
- "irq=0.%x.%x.\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
- break;
- case QDIO_IRQ_STATE_ESTABLISHED:
- case QDIO_IRQ_STATE_ACTIVE:
- /* I/O has been terminated by common I/O layer. */
- QDIO_PRINT_INFO("Queues on irq 0.%x.%04x killed by cio.\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, trace, "cio:term");
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
- if (get_device(&cdev->dev)) {
- /* Can't call shutdown from interrupt context. */
- PREPARE_WORK(&cdev->private->kick_work,
- qdio_call_shutdown);
- queue_work(ccw_device_work, &cdev->private->kick_work);
- }
- break;
- default:
- BUG();
- }
- ccw_device_set_timeout(cdev, 0);
- wake_up(&cdev->private->wait_q);
-}
-
-static void
-qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
-{
- struct qdio_irq *irq_ptr;
- int cstat,dstat;
- char dbf_text[15];
-
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0, trace, "qint");
- sprintf(dbf_text, "%s", cdev->dev.bus_id);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
-
- if (!intparm) {
- QDIO_PRINT_ERR("got unsolicited interrupt in qdio " \
- "handler, device %s\n", cdev->dev.bus_id);
- return;
- }
-
- irq_ptr = cdev->private->qdio_data;
- if (!irq_ptr) {
- QDIO_DBF_TEXT2(1, trace, "uint");
- sprintf(dbf_text,"%s", cdev->dev.bus_id);
- QDIO_DBF_TEXT2(1,trace,dbf_text);
- QDIO_PRINT_ERR("received interrupt on unused device %s!\n",
- cdev->dev.bus_id);
- return;
- }
-
- if (IS_ERR(irb)) {
- /* Currently running i/o is in error. */
- switch (PTR_ERR(irb)) {
- case -EIO:
- QDIO_PRINT_ERR("i/o error on device %s\n",
- cdev->dev.bus_id);
- return;
- case -ETIMEDOUT:
- qdio_timeout_handler(cdev);
- return;
- default:
- QDIO_PRINT_ERR("unknown error state %ld on device %s\n",
- PTR_ERR(irb), cdev->dev.bus_id);
- return;
- }
- }
-
- qdio_irq_check_sense(irq_ptr->schid, irb);
-
-#ifdef CONFIG_QDIO_DEBUG
- sprintf(dbf_text, "state:%d", irq_ptr->state);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
-
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
-
- switch (irq_ptr->state) {
- case QDIO_IRQ_STATE_INACTIVE:
- qdio_establish_handle_irq(cdev, cstat, dstat);
- break;
-
- case QDIO_IRQ_STATE_CLEANUP:
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
- break;
-
- case QDIO_IRQ_STATE_ESTABLISHED:
- case QDIO_IRQ_STATE_ACTIVE:
- if (cstat & SCHN_STAT_PCI) {
- qdio_handle_pci(irq_ptr);
- break;
- }
-
- if ((cstat&~SCHN_STAT_PCI)||dstat) {
- qdio_handle_activate_check(cdev, intparm, cstat, dstat);
- break;
- }
- default:
- QDIO_PRINT_ERR("got interrupt for queues in state %d on " \
- "device %s?!\n",
- irq_ptr->state, cdev->dev.bus_id);
- }
- wake_up(&cdev->private->wait_q);
-
-}
-
-int
-qdio_synchronize(struct ccw_device *cdev, unsigned int flags,
- unsigned int queue_number)
-{
- int cc = 0;
- struct qdio_q *q;
- struct qdio_irq *irq_ptr;
- void *ptr;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15]="SyncXXXX";
-#endif
-
- irq_ptr = cdev->private->qdio_data;
- if (!irq_ptr)
- return -ENODEV;
-
-#ifdef CONFIG_QDIO_DEBUG
- *((int*)(&dbf_text[4])) = irq_ptr->schid.sch_no;
- QDIO_DBF_HEX4(0,trace,dbf_text,QDIO_DBF_TRACE_LEN);
- *((int*)(&dbf_text[0]))=flags;
- *((int*)(&dbf_text[4]))=queue_number;
- QDIO_DBF_HEX4(0,trace,dbf_text,QDIO_DBF_TRACE_LEN);
-#endif /* CONFIG_QDIO_DEBUG */
-
- if (flags&QDIO_FLAG_SYNC_INPUT) {
- q=irq_ptr->input_qs[queue_number];
- if (!q)
- return -EINVAL;
- if (!(irq_ptr->is_qebsm))
- cc = do_siga_sync(q->schid, 0, q->mask);
- } else if (flags&QDIO_FLAG_SYNC_OUTPUT) {
- q=irq_ptr->output_qs[queue_number];
- if (!q)
- return -EINVAL;
- if (!(irq_ptr->is_qebsm))
- cc = do_siga_sync(q->schid, q->mask, 0);
- } else
- return -EINVAL;
-
- ptr=&cc;
- if (cc)
- QDIO_DBF_HEX3(0,trace,&ptr,sizeof(int));
-
- return cc;
-}
-
-static int
-qdio_get_ssqd_information(struct subchannel_id *schid,
- struct qdio_chsc_ssqd **ssqd_area)
-{
- int result;
-
- QDIO_DBF_TEXT0(0, setup, "getssqd");
- *ssqd_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC);
- if (!ssqd_area) {
- QDIO_PRINT_WARN("Could not get memory for chsc on sch x%x.\n",
- schid->sch_no);
- return -ENOMEM;
- }
-
- (*ssqd_area)->request = (struct chsc_header) {
- .length = 0x0010,
- .code = 0x0024,
- };
- (*ssqd_area)->first_sch = schid->sch_no;
- (*ssqd_area)->last_sch = schid->sch_no;
- (*ssqd_area)->ssid = schid->ssid;
- result = chsc(*ssqd_area);
-
- if (result) {
- QDIO_PRINT_WARN("CHSC returned cc %i on sch 0.%x.%x.\n",
- result, schid->ssid, schid->sch_no);
- goto out;
- }
-
- if ((*ssqd_area)->response.code != QDIO_CHSC_RESPONSE_CODE_OK) {
- QDIO_PRINT_WARN("CHSC response is 0x%x on sch 0.%x.%x.\n",
- (*ssqd_area)->response.code,
- schid->ssid, schid->sch_no);
- goto out;
- }
- if (!((*ssqd_area)->flags & CHSC_FLAG_QDIO_CAPABILITY) ||
- !((*ssqd_area)->flags & CHSC_FLAG_VALIDITY) ||
- ((*ssqd_area)->sch != schid->sch_no)) {
- QDIO_PRINT_WARN("huh? problems checking out sch 0.%x.%x... " \
- "using all SIGAs.\n",
- schid->ssid, schid->sch_no);
- goto out;
- }
- return 0;
-out:
- return -EINVAL;
-}
-
-int
-qdio_get_ssqd_pct(struct ccw_device *cdev)
-{
- struct qdio_chsc_ssqd *ssqd_area;
- struct subchannel_id schid;
- char dbf_text[15];
- int rc;
- int pct = 0;
-
- QDIO_DBF_TEXT0(0, setup, "getpct");
- schid = ccw_device_get_subchannel_id(cdev);
- rc = qdio_get_ssqd_information(&schid, &ssqd_area);
- if (!rc)
- pct = (int)ssqd_area->pct;
- if (rc != -ENOMEM)
- mempool_free(ssqd_area, qdio_mempool_scssc);
- sprintf(dbf_text, "pct: %d", pct);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
- return pct;
-}
-EXPORT_SYMBOL(qdio_get_ssqd_pct);
-
-static void
-qdio_check_subchannel_qebsm(struct qdio_irq *irq_ptr, unsigned long token)
-{
- struct qdio_q *q;
- int i;
- unsigned int count, start_buf;
- char dbf_text[15];
-
- /*check if QEBSM is disabled */
- if (!(irq_ptr->is_qebsm) || !(irq_ptr->qdioac & 0x01)) {
- irq_ptr->is_qebsm = 0;
- irq_ptr->sch_token = 0;
- irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM;
- QDIO_DBF_TEXT0(0,setup,"noV=V");
- return;
- }
- irq_ptr->sch_token = token;
- /*input queue*/
- for (i = 0; i < irq_ptr->no_input_qs;i++) {
- q = irq_ptr->input_qs[i];
- count = QDIO_MAX_BUFFERS_PER_Q;
- start_buf = 0;
- set_slsb(q, &start_buf, SLSB_P_INPUT_NOT_INIT, &count);
- }
- sprintf(dbf_text,"V=V:%2x",irq_ptr->is_qebsm);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- sprintf(dbf_text,"%8lx",irq_ptr->sch_token);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- /*output queue*/
- for (i = 0; i < irq_ptr->no_output_qs; i++) {
- q = irq_ptr->output_qs[i];
- count = QDIO_MAX_BUFFERS_PER_Q;
- start_buf = 0;
- set_slsb(q, &start_buf, SLSB_P_OUTPUT_NOT_INIT, &count);
- }
-}
-
-static void
-qdio_get_ssqd_siga(struct qdio_irq *irq_ptr)
-{
- int rc;
- struct qdio_chsc_ssqd *ssqd_area;
-
- QDIO_DBF_TEXT0(0,setup,"getssqd");
- irq_ptr->qdioac = 0;
- rc = qdio_get_ssqd_information(&irq_ptr->schid, &ssqd_area);
- if (rc) {
- QDIO_PRINT_WARN("using all SIGAs for sch x%x.n",
- irq_ptr->schid.sch_no);
- irq_ptr->qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |
- CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
- CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
- irq_ptr->is_qebsm = 0;
- } else
- irq_ptr->qdioac = ssqd_area->qdioac1;
-
- qdio_check_subchannel_qebsm(irq_ptr, ssqd_area->sch_token);
- if (rc != -ENOMEM)
- mempool_free(ssqd_area, qdio_mempool_scssc);
-}
-
-static unsigned int
-tiqdio_check_chsc_availability(void)
-{
- char dbf_text[15];
-
- if (!css_characteristics_avail)
- return -EIO;
-
- /* Check for bit 41. */
- if (!css_general_characteristics.aif) {
- QDIO_PRINT_WARN("Adapter interruption facility not " \
- "installed.\n");
- return -ENOENT;
- }
-
- /* Check for bits 107 and 108. */
- if (!css_chsc_characteristics.scssc ||
- !css_chsc_characteristics.scsscf) {
- QDIO_PRINT_WARN("Set Chan Subsys. Char. & Fast-CHSCs " \
- "not available.\n");
- return -ENOENT;
- }
-
- /* Check for OSA/FCP thin interrupts (bit 67). */
- hydra_thinints = css_general_characteristics.aif_osa;
- sprintf(dbf_text,"hydrati%1x", hydra_thinints);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
-
-#ifdef CONFIG_64BIT
- /* Check for QEBSM support in general (bit 58). */
- is_passthrough = css_general_characteristics.qebsm;
-#endif
- sprintf(dbf_text,"cssQBS:%1x", is_passthrough);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
-
- /* Check for aif time delay disablement fac (bit 56). If installed,
- * omit svs even under lpar (good point by rick again) */
- omit_svs = css_general_characteristics.aif_tdd;
- sprintf(dbf_text,"omitsvs%1x", omit_svs);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- return 0;
-}
-
-
-static unsigned int
-tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero)
-{
- unsigned long real_addr_local_summary_bit;
- unsigned long real_addr_dev_st_chg_ind;
- void *ptr;
- char dbf_text[15];
-
- unsigned int resp_code;
- int result;
-
- struct {
- struct chsc_header request;
- u16 operation_code;
- u16 reserved1;
- u32 reserved2;
- u32 reserved3;
- u64 summary_indicator_addr;
- u64 subchannel_indicator_addr;
- u32 ks:4;
- u32 kc:4;
- u32 reserved4:21;
- u32 isc:3;
- u32 word_with_d_bit;
- /* set to 0x10000000 to enable
- * time delay disablement facility */
- u32 reserved5;
- struct subchannel_id schid;
- u32 reserved6[1004];
- struct chsc_header response;
- u32 reserved7;
- } *scssc_area;
-
- if (!irq_ptr->is_thinint_irq)
- return -ENODEV;
-
- if (reset_to_zero) {
- real_addr_local_summary_bit=0;
- real_addr_dev_st_chg_ind=0;
- } else {
- real_addr_local_summary_bit=
- virt_to_phys((volatile void *)tiqdio_ind);
- real_addr_dev_st_chg_ind=
- virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind);
- }
-
- scssc_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC);
- if (!scssc_area) {
- QDIO_PRINT_WARN("No memory for setting indicators on " \
- "subchannel 0.%x.%x.\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
- return -ENOMEM;
- }
- scssc_area->request = (struct chsc_header) {
- .length = 0x0fe0,
- .code = 0x0021,
- };
- scssc_area->operation_code = 0;
-
- scssc_area->summary_indicator_addr = real_addr_local_summary_bit;
- scssc_area->subchannel_indicator_addr = real_addr_dev_st_chg_ind;
- scssc_area->ks = QDIO_STORAGE_KEY;
- scssc_area->kc = QDIO_STORAGE_KEY;
- scssc_area->isc = TIQDIO_THININT_ISC;
- scssc_area->schid = irq_ptr->schid;
- /* enables the time delay disablement facility. Don't care
- * whether it is really there (i.e. we haven't checked for
- * it) */
- if (css_general_characteristics.aif_tdd)
- scssc_area->word_with_d_bit = 0x10000000;
- else
- QDIO_PRINT_WARN("Time delay disablement facility " \
- "not available\n");
-
- result = chsc(scssc_area);
- if (result) {
- QDIO_PRINT_WARN("could not set indicators on irq 0.%x.%x, " \
- "cc=%i.\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no,result);
- result = -EIO;
- goto out;
- }
-
- resp_code = scssc_area->response.code;
- if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) {
- QDIO_PRINT_WARN("response upon setting indicators " \
- "is 0x%x.\n",resp_code);
- sprintf(dbf_text,"sidR%4x",resp_code);
- QDIO_DBF_TEXT1(0,trace,dbf_text);
- QDIO_DBF_TEXT1(0,setup,dbf_text);
- ptr=&scssc_area->response;
- QDIO_DBF_HEX2(1,setup,&ptr,QDIO_DBF_SETUP_LEN);
- result = -EIO;
- goto out;
- }
-
- QDIO_DBF_TEXT2(0,setup,"setscind");
- QDIO_DBF_HEX2(0,setup,&real_addr_local_summary_bit,
- sizeof(unsigned long));
- QDIO_DBF_HEX2(0,setup,&real_addr_dev_st_chg_ind,sizeof(unsigned long));
- result = 0;
-out:
- mempool_free(scssc_area, qdio_mempool_scssc);
- return result;
-
-}
-
-static unsigned int
-tiqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target)
-{
- unsigned int resp_code;
- int result;
- void *ptr;
- char dbf_text[15];
-
- struct {
- struct chsc_header request;
- u16 operation_code;
- u16 reserved1;
- u32 reserved2;
- u32 reserved3;
- u32 reserved4[2];
- u32 delay_target;
- u32 reserved5[1009];
- struct chsc_header response;
- u32 reserved6;
- } *scsscf_area;
-
- if (!irq_ptr->is_thinint_irq)
- return -ENODEV;
-
- scsscf_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC);
- if (!scsscf_area) {
- QDIO_PRINT_WARN("No memory for setting delay target on " \
- "subchannel 0.%x.%x.\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
- return -ENOMEM;
- }
- scsscf_area->request = (struct chsc_header) {
- .length = 0x0fe0,
- .code = 0x1027,
- };
-
- scsscf_area->delay_target = delay_target<<16;
-
- result=chsc(scsscf_area);
- if (result) {
- QDIO_PRINT_WARN("could not set delay target on irq 0.%x.%x, " \
- "cc=%i. Continuing.\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
- result);
- result = -EIO;
- goto out;
- }
-
- resp_code = scsscf_area->response.code;
- if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) {
- QDIO_PRINT_WARN("response upon setting delay target " \
- "is 0x%x. Continuing.\n",resp_code);
- sprintf(dbf_text,"sdtR%4x",resp_code);
- QDIO_DBF_TEXT1(0,trace,dbf_text);
- QDIO_DBF_TEXT1(0,setup,dbf_text);
- ptr=&scsscf_area->response;
- QDIO_DBF_HEX2(1,trace,&ptr,QDIO_DBF_TRACE_LEN);
- }
- QDIO_DBF_TEXT2(0,trace,"delytrgt");
- QDIO_DBF_HEX2(0,trace,&delay_target,sizeof(unsigned long));
- result = 0; /* not critical */
-out:
- mempool_free(scsscf_area, qdio_mempool_scssc);
- return result;
-}
-
-int
-qdio_cleanup(struct ccw_device *cdev, int how)
-{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
- int rc;
-
- irq_ptr = cdev->private->qdio_data;
- if (!irq_ptr)
- return -ENODEV;
-
- sprintf(dbf_text,"qcln%4x",irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT1(0,trace,dbf_text);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
-
- rc = qdio_shutdown(cdev, how);
- if ((rc == 0) || (rc == -EINPROGRESS))
- rc = qdio_free(cdev);
- return rc;
-}
-
-int
-qdio_shutdown(struct ccw_device *cdev, int how)
-{
- struct qdio_irq *irq_ptr;
- int i;
- int result = 0;
- int rc;
- unsigned long flags;
- int timeout;
- char dbf_text[15];
-
- irq_ptr = cdev->private->qdio_data;
- if (!irq_ptr)
- return -ENODEV;
-
- down(&irq_ptr->setting_up_sema);
-
- sprintf(dbf_text,"qsqs%4x",irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT1(0,trace,dbf_text);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
-
- /* mark all qs as uninteresting */
- for (i=0;i<irq_ptr->no_input_qs;i++)
- atomic_set(&irq_ptr->input_qs[i]->is_in_shutdown,1);
-
- for (i=0;i<irq_ptr->no_output_qs;i++)
- atomic_set(&irq_ptr->output_qs[i]->is_in_shutdown,1);
-
- tasklet_kill(&tiqdio_tasklet);
-
- for (i=0;i<irq_ptr->no_input_qs;i++) {
- qdio_unmark_q(irq_ptr->input_qs[i]);
- tasklet_kill(&irq_ptr->input_qs[i]->tasklet);
- wait_event_interruptible_timeout(cdev->private->wait_q,
- !atomic_read(&irq_ptr->
- input_qs[i]->
- use_count),
- QDIO_NO_USE_COUNT_TIMEOUT);
- if (atomic_read(&irq_ptr->input_qs[i]->use_count))
- result=-EINPROGRESS;
- }
-
- for (i=0;i<irq_ptr->no_output_qs;i++) {
- tasklet_kill(&irq_ptr->output_qs[i]->tasklet);
- del_timer(&irq_ptr->output_qs[i]->timer);
- wait_event_interruptible_timeout(cdev->private->wait_q,
- !atomic_read(&irq_ptr->
- output_qs[i]->
- use_count),
- QDIO_NO_USE_COUNT_TIMEOUT);
- if (atomic_read(&irq_ptr->output_qs[i]->use_count))
- result=-EINPROGRESS;
- }
-
- /* cleanup subchannel */
- spin_lock_irqsave(get_ccwdev_lock(cdev),flags);
- if (how&QDIO_FLAG_CLEANUP_USING_CLEAR) {
- rc = ccw_device_clear(cdev, QDIO_DOING_CLEANUP);
- timeout=QDIO_CLEANUP_CLEAR_TIMEOUT;
- } else if (how&QDIO_FLAG_CLEANUP_USING_HALT) {
- rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
- timeout=QDIO_CLEANUP_HALT_TIMEOUT;
- } else { /* default behaviour */
- rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
- timeout=QDIO_CLEANUP_HALT_TIMEOUT;
- }
- if (rc == -ENODEV) {
- /* No need to wait for device no longer present. */
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
- spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
- } else if (((void *)cdev->handler != (void *)qdio_handler) && rc == 0) {
- /*
- * Whoever put another handler there, has to cope with the
- * interrupt theirself. Might happen if qdio_shutdown was
- * called on already shutdown queues, but this shouldn't have
- * bad side effects.
- */
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
- spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
- } else if (rc == 0) {
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_CLEANUP);
- ccw_device_set_timeout(cdev, timeout);
- spin_unlock_irqrestore(get_ccwdev_lock(cdev),flags);
-
- wait_event(cdev->private->wait_q,
- irq_ptr->state == QDIO_IRQ_STATE_INACTIVE ||
- irq_ptr->state == QDIO_IRQ_STATE_ERR);
- } else {
- QDIO_PRINT_INFO("ccw_device_{halt,clear} returned %d for "
- "device %s\n", result, cdev->dev.bus_id);
- spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
- result = rc;
- goto out;
- }
- if (irq_ptr->is_thinint_irq) {
- qdio_put_indicator((__u32*)irq_ptr->dev_st_chg_ind);
- tiqdio_set_subchannel_ind(irq_ptr,1);
- /* reset adapter interrupt indicators */
- }
-
- /* exchange int handlers, if necessary */
- if ((void*)cdev->handler == (void*)qdio_handler)
- cdev->handler=irq_ptr->original_int_handler;
-
- /* Ignore errors. */
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
- ccw_device_set_timeout(cdev, 0);
-out:
- up(&irq_ptr->setting_up_sema);
- return result;
-}
-
-int
-qdio_free(struct ccw_device *cdev)
-{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
-
- irq_ptr = cdev->private->qdio_data;
- if (!irq_ptr)
- return -ENODEV;
-
- down(&irq_ptr->setting_up_sema);
-
- sprintf(dbf_text,"qfqs%4x",irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT1(0,trace,dbf_text);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
-
- cdev->private->qdio_data = NULL;
-
- up(&irq_ptr->setting_up_sema);
-
- qdio_release_irq_memory(irq_ptr);
- module_put(THIS_MODULE);
- return 0;
-}
-
-static void
-qdio_allocate_do_dbf(struct qdio_initialize *init_data)
-{
- char dbf_text[20]; /* if a printf printed out more than 8 chars */
-
- sprintf(dbf_text,"qfmt:%x",init_data->q_format);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- QDIO_DBF_HEX0(0,setup,init_data->adapter_name,8);
- sprintf(dbf_text,"qpff%4x",init_data->qib_param_field_format);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- QDIO_DBF_HEX0(0,setup,&init_data->qib_param_field,sizeof(char*));
- QDIO_DBF_HEX0(0,setup,&init_data->input_slib_elements,sizeof(long*));
- QDIO_DBF_HEX0(0,setup,&init_data->output_slib_elements,sizeof(long*));
- sprintf(dbf_text,"miit%4x",init_data->min_input_threshold);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- sprintf(dbf_text,"mait%4x",init_data->max_input_threshold);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- sprintf(dbf_text,"miot%4x",init_data->min_output_threshold);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- sprintf(dbf_text,"maot%4x",init_data->max_output_threshold);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- sprintf(dbf_text,"niq:%4x",init_data->no_input_qs);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- sprintf(dbf_text,"noq:%4x",init_data->no_output_qs);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- QDIO_DBF_HEX0(0,setup,&init_data->input_handler,sizeof(void*));
- QDIO_DBF_HEX0(0,setup,&init_data->output_handler,sizeof(void*));
- QDIO_DBF_HEX0(0,setup,&init_data->int_parm,sizeof(long));
- QDIO_DBF_HEX0(0,setup,&init_data->flags,sizeof(long));
- QDIO_DBF_HEX0(0,setup,&init_data->input_sbal_addr_array,sizeof(void*));
- QDIO_DBF_HEX0(0,setup,&init_data->output_sbal_addr_array,sizeof(void*));
-}
-
-static void
-qdio_allocate_fill_input_desc(struct qdio_irq *irq_ptr, int i, int iqfmt)
-{
- irq_ptr->input_qs[i]->is_iqdio_q = iqfmt;
- irq_ptr->input_qs[i]->is_thinint_q = irq_ptr->is_thinint_irq;
-
- irq_ptr->qdr->qdf0[i].sliba=(unsigned long)(irq_ptr->input_qs[i]->slib);
-
- irq_ptr->qdr->qdf0[i].sla=(unsigned long)(irq_ptr->input_qs[i]->sl);
-
- irq_ptr->qdr->qdf0[i].slsba=
- (unsigned long)(&irq_ptr->input_qs[i]->slsb.acc.val[0]);
-
- irq_ptr->qdr->qdf0[i].akey=QDIO_STORAGE_KEY;
- irq_ptr->qdr->qdf0[i].bkey=QDIO_STORAGE_KEY;
- irq_ptr->qdr->qdf0[i].ckey=QDIO_STORAGE_KEY;
- irq_ptr->qdr->qdf0[i].dkey=QDIO_STORAGE_KEY;
-}
-
-static void
-qdio_allocate_fill_output_desc(struct qdio_irq *irq_ptr, int i,
- int j, int iqfmt)
-{
- irq_ptr->output_qs[i]->is_iqdio_q = iqfmt;
- irq_ptr->output_qs[i]->is_thinint_q = irq_ptr->is_thinint_irq;
-
- irq_ptr->qdr->qdf0[i+j].sliba=(unsigned long)(irq_ptr->output_qs[i]->slib);
-
- irq_ptr->qdr->qdf0[i+j].sla=(unsigned long)(irq_ptr->output_qs[i]->sl);
-
- irq_ptr->qdr->qdf0[i+j].slsba=
- (unsigned long)(&irq_ptr->output_qs[i]->slsb.acc.val[0]);
-
- irq_ptr->qdr->qdf0[i+j].akey=QDIO_STORAGE_KEY;
- irq_ptr->qdr->qdf0[i+j].bkey=QDIO_STORAGE_KEY;
- irq_ptr->qdr->qdf0[i+j].ckey=QDIO_STORAGE_KEY;
- irq_ptr->qdr->qdf0[i+j].dkey=QDIO_STORAGE_KEY;
-}
-
-
-static void
-qdio_initialize_set_siga_flags_input(struct qdio_irq *irq_ptr)
-{
- int i;
-
- for (i=0;i<irq_ptr->no_input_qs;i++) {
- irq_ptr->input_qs[i]->siga_sync=
- irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY;
- irq_ptr->input_qs[i]->siga_in=
- irq_ptr->qdioac&CHSC_FLAG_SIGA_INPUT_NECESSARY;
- irq_ptr->input_qs[i]->siga_out=
- irq_ptr->qdioac&CHSC_FLAG_SIGA_OUTPUT_NECESSARY;
- irq_ptr->input_qs[i]->siga_sync_done_on_thinints=
- irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS;
- irq_ptr->input_qs[i]->hydra_gives_outbound_pcis=
- irq_ptr->hydra_gives_outbound_pcis;
- irq_ptr->input_qs[i]->siga_sync_done_on_outb_tis=
- ((irq_ptr->qdioac&
- (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS|
- CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS))==
- (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS|
- CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS));
-
- }
-}
-
-static void
-qdio_initialize_set_siga_flags_output(struct qdio_irq *irq_ptr)
-{
- int i;
-
- for (i=0;i<irq_ptr->no_output_qs;i++) {
- irq_ptr->output_qs[i]->siga_sync=
- irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY;
- irq_ptr->output_qs[i]->siga_in=
- irq_ptr->qdioac&CHSC_FLAG_SIGA_INPUT_NECESSARY;
- irq_ptr->output_qs[i]->siga_out=
- irq_ptr->qdioac&CHSC_FLAG_SIGA_OUTPUT_NECESSARY;
- irq_ptr->output_qs[i]->siga_sync_done_on_thinints=
- irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS;
- irq_ptr->output_qs[i]->hydra_gives_outbound_pcis=
- irq_ptr->hydra_gives_outbound_pcis;
- irq_ptr->output_qs[i]->siga_sync_done_on_outb_tis=
- ((irq_ptr->qdioac&
- (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS|
- CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS))==
- (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS|
- CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS));
-
- }
-}
-
-static int
-qdio_establish_irq_check_for_errors(struct ccw_device *cdev, int cstat,
- int dstat)
-{
- char dbf_text[15];
- struct qdio_irq *irq_ptr;
-
- irq_ptr = cdev->private->qdio_data;
-
- if (cstat || (dstat & ~(DEV_STAT_CHN_END|DEV_STAT_DEV_END))) {
- sprintf(dbf_text,"ick1%4x",irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1,trace,dbf_text);
- QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int));
- QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int));
- QDIO_PRINT_ERR("received check condition on establish " \
- "queues on irq 0.%x.%x (cs=x%x, ds=x%x).\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
- cstat,dstat);
- qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ERR);
- }
-
- if (!(dstat & DEV_STAT_DEV_END)) {
- QDIO_DBF_TEXT2(1,setup,"eq:no de");
- QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat));
- QDIO_DBF_HEX2(0,setup,&cstat, sizeof(cstat));
- QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: didn't get "
- "device end: dstat=%02x, cstat=%02x\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
- dstat, cstat);
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
- return 1;
- }
-
- if (dstat & ~(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) {
- QDIO_DBF_TEXT2(1,setup,"eq:badio");
- QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat));
- QDIO_DBF_HEX2(0,setup,&cstat, sizeof(cstat));
- QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: got "
- "the following devstat: dstat=%02x, "
- "cstat=%02x\n", irq_ptr->schid.ssid,
- irq_ptr->schid.sch_no, dstat, cstat);
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
- return 1;
- }
- return 0;
-}
-
-static void
-qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat)
-{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
-
- irq_ptr = cdev->private->qdio_data;
-
- sprintf(dbf_text,"qehi%4x",cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- QDIO_DBF_TEXT0(0,trace,dbf_text);
-
- if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat)) {
- ccw_device_set_timeout(cdev, 0);
- return;
- }
-
- qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED);
- ccw_device_set_timeout(cdev, 0);
-}
-
-int
-qdio_initialize(struct qdio_initialize *init_data)
-{
- int rc;
- char dbf_text[15];
-
- sprintf(dbf_text,"qini%4x",init_data->cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- QDIO_DBF_TEXT0(0,trace,dbf_text);
-
- rc = qdio_allocate(init_data);
- if (rc == 0) {
- rc = qdio_establish(init_data);
- if (rc != 0)
- qdio_free(init_data->cdev);
- }
-
- return rc;
-}
-
-
-int
-qdio_allocate(struct qdio_initialize *init_data)
-{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
-
- sprintf(dbf_text,"qalc%4x",init_data->cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- QDIO_DBF_TEXT0(0,trace,dbf_text);
- if ( (init_data->no_input_qs>QDIO_MAX_QUEUES_PER_IRQ) ||
- (init_data->no_output_qs>QDIO_MAX_QUEUES_PER_IRQ) ||
- ((init_data->no_input_qs) && (!init_data->input_handler)) ||
- ((init_data->no_output_qs) && (!init_data->output_handler)) )
- return -EINVAL;
-
- if (!init_data->input_sbal_addr_array)
- return -EINVAL;
-
- if (!init_data->output_sbal_addr_array)
- return -EINVAL;
-
- qdio_allocate_do_dbf(init_data);
-
- /* create irq */
- irq_ptr = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
-
- QDIO_DBF_TEXT0(0,setup,"irq_ptr:");
- QDIO_DBF_HEX0(0,setup,&irq_ptr,sizeof(void*));
-
- if (!irq_ptr) {
- QDIO_PRINT_ERR("allocation of irq_ptr failed!\n");
- return -ENOMEM;
- }
-
- init_MUTEX(&irq_ptr->setting_up_sema);
-
- /* QDR must be in DMA area since CCW data address is only 32 bit */
- irq_ptr->qdr = (struct qdr *) __get_free_page(GFP_KERNEL | GFP_DMA);
- if (!(irq_ptr->qdr)) {
- free_page((unsigned long) irq_ptr);
- QDIO_PRINT_ERR("allocation of irq_ptr->qdr failed!\n");
- return -ENOMEM;
- }
- QDIO_DBF_TEXT0(0,setup,"qdr:");
- QDIO_DBF_HEX0(0,setup,&irq_ptr->qdr,sizeof(void*));
-
- if (qdio_alloc_qs(irq_ptr,
- init_data->no_input_qs,
- init_data->no_output_qs)) {
- QDIO_PRINT_ERR("queue allocation failed!\n");
- qdio_release_irq_memory(irq_ptr);
- return -ENOMEM;
- }
-
- init_data->cdev->private->qdio_data = irq_ptr;
-
- qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE);
-
- return 0;
-}
-
-static int qdio_fill_irq(struct qdio_initialize *init_data)
-{
- int i;
- char dbf_text[15];
- struct ciw *ciw;
- int is_iqdio;
- struct qdio_irq *irq_ptr;
-
- irq_ptr = init_data->cdev->private->qdio_data;
-
- memset(irq_ptr,0,((char*)&irq_ptr->qdr)-((char*)irq_ptr));
-
- /* wipes qib.ac, required by ar7063 */
- memset(irq_ptr->qdr,0,sizeof(struct qdr));
-
- irq_ptr->int_parm=init_data->int_parm;
-
- irq_ptr->schid = ccw_device_get_subchannel_id(init_data->cdev);
- irq_ptr->no_input_qs=init_data->no_input_qs;
- irq_ptr->no_output_qs=init_data->no_output_qs;
-
- if (init_data->q_format==QDIO_IQDIO_QFMT) {
- irq_ptr->is_iqdio_irq=1;
- irq_ptr->is_thinint_irq=1;
- } else {
- irq_ptr->is_iqdio_irq=0;
- irq_ptr->is_thinint_irq=hydra_thinints;
- }
- sprintf(dbf_text,"is_i_t%1x%1x",
- irq_ptr->is_iqdio_irq,irq_ptr->is_thinint_irq);
- QDIO_DBF_TEXT2(0,setup,dbf_text);
-
- if (irq_ptr->is_thinint_irq) {
- irq_ptr->dev_st_chg_ind = qdio_get_indicator();
- QDIO_DBF_HEX1(0,setup,&irq_ptr->dev_st_chg_ind,sizeof(void*));
- if (!irq_ptr->dev_st_chg_ind) {
- QDIO_PRINT_WARN("no indicator location available " \
- "for irq 0.%x.%x\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
- qdio_release_irq_memory(irq_ptr);
- return -ENOBUFS;
- }
- }
-
- /* defaults */
- irq_ptr->equeue.cmd=DEFAULT_ESTABLISH_QS_CMD;
- irq_ptr->equeue.count=DEFAULT_ESTABLISH_QS_COUNT;
- irq_ptr->aqueue.cmd=DEFAULT_ACTIVATE_QS_CMD;
- irq_ptr->aqueue.count=DEFAULT_ACTIVATE_QS_COUNT;
-
- qdio_fill_qs(irq_ptr, init_data->cdev,
- init_data->no_input_qs,
- init_data->no_output_qs,
- init_data->input_handler,
- init_data->output_handler,init_data->int_parm,
- init_data->q_format,init_data->flags,
- init_data->input_sbal_addr_array,
- init_data->output_sbal_addr_array);
-
- if (!try_module_get(THIS_MODULE)) {
- QDIO_PRINT_CRIT("try_module_get() failed!\n");
- qdio_release_irq_memory(irq_ptr);
- return -EINVAL;
- }
-
- qdio_fill_thresholds(irq_ptr,init_data->no_input_qs,
- init_data->no_output_qs,
- init_data->min_input_threshold,
- init_data->max_input_threshold,
- init_data->min_output_threshold,
- init_data->max_output_threshold);
-
- /* fill in qdr */
- irq_ptr->qdr->qfmt=init_data->q_format;
- irq_ptr->qdr->iqdcnt=init_data->no_input_qs;
- irq_ptr->qdr->oqdcnt=init_data->no_output_qs;
- irq_ptr->qdr->iqdsz=sizeof(struct qdesfmt0)/4; /* size in words */
- irq_ptr->qdr->oqdsz=sizeof(struct qdesfmt0)/4;
-
- irq_ptr->qdr->qiba=(unsigned long)&irq_ptr->qib;
- irq_ptr->qdr->qkey=QDIO_STORAGE_KEY;
-
- /* fill in qib */
- irq_ptr->is_qebsm = is_passthrough;
- if (irq_ptr->is_qebsm)
- irq_ptr->qib.rflags |= QIB_RFLAGS_ENABLE_QEBSM;
-
- irq_ptr->qib.qfmt=init_data->q_format;
- if (init_data->no_input_qs)
- irq_ptr->qib.isliba=(unsigned long)(irq_ptr->input_qs[0]->slib);
- if (init_data->no_output_qs)
- irq_ptr->qib.osliba=(unsigned long)(irq_ptr->output_qs[0]->slib);
- memcpy(irq_ptr->qib.ebcnam,init_data->adapter_name,8);
-
- qdio_set_impl_params(irq_ptr,init_data->qib_param_field_format,
- init_data->qib_param_field,
- init_data->no_input_qs,
- init_data->no_output_qs,
- init_data->input_slib_elements,
- init_data->output_slib_elements);
-
- /* first input descriptors, then output descriptors */
- is_iqdio = (init_data->q_format == QDIO_IQDIO_QFMT) ? 1 : 0;
- for (i=0;i<init_data->no_input_qs;i++)
- qdio_allocate_fill_input_desc(irq_ptr, i, is_iqdio);
-
- for (i=0;i<init_data->no_output_qs;i++)
- qdio_allocate_fill_output_desc(irq_ptr, i,
- init_data->no_input_qs,
- is_iqdio);
-
- /* qdr, qib, sls, slsbs, slibs, sbales filled. */
-
- /* get qdio commands */
- ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_EQUEUE);
- if (!ciw) {
- QDIO_DBF_TEXT2(1,setup,"no eq");
- QDIO_PRINT_INFO("No equeue CIW found for QDIO commands. "
- "Trying to use default.\n");
- } else
- irq_ptr->equeue = *ciw;
- ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_AQUEUE);
- if (!ciw) {
- QDIO_DBF_TEXT2(1,setup,"no aq");
- QDIO_PRINT_INFO("No aqueue CIW found for QDIO commands. "
- "Trying to use default.\n");
- } else
- irq_ptr->aqueue = *ciw;
-
- /* Set new interrupt handler. */
- irq_ptr->original_int_handler = init_data->cdev->handler;
- init_data->cdev->handler = qdio_handler;
-
- return 0;
-}
-
-int
-qdio_establish(struct qdio_initialize *init_data)
-{
- struct qdio_irq *irq_ptr;
- unsigned long saveflags;
- int result, result2;
- struct ccw_device *cdev;
- char dbf_text[20];
-
- cdev=init_data->cdev;
- irq_ptr = cdev->private->qdio_data;
- if (!irq_ptr)
- return -EINVAL;
-
- if (cdev->private->state != DEV_STATE_ONLINE)
- return -EINVAL;
-
- down(&irq_ptr->setting_up_sema);
-
- qdio_fill_irq(init_data);
-
- /* the thinint CHSC stuff */
- if (irq_ptr->is_thinint_irq) {
-
- result = tiqdio_set_subchannel_ind(irq_ptr,0);
- if (result) {
- up(&irq_ptr->setting_up_sema);
- qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
- return result;
- }
- tiqdio_set_delay_target(irq_ptr,TIQDIO_DELAY_TARGET);
- }
-
- sprintf(dbf_text,"qest%4x",cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- QDIO_DBF_TEXT0(0,trace,dbf_text);
-
- /* establish q */
- irq_ptr->ccw.cmd_code=irq_ptr->equeue.cmd;
- irq_ptr->ccw.flags=CCW_FLAG_SLI;
- irq_ptr->ccw.count=irq_ptr->equeue.count;
- irq_ptr->ccw.cda=QDIO_GET_ADDR(irq_ptr->qdr);
-
- spin_lock_irqsave(get_ccwdev_lock(cdev),saveflags);
-
- ccw_device_set_options_mask(cdev, 0);
- result = ccw_device_start(cdev, &irq_ptr->ccw,
- QDIO_DOING_ESTABLISH, 0, 0);
- if (result) {
- result2 = ccw_device_start(cdev, &irq_ptr->ccw,
- QDIO_DOING_ESTABLISH, 0, 0);
- sprintf(dbf_text,"eq:io%4x",result);
- QDIO_DBF_TEXT2(1,setup,dbf_text);
- if (result2) {
- sprintf(dbf_text,"eq:io%4x",result);
- QDIO_DBF_TEXT2(1,setup,dbf_text);
- }
- QDIO_PRINT_WARN("establish queues on irq 0.%x.%04x: do_IO " \
- "returned %i, next try returned %i\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
- result, result2);
- result=result2;
- if (result)
- ccw_device_set_timeout(cdev, 0);
- }
-
- spin_unlock_irqrestore(get_ccwdev_lock(cdev),saveflags);
-
- if (result) {
- up(&irq_ptr->setting_up_sema);
- qdio_shutdown(cdev,QDIO_FLAG_CLEANUP_USING_CLEAR);
- return result;
- }
-
- wait_event_interruptible_timeout(cdev->private->wait_q,
- irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED ||
- irq_ptr->state == QDIO_IRQ_STATE_ERR,
- QDIO_ESTABLISH_TIMEOUT);
-
- if (irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED)
- result = 0;
- else {
- up(&irq_ptr->setting_up_sema);
- qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
- return -EIO;
- }
-
- qdio_get_ssqd_siga(irq_ptr);
- /* if this gets set once, we're running under VM and can omit SVSes */
- if (irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY)
- omit_svs=1;
-
- sprintf(dbf_text,"qdioac%2x",irq_ptr->qdioac);
- QDIO_DBF_TEXT2(0,setup,dbf_text);
-
- sprintf(dbf_text,"qib ac%2x",irq_ptr->qib.ac);
- QDIO_DBF_TEXT2(0,setup,dbf_text);
-
- irq_ptr->hydra_gives_outbound_pcis=
- irq_ptr->qib.ac&QIB_AC_OUTBOUND_PCI_SUPPORTED;
- irq_ptr->sync_done_on_outb_pcis=
- irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS;
-
- qdio_initialize_set_siga_flags_input(irq_ptr);
- qdio_initialize_set_siga_flags_output(irq_ptr);
-
- up(&irq_ptr->setting_up_sema);
-
- return result;
-
-}
-
-int
-qdio_activate(struct ccw_device *cdev, int flags)
-{
- struct qdio_irq *irq_ptr;
- int i,result=0,result2;
- unsigned long saveflags;
- char dbf_text[20]; /* see qdio_initialize */
-
- irq_ptr = cdev->private->qdio_data;
- if (!irq_ptr)
- return -ENODEV;
-
- if (cdev->private->state != DEV_STATE_ONLINE)
- return -EINVAL;
-
- down(&irq_ptr->setting_up_sema);
- if (irq_ptr->state==QDIO_IRQ_STATE_INACTIVE) {
- result=-EBUSY;
- goto out;
- }
-
- sprintf(dbf_text,"qact%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(0,setup,dbf_text);
- QDIO_DBF_TEXT2(0,trace,dbf_text);
-
- /* activate q */
- irq_ptr->ccw.cmd_code=irq_ptr->aqueue.cmd;
- irq_ptr->ccw.flags=CCW_FLAG_SLI;
- irq_ptr->ccw.count=irq_ptr->aqueue.count;
- irq_ptr->ccw.cda=QDIO_GET_ADDR(0);
-
- spin_lock_irqsave(get_ccwdev_lock(cdev),saveflags);
-
- ccw_device_set_timeout(cdev, 0);
- ccw_device_set_options(cdev, CCWDEV_REPORT_ALL);
- result=ccw_device_start(cdev,&irq_ptr->ccw,QDIO_DOING_ACTIVATE,
- 0, DOIO_DENY_PREFETCH);
- if (result) {
- result2=ccw_device_start(cdev,&irq_ptr->ccw,
- QDIO_DOING_ACTIVATE,0,0);
- sprintf(dbf_text,"aq:io%4x",result);
- QDIO_DBF_TEXT2(1,setup,dbf_text);
- if (result2) {
- sprintf(dbf_text,"aq:io%4x",result);
- QDIO_DBF_TEXT2(1,setup,dbf_text);
- }
- QDIO_PRINT_WARN("activate queues on irq 0.%x.%04x: do_IO " \
- "returned %i, next try returned %i\n",
- irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
- result, result2);
- result=result2;
- }
-
- spin_unlock_irqrestore(get_ccwdev_lock(cdev),saveflags);
- if (result)
- goto out;
-
- for (i=0;i<irq_ptr->no_input_qs;i++) {
- if (irq_ptr->is_thinint_irq) {
- /*
- * that way we know, that, if we will get interrupted
- * by tiqdio_inbound_processing, qdio_unmark_q will
- * not be called
- */
- qdio_reserve_q(irq_ptr->input_qs[i]);
- qdio_mark_tiq(irq_ptr->input_qs[i]);
- qdio_release_q(irq_ptr->input_qs[i]);
- }
- }
-
- if (flags&QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT) {
- for (i=0;i<irq_ptr->no_input_qs;i++) {
- irq_ptr->input_qs[i]->is_input_q|=
- QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT;
- }
- }
-
- msleep(QDIO_ACTIVATE_TIMEOUT);
- switch (irq_ptr->state) {
- case QDIO_IRQ_STATE_STOPPED:
- case QDIO_IRQ_STATE_ERR:
- up(&irq_ptr->setting_up_sema);
- qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
- down(&irq_ptr->setting_up_sema);
- result = -EIO;
- break;
- default:
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ACTIVE);
- result = 0;
- }
- out:
- up(&irq_ptr->setting_up_sema);
-
- return result;
-}
-
-/* buffers filled forwards again to make Rick happy */
-static void
-qdio_do_qdio_fill_input(struct qdio_q *q, unsigned int qidx,
- unsigned int count, struct qdio_buffer *buffers)
-{
- struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;
- int tmp = 0;
-
- qidx &= (QDIO_MAX_BUFFERS_PER_Q - 1);
- if (irq->is_qebsm) {
- while (count) {
- tmp = set_slsb(q, &qidx, SLSB_CU_INPUT_EMPTY, &count);
- if (!tmp)
- return;
- }
- return;
- }
- for (;;) {
- set_slsb(q, &qidx, SLSB_CU_INPUT_EMPTY, &count);
- count--;
- if (!count) break;
- qidx = (qidx + 1) & (QDIO_MAX_BUFFERS_PER_Q - 1);
- }
-}
-
-static void
-qdio_do_qdio_fill_output(struct qdio_q *q, unsigned int qidx,
- unsigned int count, struct qdio_buffer *buffers)
-{
- struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;
- int tmp = 0;
-
- qidx &= (QDIO_MAX_BUFFERS_PER_Q - 1);
- if (irq->is_qebsm) {
- while (count) {
- tmp = set_slsb(q, &qidx, SLSB_CU_OUTPUT_PRIMED, &count);
- if (!tmp)
- return;
- }
- return;
- }
-
- for (;;) {
- set_slsb(q, &qidx, SLSB_CU_OUTPUT_PRIMED, &count);
- count--;
- if (!count) break;
- qidx = (qidx + 1) & (QDIO_MAX_BUFFERS_PER_Q - 1);
- }
-}
-
-static void
-do_qdio_handle_inbound(struct qdio_q *q, unsigned int callflags,
- unsigned int qidx, unsigned int count,
- struct qdio_buffer *buffers)
-{
- int used_elements;
-
- /* This is the inbound handling of queues */
- used_elements=atomic_add_return(count, &q->number_of_buffers_used) - count;
-
- qdio_do_qdio_fill_input(q,qidx,count,buffers);
-
- if ((used_elements+count==QDIO_MAX_BUFFERS_PER_Q)&&
- (callflags&QDIO_FLAG_UNDER_INTERRUPT))
- atomic_xchg(&q->polling,0);
-
- if (used_elements)
- return;
- if (callflags&QDIO_FLAG_DONT_SIGA)
- return;
- if (q->siga_in) {
- int result;
-
- result=qdio_siga_input(q);
- if (result) {
- if (q->siga_error)
- q->error_status_flags|=
- QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR;
- q->error_status_flags|=QDIO_STATUS_LOOK_FOR_ERROR;
- q->siga_error=result;
- }
- }
-
- qdio_mark_q(q);
-}
-
-static void
-do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags,
- unsigned int qidx, unsigned int count,
- struct qdio_buffer *buffers)
-{
- int used_elements;
- unsigned int cnt, start_buf;
- unsigned char state = 0;
- struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;
-
- /* This is the outbound handling of queues */
- qdio_do_qdio_fill_output(q,qidx,count,buffers);
-
- used_elements=atomic_add_return(count, &q->number_of_buffers_used) - count;
-
- if (callflags&QDIO_FLAG_DONT_SIGA) {
- qdio_perf_stat_inc(&perf_stats.outbound_cnt);
- return;
- }
- if (callflags & QDIO_FLAG_PCI_OUT)
- q->is_pci_out = 1;
- else
- q->is_pci_out = 0;
- if (q->is_iqdio_q) {
- /* one siga for every sbal */
- while (count--)
- qdio_kick_outbound_q(q);
-
- __qdio_outbound_processing(q);
- } else {
- /* under VM, we do a SIGA sync unconditionally */
- SYNC_MEMORY;
- else {
- /*
- * w/o shadow queues (else branch of
- * SYNC_MEMORY :-/ ), we try to
- * fast-requeue buffers
- */
- if (irq->is_qebsm) {
- cnt = 1;
- start_buf = ((qidx+QDIO_MAX_BUFFERS_PER_Q-1) &
- (QDIO_MAX_BUFFERS_PER_Q-1));
- qdio_do_eqbs(q, &state, &start_buf, &cnt);
- } else
- state = q->slsb.acc.val[(qidx+QDIO_MAX_BUFFERS_PER_Q-1)
- &(QDIO_MAX_BUFFERS_PER_Q-1) ];
- if (state != SLSB_CU_OUTPUT_PRIMED) {
- qdio_kick_outbound_q(q);
- } else {
- QDIO_DBF_TEXT3(0,trace, "fast-req");
- qdio_perf_stat_inc(&perf_stats.fast_reqs);
- }
- }
- /*
- * only marking the q could take too long,
- * the upper layer module could do a lot of
- * traffic in that time
- */
- __qdio_outbound_processing(q);
- }
-
- qdio_perf_stat_inc(&perf_stats.outbound_cnt);
-}
-
-/* count must be 1 in iqdio */
-int
-do_QDIO(struct ccw_device *cdev,unsigned int callflags,
- unsigned int queue_number, unsigned int qidx,
- unsigned int count,struct qdio_buffer *buffers)
-{
- struct qdio_irq *irq_ptr;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[20];
-
- sprintf(dbf_text,"doQD%04x",cdev->private->schid.sch_no);
- QDIO_DBF_TEXT3(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
-
- if ( (qidx>QDIO_MAX_BUFFERS_PER_Q) ||
- (count>QDIO_MAX_BUFFERS_PER_Q) ||
- (queue_number>QDIO_MAX_QUEUES_PER_IRQ) )
- return -EINVAL;
-
- if (count==0)
- return 0;
-
- irq_ptr = cdev->private->qdio_data;
- if (!irq_ptr)
- return -ENODEV;
-
-#ifdef CONFIG_QDIO_DEBUG
- if (callflags&QDIO_FLAG_SYNC_INPUT)
- QDIO_DBF_HEX3(0,trace,&irq_ptr->input_qs[queue_number],
- sizeof(void*));
- else
- QDIO_DBF_HEX3(0,trace,&irq_ptr->output_qs[queue_number],
- sizeof(void*));
- sprintf(dbf_text,"flag%04x",callflags);
- QDIO_DBF_TEXT3(0,trace,dbf_text);
- sprintf(dbf_text,"qi%02xct%02x",qidx,count);
- QDIO_DBF_TEXT3(0,trace,dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
-
- if (irq_ptr->state!=QDIO_IRQ_STATE_ACTIVE)
- return -EBUSY;
-
- if (callflags&QDIO_FLAG_SYNC_INPUT)
- do_qdio_handle_inbound(irq_ptr->input_qs[queue_number],
- callflags, qidx, count, buffers);
- else if (callflags&QDIO_FLAG_SYNC_OUTPUT)
- do_qdio_handle_outbound(irq_ptr->output_qs[queue_number],
- callflags, qidx, count, buffers);
- else {
- QDIO_DBF_TEXT3(1,trace,"doQD:inv");
- return -EINVAL;
- }
- return 0;
-}
-
-static int
-qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset,
- int buffer_length, int *eof, void *data)
-{
- int c=0;
-
- /* we are always called with buffer_length=4k, so we all
- deliver on the first read */
- if (offset>0)
- return 0;
-
-#define _OUTP_IT(x...) c+=sprintf(buffer+c,x)
-#ifdef CONFIG_64BIT
- _OUTP_IT("Number of tasklet runs (total) : %li\n",
- (long)atomic64_read(&perf_stats.tl_runs));
- _OUTP_IT("Inbound tasklet runs tried/retried : %li/%li\n",
- (long)atomic64_read(&perf_stats.inbound_tl_runs),
- (long)atomic64_read(&perf_stats.inbound_tl_runs_resched));
- _OUTP_IT("Inbound-thin tasklet runs tried/retried : %li/%li\n",
- (long)atomic64_read(&perf_stats.inbound_thin_tl_runs),
- (long)atomic64_read(&perf_stats.inbound_thin_tl_runs_resched));
- _OUTP_IT("Outbound tasklet runs tried/retried : %li/%li\n",
- (long)atomic64_read(&perf_stats.outbound_tl_runs),
- (long)atomic64_read(&perf_stats.outbound_tl_runs_resched));
- _OUTP_IT("\n");
- _OUTP_IT("Number of SIGA sync's issued : %li\n",
- (long)atomic64_read(&perf_stats.siga_syncs));
- _OUTP_IT("Number of SIGA in's issued : %li\n",
- (long)atomic64_read(&perf_stats.siga_ins));
- _OUTP_IT("Number of SIGA out's issued : %li\n",
- (long)atomic64_read(&perf_stats.siga_outs));
- _OUTP_IT("Number of PCIs caught : %li\n",
- (long)atomic64_read(&perf_stats.pcis));
- _OUTP_IT("Number of adapter interrupts caught : %li\n",
- (long)atomic64_read(&perf_stats.thinints));
- _OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA) : %li\n",
- (long)atomic64_read(&perf_stats.fast_reqs));
- _OUTP_IT("\n");
- _OUTP_IT("Number of inbound transfers : %li\n",
- (long)atomic64_read(&perf_stats.inbound_cnt));
- _OUTP_IT("Number of do_QDIOs outbound : %li\n",
- (long)atomic64_read(&perf_stats.outbound_cnt));
-#else /* CONFIG_64BIT */
- _OUTP_IT("Number of tasklet runs (total) : %i\n",
- atomic_read(&perf_stats.tl_runs));
- _OUTP_IT("Inbound tasklet runs tried/retried : %i/%i\n",
- atomic_read(&perf_stats.inbound_tl_runs),
- atomic_read(&perf_stats.inbound_tl_runs_resched));
- _OUTP_IT("Inbound-thin tasklet runs tried/retried : %i/%i\n",
- atomic_read(&perf_stats.inbound_thin_tl_runs),
- atomic_read(&perf_stats.inbound_thin_tl_runs_resched));
- _OUTP_IT("Outbound tasklet runs tried/retried : %i/%i\n",
- atomic_read(&perf_stats.outbound_tl_runs),
- atomic_read(&perf_stats.outbound_tl_runs_resched));
- _OUTP_IT("\n");
- _OUTP_IT("Number of SIGA sync's issued : %i\n",
- atomic_read(&perf_stats.siga_syncs));
- _OUTP_IT("Number of SIGA in's issued : %i\n",
- atomic_read(&perf_stats.siga_ins));
- _OUTP_IT("Number of SIGA out's issued : %i\n",
- atomic_read(&perf_stats.siga_outs));
- _OUTP_IT("Number of PCIs caught : %i\n",
- atomic_read(&perf_stats.pcis));
- _OUTP_IT("Number of adapter interrupts caught : %i\n",
- atomic_read(&perf_stats.thinints));
- _OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA) : %i\n",
- atomic_read(&perf_stats.fast_reqs));
- _OUTP_IT("\n");
- _OUTP_IT("Number of inbound transfers : %i\n",
- atomic_read(&perf_stats.inbound_cnt));
- _OUTP_IT("Number of do_QDIOs outbound : %i\n",
- atomic_read(&perf_stats.outbound_cnt));
-#endif /* CONFIG_64BIT */
- _OUTP_IT("\n");
-
- return c;
-}
-
-static struct proc_dir_entry *qdio_perf_proc_file;
-
-static void
-qdio_add_procfs_entry(void)
-{
- proc_perf_file_registration=0;
- qdio_perf_proc_file=create_proc_entry(QDIO_PERF,
- S_IFREG|0444,NULL);
- if (qdio_perf_proc_file) {
- qdio_perf_proc_file->read_proc=&qdio_perf_procfile_read;
- } else proc_perf_file_registration=-1;
-
- if (proc_perf_file_registration)
- QDIO_PRINT_WARN("was not able to register perf. " \
- "proc-file (%i).\n",
- proc_perf_file_registration);
-}
-
-static void
-qdio_remove_procfs_entry(void)
-{
- if (!proc_perf_file_registration) /* means if it went ok earlier */
- remove_proc_entry(QDIO_PERF,NULL);
-}
-
-/**
- * attributes in sysfs
- *****************************************************************************/
-
-static ssize_t
-qdio_performance_stats_show(struct bus_type *bus, char *buf)
-{
- return sprintf(buf, "%i\n", qdio_performance_stats ? 1 : 0);
-}
-
-static ssize_t
-qdio_performance_stats_store(struct bus_type *bus, const char *buf, size_t count)
-{
- unsigned long i;
- int ret;
-
- ret = strict_strtoul(buf, 16, &i);
- if (!ret && ((i == 0) || (i == 1))) {
- if (i == qdio_performance_stats)
- return count;
- qdio_performance_stats = i;
- if (i==0) {
- /* reset perf. stat. info */
-#ifdef CONFIG_64BIT
- atomic64_set(&perf_stats.tl_runs, 0);
- atomic64_set(&perf_stats.outbound_tl_runs, 0);
- atomic64_set(&perf_stats.inbound_tl_runs, 0);
- atomic64_set(&perf_stats.inbound_tl_runs_resched, 0);
- atomic64_set(&perf_stats.inbound_thin_tl_runs, 0);
- atomic64_set(&perf_stats.inbound_thin_tl_runs_resched,
- 0);
- atomic64_set(&perf_stats.siga_outs, 0);
- atomic64_set(&perf_stats.siga_ins, 0);
- atomic64_set(&perf_stats.siga_syncs, 0);
- atomic64_set(&perf_stats.pcis, 0);
- atomic64_set(&perf_stats.thinints, 0);
- atomic64_set(&perf_stats.fast_reqs, 0);
- atomic64_set(&perf_stats.outbound_cnt, 0);
- atomic64_set(&perf_stats.inbound_cnt, 0);
-#else /* CONFIG_64BIT */
- atomic_set(&perf_stats.tl_runs, 0);
- atomic_set(&perf_stats.outbound_tl_runs, 0);
- atomic_set(&perf_stats.inbound_tl_runs, 0);
- atomic_set(&perf_stats.inbound_tl_runs_resched, 0);
- atomic_set(&perf_stats.inbound_thin_tl_runs, 0);
- atomic_set(&perf_stats.inbound_thin_tl_runs_resched, 0);
- atomic_set(&perf_stats.siga_outs, 0);
- atomic_set(&perf_stats.siga_ins, 0);
- atomic_set(&perf_stats.siga_syncs, 0);
- atomic_set(&perf_stats.pcis, 0);
- atomic_set(&perf_stats.thinints, 0);
- atomic_set(&perf_stats.fast_reqs, 0);
- atomic_set(&perf_stats.outbound_cnt, 0);
- atomic_set(&perf_stats.inbound_cnt, 0);
-#endif /* CONFIG_64BIT */
- }
- } else {
- QDIO_PRINT_ERR("QDIO performance_stats: write 0 or 1 to this file!\n");
- return -EINVAL;
- }
- return count;
-}
-
-static BUS_ATTR(qdio_performance_stats, 0644, qdio_performance_stats_show,
- qdio_performance_stats_store);
-
-static void
-tiqdio_register_thinints(void)
-{
- char dbf_text[20];
-
- tiqdio_ind =
- s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL);
- if (IS_ERR(tiqdio_ind)) {
- sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_ind));
- QDIO_DBF_TEXT0(0,setup,dbf_text);
- QDIO_PRINT_ERR("failed to register adapter handler " \
- "(rc=%li).\nAdapter interrupts might " \
- "not work. Continuing.\n",
- PTR_ERR(tiqdio_ind));
- tiqdio_ind = NULL;
- }
-}
-
-static void
-tiqdio_unregister_thinints(void)
-{
- if (tiqdio_ind)
- s390_unregister_adapter_interrupt(tiqdio_ind);
-}
-
-static int
-qdio_get_qdio_memory(void)
-{
- int i;
- indicator_used[0]=1;
-
- for (i=1;i<INDICATORS_PER_CACHELINE;i++)
- indicator_used[i]=0;
- indicators = kzalloc(sizeof(__u32)*(INDICATORS_PER_CACHELINE),
- GFP_KERNEL);
- if (!indicators)
- return -ENOMEM;
- return 0;
-}
-
-static void
-qdio_release_qdio_memory(void)
-{
- kfree(indicators);
-}
-
-static void
-qdio_unregister_dbf_views(void)
-{
- if (qdio_dbf_setup)
- debug_unregister(qdio_dbf_setup);
- if (qdio_dbf_sbal)
- debug_unregister(qdio_dbf_sbal);
- if (qdio_dbf_sense)
- debug_unregister(qdio_dbf_sense);
- if (qdio_dbf_trace)
- debug_unregister(qdio_dbf_trace);
-#ifdef CONFIG_QDIO_DEBUG
- if (qdio_dbf_slsb_out)
- debug_unregister(qdio_dbf_slsb_out);
- if (qdio_dbf_slsb_in)
- debug_unregister(qdio_dbf_slsb_in);
-#endif /* CONFIG_QDIO_DEBUG */
-}
-
-static int
-qdio_register_dbf_views(void)
-{
- qdio_dbf_setup=debug_register(QDIO_DBF_SETUP_NAME,
- QDIO_DBF_SETUP_PAGES,
- QDIO_DBF_SETUP_NR_AREAS,
- QDIO_DBF_SETUP_LEN);
- if (!qdio_dbf_setup)
- goto oom;
- debug_register_view(qdio_dbf_setup,&debug_hex_ascii_view);
- debug_set_level(qdio_dbf_setup,QDIO_DBF_SETUP_LEVEL);
-
- qdio_dbf_sbal=debug_register(QDIO_DBF_SBAL_NAME,
- QDIO_DBF_SBAL_PAGES,
- QDIO_DBF_SBAL_NR_AREAS,
- QDIO_DBF_SBAL_LEN);
- if (!qdio_dbf_sbal)
- goto oom;
-
- debug_register_view(qdio_dbf_sbal,&debug_hex_ascii_view);
- debug_set_level(qdio_dbf_sbal,QDIO_DBF_SBAL_LEVEL);
-
- qdio_dbf_sense=debug_register(QDIO_DBF_SENSE_NAME,
- QDIO_DBF_SENSE_PAGES,
- QDIO_DBF_SENSE_NR_AREAS,
- QDIO_DBF_SENSE_LEN);
- if (!qdio_dbf_sense)
- goto oom;
-
- debug_register_view(qdio_dbf_sense,&debug_hex_ascii_view);
- debug_set_level(qdio_dbf_sense,QDIO_DBF_SENSE_LEVEL);
-
- qdio_dbf_trace=debug_register(QDIO_DBF_TRACE_NAME,
- QDIO_DBF_TRACE_PAGES,
- QDIO_DBF_TRACE_NR_AREAS,
- QDIO_DBF_TRACE_LEN);
- if (!qdio_dbf_trace)
- goto oom;
-
- debug_register_view(qdio_dbf_trace,&debug_hex_ascii_view);
- debug_set_level(qdio_dbf_trace,QDIO_DBF_TRACE_LEVEL);
-
-#ifdef CONFIG_QDIO_DEBUG
- qdio_dbf_slsb_out=debug_register(QDIO_DBF_SLSB_OUT_NAME,
- QDIO_DBF_SLSB_OUT_PAGES,
- QDIO_DBF_SLSB_OUT_NR_AREAS,
- QDIO_DBF_SLSB_OUT_LEN);
- if (!qdio_dbf_slsb_out)
- goto oom;
- debug_register_view(qdio_dbf_slsb_out,&debug_hex_ascii_view);
- debug_set_level(qdio_dbf_slsb_out,QDIO_DBF_SLSB_OUT_LEVEL);
-
- qdio_dbf_slsb_in=debug_register(QDIO_DBF_SLSB_IN_NAME,
- QDIO_DBF_SLSB_IN_PAGES,
- QDIO_DBF_SLSB_IN_NR_AREAS,
- QDIO_DBF_SLSB_IN_LEN);
- if (!qdio_dbf_slsb_in)
- goto oom;
- debug_register_view(qdio_dbf_slsb_in,&debug_hex_ascii_view);
- debug_set_level(qdio_dbf_slsb_in,QDIO_DBF_SLSB_IN_LEVEL);
-#endif /* CONFIG_QDIO_DEBUG */
- return 0;
-oom:
- QDIO_PRINT_ERR("not enough memory for dbf.\n");
- qdio_unregister_dbf_views();
- return -ENOMEM;
-}
-
-static void *qdio_mempool_alloc(gfp_t gfp_mask, void *size)
-{
- return (void *) get_zeroed_page(gfp_mask|GFP_DMA);
-}
-
-static void qdio_mempool_free(void *element, void *size)
-{
- free_page((unsigned long) element);
-}
-
-static int __init
-init_QDIO(void)
-{
- int res;
- void *ptr;
-
- printk("qdio: loading %s\n",version);
-
- res=qdio_get_qdio_memory();
- if (res)
- return res;
-
- qdio_q_cache = kmem_cache_create("qdio_q", sizeof(struct qdio_q),
- 256, 0, NULL);
- if (!qdio_q_cache) {
- qdio_release_qdio_memory();
- return -ENOMEM;
- }
-
- res = qdio_register_dbf_views();
- if (res) {
- kmem_cache_destroy(qdio_q_cache);
- qdio_release_qdio_memory();
- return res;
- }
-
- QDIO_DBF_TEXT0(0,setup,"initQDIO");
- res = bus_create_file(&ccw_bus_type, &bus_attr_qdio_performance_stats);
-
- memset((void*)&perf_stats,0,sizeof(perf_stats));
- QDIO_DBF_TEXT0(0,setup,"perfstat");
- ptr=&perf_stats;
- QDIO_DBF_HEX0(0,setup,&ptr,sizeof(void*));
-
- qdio_add_procfs_entry();
-
- qdio_mempool_scssc = mempool_create(QDIO_MEMPOOL_SCSSC_ELEMENTS,
- qdio_mempool_alloc,
- qdio_mempool_free, NULL);
-
- if (tiqdio_check_chsc_availability())
- QDIO_PRINT_ERR("Not all CHSCs supported. Continuing.\n");
-
- tiqdio_register_thinints();
-
- return 0;
- }
-
-static void __exit
-cleanup_QDIO(void)
-{
- tiqdio_unregister_thinints();
- qdio_remove_procfs_entry();
- qdio_release_qdio_memory();
- qdio_unregister_dbf_views();
- mempool_destroy(qdio_mempool_scssc);
- kmem_cache_destroy(qdio_q_cache);
- bus_remove_file(&ccw_bus_type, &bus_attr_qdio_performance_stats);
- printk("qdio: %s: module removed\n",version);
-}
-
-module_init(init_QDIO);
-module_exit(cleanup_QDIO);
-
-EXPORT_SYMBOL(qdio_allocate);
-EXPORT_SYMBOL(qdio_establish);
-EXPORT_SYMBOL(qdio_initialize);
-EXPORT_SYMBOL(qdio_activate);
-EXPORT_SYMBOL(do_QDIO);
-EXPORT_SYMBOL(qdio_shutdown);
-EXPORT_SYMBOL(qdio_free);
-EXPORT_SYMBOL(qdio_cleanup);
-EXPORT_SYMBOL(qdio_synchronize);
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index c3df6b2c38b..e3ea1d5f281 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -1,66 +1,28 @@
+/*
+ * linux/drivers/s390/cio/qdio.h
+ *
+ * Copyright 2000,2008 IBM Corp.
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ * Jan Glauber <jang@linux.vnet.ibm.com>
+ */
#ifndef _CIO_QDIO_H
#define _CIO_QDIO_H
#include <asm/page.h>
+#include <asm/schid.h>
+#include "chsc.h"
-#include "schid.h"
-
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_VERBOSE_LEVEL 9
-#else /* CONFIG_QDIO_DEBUG */
-#define QDIO_VERBOSE_LEVEL 5
-#endif /* CONFIG_QDIO_DEBUG */
-#define QDIO_USE_PROCESSING_STATE
-
-#define QDIO_MINIMAL_BH_RELIEF_TIME 16
-#define QDIO_TIMER_POLL_VALUE 1
-#define IQDIO_TIMER_POLL_VALUE 1
+#define QDIO_BUSY_BIT_PATIENCE 100 /* 100 microseconds */
+#define QDIO_BUSY_BIT_GIVE_UP 2000000 /* 2 seconds = eternity */
+#define QDIO_INPUT_THRESHOLD 500 /* 500 microseconds */
/*
- * unfortunately this can't be (QDIO_MAX_BUFFERS_PER_Q*4/3) or so -- as
- * we never know, whether we'll get initiative again, e.g. to give the
- * transmit skb's back to the stack, however the stack may be waiting for
- * them... therefore we define 4 as threshold to start polling (which
- * will stop as soon as the asynchronous queue catches up)
- * btw, this only applies to the asynchronous HiperSockets queue
+ * if an asynchronous HiperSockets queue runs full, the 10 seconds timer wait
+ * till next initiative to give transmitted skbs back to the stack is too long.
+ * Therefore polling is started in case of multicast queue is filled more
+ * than 50 percent.
*/
-#define IQDIO_FILL_LEVEL_TO_POLL 4
-
-#define TIQDIO_THININT_ISC 3
-#define TIQDIO_DELAY_TARGET 0
-#define QDIO_BUSY_BIT_PATIENCE 100 /* in microsecs */
-#define QDIO_BUSY_BIT_GIVE_UP 10000000 /* 10 seconds */
-#define IQDIO_GLOBAL_LAPS 2 /* GLOBAL_LAPS are not used as we */
-#define IQDIO_GLOBAL_LAPS_INT 1 /* don't global summary */
-#define IQDIO_LOCAL_LAPS 4
-#define IQDIO_LOCAL_LAPS_INT 1
-#define IQDIO_GLOBAL_SUMMARY_CC_MASK 2
-/*#define IQDIO_IQDC_INT_PARM 0x1234*/
-
-#define QDIO_Q_LAPS 5
-
-#define QDIO_STORAGE_KEY PAGE_DEFAULT_KEY
-
-#define L2_CACHELINE_SIZE 256
-#define INDICATORS_PER_CACHELINE (L2_CACHELINE_SIZE/sizeof(__u32))
-
-#define QDIO_PERF "qdio_perf"
-
-/* must be a power of 2 */
-/*#define QDIO_STATS_NUMBER 4
-
-#define QDIO_STATS_CLASSES 2
-#define QDIO_STATS_COUNT_NEEDED 2*/
-
-#define QDIO_NO_USE_COUNT_TIMEOUT (1*HZ) /* wait for 1 sec on each q before
- exiting without having use_count
- of the queue to 0 */
-
-#define QDIO_ESTABLISH_TIMEOUT (1*HZ)
-#define QDIO_CLEANUP_CLEAR_TIMEOUT (20*HZ)
-#define QDIO_CLEANUP_HALT_TIMEOUT (10*HZ)
-#define QDIO_FORCE_CHECK_TIMEOUT (10*HZ)
-#define QDIO_ACTIVATE_TIMEOUT (5) /* 5 ms */
+#define QDIO_IQDIO_POLL_LVL 65 /* HS multicast queue */
enum qdio_irq_states {
QDIO_IRQ_STATE_INACTIVE,
@@ -72,565 +34,355 @@ enum qdio_irq_states {
NR_QDIO_IRQ_STATES,
};
-/* used as intparm in do_IO: */
-#define QDIO_DOING_SENSEID 0
-#define QDIO_DOING_ESTABLISH 1
-#define QDIO_DOING_ACTIVATE 2
-#define QDIO_DOING_CLEANUP 3
-
-/************************* DEBUG FACILITY STUFF *********************/
-
-#define QDIO_DBF_HEX(ex,name,level,addr,len) \
- do { \
- if (ex) \
- debug_exception(qdio_dbf_##name,level,(void*)(addr),len); \
- else \
- debug_event(qdio_dbf_##name,level,(void*)(addr),len); \
- } while (0)
-#define QDIO_DBF_TEXT(ex,name,level,text) \
- do { \
- if (ex) \
- debug_text_exception(qdio_dbf_##name,level,text); \
- else \
- debug_text_event(qdio_dbf_##name,level,text); \
- } while (0)
-
-
-#define QDIO_DBF_HEX0(ex,name,addr,len) QDIO_DBF_HEX(ex,name,0,addr,len)
-#define QDIO_DBF_HEX1(ex,name,addr,len) QDIO_DBF_HEX(ex,name,1,addr,len)
-#define QDIO_DBF_HEX2(ex,name,addr,len) QDIO_DBF_HEX(ex,name,2,addr,len)
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_HEX3(ex,name,addr,len) QDIO_DBF_HEX(ex,name,3,addr,len)
-#define QDIO_DBF_HEX4(ex,name,addr,len) QDIO_DBF_HEX(ex,name,4,addr,len)
-#define QDIO_DBF_HEX5(ex,name,addr,len) QDIO_DBF_HEX(ex,name,5,addr,len)
-#define QDIO_DBF_HEX6(ex,name,addr,len) QDIO_DBF_HEX(ex,name,6,addr,len)
-#else /* CONFIG_QDIO_DEBUG */
-#define QDIO_DBF_HEX3(ex,name,addr,len) do {} while (0)
-#define QDIO_DBF_HEX4(ex,name,addr,len) do {} while (0)
-#define QDIO_DBF_HEX5(ex,name,addr,len) do {} while (0)
-#define QDIO_DBF_HEX6(ex,name,addr,len) do {} while (0)
-#endif /* CONFIG_QDIO_DEBUG */
-
-#define QDIO_DBF_TEXT0(ex,name,text) QDIO_DBF_TEXT(ex,name,0,text)
-#define QDIO_DBF_TEXT1(ex,name,text) QDIO_DBF_TEXT(ex,name,1,text)
-#define QDIO_DBF_TEXT2(ex,name,text) QDIO_DBF_TEXT(ex,name,2,text)
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_TEXT3(ex,name,text) QDIO_DBF_TEXT(ex,name,3,text)
-#define QDIO_DBF_TEXT4(ex,name,text) QDIO_DBF_TEXT(ex,name,4,text)
-#define QDIO_DBF_TEXT5(ex,name,text) QDIO_DBF_TEXT(ex,name,5,text)
-#define QDIO_DBF_TEXT6(ex,name,text) QDIO_DBF_TEXT(ex,name,6,text)
-#else /* CONFIG_QDIO_DEBUG */
-#define QDIO_DBF_TEXT3(ex,name,text) do {} while (0)
-#define QDIO_DBF_TEXT4(ex,name,text) do {} while (0)
-#define QDIO_DBF_TEXT5(ex,name,text) do {} while (0)
-#define QDIO_DBF_TEXT6(ex,name,text) do {} while (0)
-#endif /* CONFIG_QDIO_DEBUG */
-
-#define QDIO_DBF_SETUP_NAME "qdio_setup"
-#define QDIO_DBF_SETUP_LEN 8
-#define QDIO_DBF_SETUP_PAGES 4
-#define QDIO_DBF_SETUP_NR_AREAS 1
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_SETUP_LEVEL 6
-#else /* CONFIG_QDIO_DEBUG */
-#define QDIO_DBF_SETUP_LEVEL 2
-#endif /* CONFIG_QDIO_DEBUG */
-
-#define QDIO_DBF_SBAL_NAME "qdio_labs" /* sbal */
-#define QDIO_DBF_SBAL_LEN 256
-#define QDIO_DBF_SBAL_PAGES 4
-#define QDIO_DBF_SBAL_NR_AREAS 2
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_SBAL_LEVEL 6
-#else /* CONFIG_QDIO_DEBUG */
-#define QDIO_DBF_SBAL_LEVEL 2
-#endif /* CONFIG_QDIO_DEBUG */
-
-#define QDIO_DBF_TRACE_NAME "qdio_trace"
-#define QDIO_DBF_TRACE_LEN 8
-#define QDIO_DBF_TRACE_NR_AREAS 2
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_TRACE_PAGES 16
-#define QDIO_DBF_TRACE_LEVEL 4 /* -------- could be even more verbose here */
-#else /* CONFIG_QDIO_DEBUG */
-#define QDIO_DBF_TRACE_PAGES 4
-#define QDIO_DBF_TRACE_LEVEL 2
-#endif /* CONFIG_QDIO_DEBUG */
-
-#define QDIO_DBF_SENSE_NAME "qdio_sense"
-#define QDIO_DBF_SENSE_LEN 64
-#define QDIO_DBF_SENSE_PAGES 2
-#define QDIO_DBF_SENSE_NR_AREAS 1
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_SENSE_LEVEL 6
-#else /* CONFIG_QDIO_DEBUG */
-#define QDIO_DBF_SENSE_LEVEL 2
-#endif /* CONFIG_QDIO_DEBUG */
-
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_TRACE_QTYPE QDIO_ZFCP_QFMT
-
-#define QDIO_DBF_SLSB_OUT_NAME "qdio_slsb_out"
-#define QDIO_DBF_SLSB_OUT_LEN QDIO_MAX_BUFFERS_PER_Q
-#define QDIO_DBF_SLSB_OUT_PAGES 256
-#define QDIO_DBF_SLSB_OUT_NR_AREAS 1
-#define QDIO_DBF_SLSB_OUT_LEVEL 6
-
-#define QDIO_DBF_SLSB_IN_NAME "qdio_slsb_in"
-#define QDIO_DBF_SLSB_IN_LEN QDIO_MAX_BUFFERS_PER_Q
-#define QDIO_DBF_SLSB_IN_PAGES 256
-#define QDIO_DBF_SLSB_IN_NR_AREAS 1
-#define QDIO_DBF_SLSB_IN_LEVEL 6
-#endif /* CONFIG_QDIO_DEBUG */
-
-#define QDIO_PRINTK_HEADER QDIO_NAME ": "
-
-#if QDIO_VERBOSE_LEVEL>8
-#define QDIO_PRINT_STUPID(x...) printk( KERN_DEBUG QDIO_PRINTK_HEADER x)
-#else
-#define QDIO_PRINT_STUPID(x...) do { } while (0)
-#endif
-
-#if QDIO_VERBOSE_LEVEL>7
-#define QDIO_PRINT_ALL(x...) printk( QDIO_PRINTK_HEADER x)
-#else
-#define QDIO_PRINT_ALL(x...) do { } while (0)
-#endif
+/* used as intparm in do_IO */
+#define QDIO_DOING_ESTABLISH 1
+#define QDIO_DOING_ACTIVATE 2
+#define QDIO_DOING_CLEANUP 3
+
+#define SLSB_STATE_NOT_INIT 0x0
+#define SLSB_STATE_EMPTY 0x1
+#define SLSB_STATE_PRIMED 0x2
+#define SLSB_STATE_HALTED 0xe
+#define SLSB_STATE_ERROR 0xf
+#define SLSB_TYPE_INPUT 0x0
+#define SLSB_TYPE_OUTPUT 0x20
+#define SLSB_OWNER_PROG 0x80
+#define SLSB_OWNER_CU 0x40
+
+#define SLSB_P_INPUT_NOT_INIT \
+ (SLSB_OWNER_PROG | SLSB_TYPE_INPUT | SLSB_STATE_NOT_INIT) /* 0x80 */
+#define SLSB_P_INPUT_ACK \
+ (SLSB_OWNER_PROG | SLSB_TYPE_INPUT | SLSB_STATE_EMPTY) /* 0x81 */
+#define SLSB_CU_INPUT_EMPTY \
+ (SLSB_OWNER_CU | SLSB_TYPE_INPUT | SLSB_STATE_EMPTY) /* 0x41 */
+#define SLSB_P_INPUT_PRIMED \
+ (SLSB_OWNER_PROG | SLSB_TYPE_INPUT | SLSB_STATE_PRIMED) /* 0x82 */
+#define SLSB_P_INPUT_HALTED \
+ (SLSB_OWNER_PROG | SLSB_TYPE_INPUT | SLSB_STATE_HALTED) /* 0x8e */
+#define SLSB_P_INPUT_ERROR \
+ (SLSB_OWNER_PROG | SLSB_TYPE_INPUT | SLSB_STATE_ERROR) /* 0x8f */
+#define SLSB_P_OUTPUT_NOT_INIT \
+ (SLSB_OWNER_PROG | SLSB_TYPE_OUTPUT | SLSB_STATE_NOT_INIT) /* 0xa0 */
+#define SLSB_P_OUTPUT_EMPTY \
+ (SLSB_OWNER_PROG | SLSB_TYPE_OUTPUT | SLSB_STATE_EMPTY) /* 0xa1 */
+#define SLSB_CU_OUTPUT_PRIMED \
+ (SLSB_OWNER_CU | SLSB_TYPE_OUTPUT | SLSB_STATE_PRIMED) /* 0x62 */
+#define SLSB_P_OUTPUT_HALTED \
+ (SLSB_OWNER_PROG | SLSB_TYPE_OUTPUT | SLSB_STATE_HALTED) /* 0xae */
+#define SLSB_P_OUTPUT_ERROR \
+ (SLSB_OWNER_PROG | SLSB_TYPE_OUTPUT | SLSB_STATE_ERROR) /* 0xaf */
+
+#define SLSB_ERROR_DURING_LOOKUP 0xff
+
+/* additional CIWs returned by extended Sense-ID */
+#define CIW_TYPE_EQUEUE 0x3 /* establish QDIO queues */
+#define CIW_TYPE_AQUEUE 0x4 /* activate QDIO queues */
-#if QDIO_VERBOSE_LEVEL>6
-#define QDIO_PRINT_INFO(x...) printk( QDIO_PRINTK_HEADER x)
-#else
-#define QDIO_PRINT_INFO(x...) do { } while (0)
-#endif
-
-#if QDIO_VERBOSE_LEVEL>5
-#define QDIO_PRINT_WARN(x...) printk( QDIO_PRINTK_HEADER x)
-#else
-#define QDIO_PRINT_WARN(x...) do { } while (0)
-#endif
-
-#if QDIO_VERBOSE_LEVEL>4
-#define QDIO_PRINT_ERR(x...) printk( QDIO_PRINTK_HEADER x)
-#else
-#define QDIO_PRINT_ERR(x...) do { } while (0)
-#endif
-
-#if QDIO_VERBOSE_LEVEL>3
-#define QDIO_PRINT_CRIT(x...) printk( QDIO_PRINTK_HEADER x)
-#else
-#define QDIO_PRINT_CRIT(x...) do { } while (0)
-#endif
-
-#if QDIO_VERBOSE_LEVEL>2
-#define QDIO_PRINT_ALERT(x...) printk( QDIO_PRINTK_HEADER x)
-#else
-#define QDIO_PRINT_ALERT(x...) do { } while (0)
-#endif
+/* flags for st qdio sch data */
+#define CHSC_FLAG_QDIO_CAPABILITY 0x80
+#define CHSC_FLAG_VALIDITY 0x40
+
+/* qdio adapter-characteristics-1 flag */
+#define AC1_SIGA_INPUT_NEEDED 0x40 /* process input queues */
+#define AC1_SIGA_OUTPUT_NEEDED 0x20 /* process output queues */
+#define AC1_SIGA_SYNC_NEEDED 0x10 /* ask hypervisor to sync */
+#define AC1_AUTOMATIC_SYNC_ON_THININT 0x08 /* set by hypervisor */
+#define AC1_AUTOMATIC_SYNC_ON_OUT_PCI 0x04 /* set by hypervisor */
+#define AC1_SC_QEBSM_AVAILABLE 0x02 /* available for subchannel */
+#define AC1_SC_QEBSM_ENABLED 0x01 /* enabled for subchannel */
-#if QDIO_VERBOSE_LEVEL>1
-#define QDIO_PRINT_EMERG(x...) printk( QDIO_PRINTK_HEADER x)
-#else
-#define QDIO_PRINT_EMERG(x...) do { } while (0)
-#endif
-
-#define QDIO_HEXDUMP16(importance,header,ptr) \
-QDIO_PRINT_##importance(header "%02x %02x %02x %02x " \
- "%02x %02x %02x %02x %02x %02x %02x %02x " \
- "%02x %02x %02x %02x\n",*(((char*)ptr)), \
- *(((char*)ptr)+1),*(((char*)ptr)+2), \
- *(((char*)ptr)+3),*(((char*)ptr)+4), \
- *(((char*)ptr)+5),*(((char*)ptr)+6), \
- *(((char*)ptr)+7),*(((char*)ptr)+8), \
- *(((char*)ptr)+9),*(((char*)ptr)+10), \
- *(((char*)ptr)+11),*(((char*)ptr)+12), \
- *(((char*)ptr)+13),*(((char*)ptr)+14), \
- *(((char*)ptr)+15)); \
-QDIO_PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
- "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
- *(((char*)ptr)+16),*(((char*)ptr)+17), \
- *(((char*)ptr)+18),*(((char*)ptr)+19), \
- *(((char*)ptr)+20),*(((char*)ptr)+21), \
- *(((char*)ptr)+22),*(((char*)ptr)+23), \
- *(((char*)ptr)+24),*(((char*)ptr)+25), \
- *(((char*)ptr)+26),*(((char*)ptr)+27), \
- *(((char*)ptr)+28),*(((char*)ptr)+29), \
- *(((char*)ptr)+30),*(((char*)ptr)+31));
-
-/****************** END OF DEBUG FACILITY STUFF *********************/
+#ifdef CONFIG_64BIT
+static inline int do_sqbs(u64 token, unsigned char state, int queue,
+ int *start, int *count)
+{
+ register unsigned long _ccq asm ("0") = *count;
+ register unsigned long _token asm ("1") = token;
+ unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
-/*
- * Some instructions as assembly
- */
+ asm volatile(
+ " .insn rsy,0xeb000000008A,%1,0,0(%2)"
+ : "+d" (_ccq), "+d" (_queuestart)
+ : "d" ((unsigned long)state), "d" (_token)
+ : "memory", "cc");
+ *count = _ccq & 0xff;
+ *start = _queuestart & 0xff;
-static inline int
-do_sqbs(unsigned long sch, unsigned char state, int queue,
- unsigned int *start, unsigned int *count)
-{
-#ifdef CONFIG_64BIT
- register unsigned long _ccq asm ("0") = *count;
- register unsigned long _sch asm ("1") = sch;
- unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
-
- asm volatile(
- " .insn rsy,0xeb000000008A,%1,0,0(%2)"
- : "+d" (_ccq), "+d" (_queuestart)
- : "d" ((unsigned long)state), "d" (_sch)
- : "memory", "cc");
- *count = _ccq & 0xff;
- *start = _queuestart & 0xff;
-
- return (_ccq >> 32) & 0xff;
-#else
- return 0;
-#endif
+ return (_ccq >> 32) & 0xff;
}
-static inline int
-do_eqbs(unsigned long sch, unsigned char *state, int queue,
- unsigned int *start, unsigned int *count)
+static inline int do_eqbs(u64 token, unsigned char *state, int queue,
+ int *start, int *count)
{
-#ifdef CONFIG_64BIT
register unsigned long _ccq asm ("0") = *count;
- register unsigned long _sch asm ("1") = sch;
+ register unsigned long _token asm ("1") = token;
unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
unsigned long _state = 0;
asm volatile(
" .insn rrf,0xB99c0000,%1,%2,0,0"
: "+d" (_ccq), "+d" (_queuestart), "+d" (_state)
- : "d" (_sch)
- : "memory", "cc" );
+ : "d" (_token)
+ : "memory", "cc");
*count = _ccq & 0xff;
*start = _queuestart & 0xff;
*state = _state & 0xff;
return (_ccq >> 32) & 0xff;
-#else
- return 0;
-#endif
}
+#else
+static inline int do_sqbs(u64 token, unsigned char state, int queue,
+ int *start, int *count) { return 0; }
+static inline int do_eqbs(u64 token, unsigned char *state, int queue,
+ int *start, int *count) { return 0; }
+#endif /* CONFIG_64BIT */
+struct qdio_irq;
-static inline int
-do_siga_sync(struct subchannel_id schid, unsigned int mask1, unsigned int mask2)
-{
- register unsigned long reg0 asm ("0") = 2;
- register struct subchannel_id reg1 asm ("1") = schid;
- register unsigned long reg2 asm ("2") = mask1;
- register unsigned long reg3 asm ("3") = mask2;
- int cc;
-
- asm volatile(
- " siga 0\n"
- " ipm %0\n"
- " srl %0,28\n"
- : "=d" (cc)
- : "d" (reg0), "d" (reg1), "d" (reg2), "d" (reg3) : "cc");
- return cc;
-}
-
-static inline int
-do_siga_input(struct subchannel_id schid, unsigned int mask)
-{
- register unsigned long reg0 asm ("0") = 1;
- register struct subchannel_id reg1 asm ("1") = schid;
- register unsigned long reg2 asm ("2") = mask;
- int cc;
-
- asm volatile(
- " siga 0\n"
- " ipm %0\n"
- " srl %0,28\n"
- : "=d" (cc)
- : "d" (reg0), "d" (reg1), "d" (reg2) : "cc", "memory");
- return cc;
-}
-
-static inline int
-do_siga_output(unsigned long schid, unsigned long mask, __u32 *bb,
- unsigned int fc)
-{
- register unsigned long __fc asm("0") = fc;
- register unsigned long __schid asm("1") = schid;
- register unsigned long __mask asm("2") = mask;
- int cc;
-
- asm volatile(
- " siga 0\n"
- "0: ipm %0\n"
- " srl %0,28\n"
- "1:\n"
- EX_TABLE(0b,1b)
- : "=d" (cc), "+d" (__fc), "+d" (__schid), "+d" (__mask)
- : "0" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION)
- : "cc", "memory");
- (*bb) = ((unsigned int) __fc) >> 31;
- return cc;
-}
-
-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;
-}
-
-/*
- * QDIO device commands returned by extended Sense-ID
- */
-#define DEFAULT_ESTABLISH_QS_CMD 0x1b
-#define DEFAULT_ESTABLISH_QS_COUNT 0x1000
-#define DEFAULT_ACTIVATE_QS_CMD 0x1f
-#define DEFAULT_ACTIVATE_QS_COUNT 0
-
-/*
- * additional CIWs returned by extended Sense-ID
- */
-#define CIW_TYPE_EQUEUE 0x3 /* establish QDIO queues */
-#define CIW_TYPE_AQUEUE 0x4 /* activate QDIO queues */
-
-#define QDIO_CHSC_RESPONSE_CODE_OK 1
-/* flags for st qdio sch data */
-#define CHSC_FLAG_QDIO_CAPABILITY 0x80
-#define CHSC_FLAG_VALIDITY 0x40
-
-#define CHSC_FLAG_SIGA_INPUT_NECESSARY 0x40
-#define CHSC_FLAG_SIGA_OUTPUT_NECESSARY 0x20
-#define CHSC_FLAG_SIGA_SYNC_NECESSARY 0x10
-#define CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS 0x08
-#define CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS 0x04
+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;
+} __attribute__ ((packed));
-struct qdio_chsc_ssqd {
+struct chsc_ssqd_area {
struct chsc_header request;
- u16 reserved1:10;
- u16 ssid:2;
- u16 fmt:4;
+ u16:10;
+ u8 ssid:2;
+ u8 fmt:4;
u16 first_sch;
- u16 reserved2;
+ u16:16;
u16 last_sch;
- u32 reserved3;
+ u32:32;
struct chsc_header response;
- u32 reserved4;
- u8 flags;
- u8 reserved5;
- u16 sch;
- u8 qfmt;
- u8 parm;
- u8 qdioac1;
- u8 sch_class;
- u8 pct;
- u8 icnt;
- u8 reserved7;
- u8 ocnt;
- u8 reserved8;
- u8 mbccnt;
- u16 qdioac2;
- u64 sch_token;
-};
+ u32:32;
+ struct qdio_ssqd_desc qdio_ssqd;
+} __attribute__ ((packed));
-struct qdio_perf_stats {
-#ifdef CONFIG_64BIT
- atomic64_t tl_runs;
- atomic64_t outbound_tl_runs;
- atomic64_t outbound_tl_runs_resched;
- atomic64_t inbound_tl_runs;
- atomic64_t inbound_tl_runs_resched;
- atomic64_t inbound_thin_tl_runs;
- atomic64_t inbound_thin_tl_runs_resched;
-
- atomic64_t siga_outs;
- atomic64_t siga_ins;
- atomic64_t siga_syncs;
- atomic64_t pcis;
- atomic64_t thinints;
- atomic64_t fast_reqs;
-
- atomic64_t outbound_cnt;
- atomic64_t inbound_cnt;
-#else /* CONFIG_64BIT */
- atomic_t tl_runs;
- atomic_t outbound_tl_runs;
- atomic_t outbound_tl_runs_resched;
- atomic_t inbound_tl_runs;
- atomic_t inbound_tl_runs_resched;
- atomic_t inbound_thin_tl_runs;
- atomic_t inbound_thin_tl_runs_resched;
-
- atomic_t siga_outs;
- atomic_t siga_ins;
- atomic_t siga_syncs;
- atomic_t pcis;
- atomic_t thinints;
- atomic_t fast_reqs;
-
- atomic_t outbound_cnt;
- atomic_t inbound_cnt;
-#endif /* CONFIG_64BIT */
+struct scssc_area {
+ struct chsc_header request;
+ u16 operation_code;
+ u16:16;
+ u32:32;
+ u32:32;
+ u64 summary_indicator_addr;
+ u64 subchannel_indicator_addr;
+ u32 ks:4;
+ u32 kc:4;
+ u32:21;
+ u32 isc:3;
+ u32 word_with_d_bit;
+ u32:32;
+ struct subchannel_id schid;
+ u32 reserved[1004];
+ struct chsc_header response;
+ u32:32;
+} __attribute__ ((packed));
+
+struct qdio_input_q {
+ /* input buffer acknowledgement flag */
+ int polling;
+
+ /* last time of noticing incoming data */
+ u64 timestamp;
+
+ /* lock for clearing the acknowledgement */
+ spinlock_t lock;
};
-/* unlikely as the later the better */
-#define SYNC_MEMORY if (unlikely(q->siga_sync)) qdio_siga_sync_q(q)
-#define SYNC_MEMORY_ALL if (unlikely(q->siga_sync)) \
- qdio_siga_sync(q,~0U,~0U)
-#define SYNC_MEMORY_ALL_OUTB if (unlikely(q->siga_sync)) \
- qdio_siga_sync(q,~0U,0)
+struct qdio_output_q {
+ /* failed siga-w attempts*/
+ atomic_t busy_siga_counter;
-#define NOW qdio_get_micros()
-#define SAVE_TIMESTAMP(q) q->timing.last_transfer_time=NOW
-#define GET_SAVED_TIMESTAMP(q) (q->timing.last_transfer_time)
-#define SAVE_FRONTIER(q,val) q->last_move_ftc=val
-#define GET_SAVED_FRONTIER(q) (q->last_move_ftc)
+ /* start time of busy condition */
+ u64 timestamp;
-#define MY_MODULE_STRING(x) #x
+ /* PCIs are enabled for the queue */
+ int pci_out_enabled;
-#ifdef CONFIG_64BIT
-#define QDIO_GET_ADDR(x) ((__u32)(unsigned long)x)
-#else /* CONFIG_64BIT */
-#define QDIO_GET_ADDR(x) ((__u32)(long)x)
-#endif /* CONFIG_64BIT */
+ /* IQDIO: output multiple buffers (enhanced SIGA) */
+ int use_enh_siga;
+
+ /* timer to check for more outbound work */
+ struct timer_list timer;
+};
struct qdio_q {
- volatile struct slsb slsb;
+ struct slsb slsb;
+ union {
+ struct qdio_input_q in;
+ struct qdio_output_q out;
+ } u;
- char unused[QDIO_MAX_BUFFERS_PER_Q];
+ /* queue number */
+ int nr;
- __u32 * dev_st_chg_ind;
+ /* bitmask of queue number */
+ int mask;
+ /* input or output queue */
int is_input_q;
- struct subchannel_id schid;
- struct ccw_device *cdev;
- unsigned int is_iqdio_q;
- unsigned int is_thinint_q;
-
- /* bit 0 means queue 0, bit 1 means queue 1, ... */
- unsigned int mask;
- unsigned int q_no;
+ /* list of thinint input queues */
+ struct list_head entry;
+ /* upper-layer program handler */
qdio_handler_t (*handler);
- /* points to the next buffer to be checked for having
- * been processed by the card (outbound)
- * or to the next buffer the program should check for (inbound) */
- volatile int first_to_check;
- /* and the last time it was: */
- volatile int last_move_ftc;
-
- atomic_t number_of_buffers_used;
- atomic_t polling;
+ /*
+ * inbound: next buffer the program should check for
+ * outbound: next buffer to check for having been processed
+ * by the card
+ */
+ int first_to_check;
- unsigned int siga_in;
- unsigned int siga_out;
- unsigned int siga_sync;
- unsigned int siga_sync_done_on_thinints;
- unsigned int siga_sync_done_on_outb_tis;
- unsigned int hydra_gives_outbound_pcis;
+ /* first_to_check of the last time */
+ int last_move_ftc;
- /* used to save beginning position when calling dd_handlers */
- int first_element_to_kick;
+ /* beginning position for calling the program */
+ int first_to_kick;
- atomic_t use_count;
- atomic_t is_in_shutdown;
+ /* number of buffers in use by the adapter */
+ atomic_t nr_buf_used;
- void *irq_ptr;
-
- struct timer_list timer;
-#ifdef QDIO_USE_TIMERS_FOR_POLLING
- atomic_t timer_already_set;
- spinlock_t timer_lock;
-#else /* QDIO_USE_TIMERS_FOR_POLLING */
+ struct qdio_irq *irq_ptr;
struct tasklet_struct tasklet;
-#endif /* QDIO_USE_TIMERS_FOR_POLLING */
-
- enum qdio_irq_states state;
-
- /* used to store the error condition during a data transfer */
+ /* error condition during a data transfer */
unsigned int qdio_error;
- unsigned int siga_error;
- unsigned int error_status_flags;
-
- /* list of interesting queues */
- volatile struct qdio_q *list_next;
- volatile struct qdio_q *list_prev;
struct sl *sl;
- volatile struct sbal *sbal[QDIO_MAX_BUFFERS_PER_Q];
-
- struct qdio_buffer *qdio_buffers[QDIO_MAX_BUFFERS_PER_Q];
-
- unsigned long int_parm;
-
- /*struct {
- int in_bh_check_limit;
- int threshold;
- } threshold_classes[QDIO_STATS_CLASSES];*/
-
- struct {
- /* inbound: the time to stop polling
- outbound: the time to kick peer */
- int threshold; /* the real value */
-
- /* outbound: last time of do_QDIO
- inbound: last time of noticing incoming data */
- /*__u64 last_transfer_times[QDIO_STATS_NUMBER];
- int last_transfer_index; */
-
- __u64 last_transfer_time;
- __u64 busy_start;
- } timing;
- atomic_t busy_siga_counter;
- unsigned int queue_type;
- unsigned int is_pci_out;
-
- /* leave this member at the end. won't be cleared in qdio_fill_qs */
- struct slib *slib; /* a page is allocated under this pointer,
- sl points into this page, offset PAGE_SIZE/2
- (after slib) */
+ struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q];
+
+ /*
+ * Warning: Leave this member at the end so it won't be cleared in
+ * qdio_fill_qs. A page is allocated under this pointer and used for
+ * slib and sl. slib is 2048 bytes big and sl points to offset
+ * PAGE_SIZE / 2.
+ */
+ struct slib *slib;
} __attribute__ ((aligned(256)));
struct qdio_irq {
- __u32 * volatile dev_st_chg_ind;
+ struct qib qib;
+ u32 *dsci; /* address of device state change indicator */
+ struct ccw_device *cdev;
unsigned long int_parm;
struct subchannel_id schid;
-
- unsigned int is_iqdio_irq;
- unsigned int is_thinint_irq;
- unsigned int hydra_gives_outbound_pcis;
- unsigned int sync_done_on_outb_pcis;
-
- /* QEBSM facility */
- unsigned int is_qebsm;
- unsigned long sch_token;
+ unsigned long sch_token; /* QEBSM facility */
enum qdio_irq_states state;
- unsigned int no_input_qs;
- unsigned int no_output_qs;
+ struct siga_flag siga_flag; /* siga sync information from qdioac */
- unsigned char qdioac;
+ int nr_input_qs;
+ int nr_output_qs;
struct ccw1 ccw;
-
struct ciw equeue;
struct ciw aqueue;
- struct qib qib;
-
- void (*original_int_handler) (struct ccw_device *,
- unsigned long, struct irb *);
+ struct qdio_ssqd_desc ssqd_desc;
+
+ void (*orig_handler) (struct ccw_device *, unsigned long, struct irb *);
- /* leave these four members together at the end. won't be cleared in qdio_fill_irq */
+ /*
+ * Warning: Leave these members together at the end so they won't be
+ * cleared in qdio_setup_irq.
+ */
struct qdr *qdr;
+ unsigned long chsc_page;
+
struct qdio_q *input_qs[QDIO_MAX_QUEUES_PER_IRQ];
struct qdio_q *output_qs[QDIO_MAX_QUEUES_PER_IRQ];
- struct semaphore setting_up_sema;
+
+ struct mutex setup_mutex;
};
-#endif
+
+/* helper functions */
+#define queue_type(q) q->irq_ptr->qib.qfmt
+
+#define is_thinint_irq(irq) \
+ (irq->qib.qfmt == QDIO_IQDIO_QFMT || \
+ css_general_characteristics.aif_osa)
+
+/* the highest iqdio queue is used for multicast */
+static inline int multicast_outbound(struct qdio_q *q)
+{
+ return (q->irq_ptr->nr_output_qs > 1) &&
+ (q->nr == q->irq_ptr->nr_output_qs - 1);
+}
+
+static inline unsigned long long get_usecs(void)
+{
+ return monotonic_clock() >> 12;
+}
+
+#define pci_out_supported(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 for_each_input_queue(irq_ptr, q, i) \
+ for (i = 0, q = irq_ptr->input_qs[0]; \
+ i < irq_ptr->nr_input_qs; \
+ q = irq_ptr->input_qs[++i])
+#define for_each_output_queue(irq_ptr, q, i) \
+ for (i = 0, q = irq_ptr->output_qs[0]; \
+ i < irq_ptr->nr_output_qs; \
+ q = irq_ptr->output_qs[++i])
+
+#define prev_buf(bufnr) \
+ ((bufnr + QDIO_MAX_BUFFERS_MASK) & QDIO_MAX_BUFFERS_MASK)
+#define next_buf(bufnr) \
+ ((bufnr + 1) & QDIO_MAX_BUFFERS_MASK)
+#define add_buf(bufnr, inc) \
+ ((bufnr + inc) & QDIO_MAX_BUFFERS_MASK)
+
+/* prototypes for thin interrupt */
+void qdio_sync_after_thinint(struct qdio_q *q);
+int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state);
+void qdio_check_outbound_after_thinint(struct qdio_q *q);
+int qdio_inbound_q_moved(struct qdio_q *q);
+void qdio_kick_inbound_handler(struct qdio_q *q);
+void qdio_stop_polling(struct qdio_q *q);
+int qdio_siga_sync_q(struct qdio_q *q);
+
+void qdio_setup_thinint(struct qdio_irq *irq_ptr);
+int qdio_establish_thinint(struct qdio_irq *irq_ptr);
+void qdio_shutdown_thinint(struct qdio_irq *irq_ptr);
+void tiqdio_add_input_queues(struct qdio_irq *irq_ptr);
+void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr);
+void tiqdio_inbound_processing(unsigned long q);
+int tiqdio_allocate_memory(void);
+void tiqdio_free_memory(void);
+int tiqdio_register_thinints(void);
+void tiqdio_unregister_thinints(void);
+
+/* prototypes for setup */
+void qdio_inbound_processing(unsigned long data);
+void qdio_outbound_processing(unsigned long data);
+void qdio_outbound_timer(unsigned long data);
+void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
+ struct irb *irb);
+int qdio_allocate_qs(struct qdio_irq *irq_ptr, int nr_input_qs,
+ int nr_output_qs);
+void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr);
+int qdio_setup_irq(struct qdio_initialize *init_data);
+void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
+ struct ccw_device *cdev);
+void qdio_release_memory(struct qdio_irq *irq_ptr);
+int qdio_setup_init(void);
+void qdio_setup_exit(void);
+
+#endif /* _CIO_QDIO_H */
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c
new file mode 100644
index 00000000000..b5390821434
--- /dev/null
+++ b/drivers/s390/cio/qdio_debug.c
@@ -0,0 +1,240 @@
+/*
+ * drivers/s390/cio/qdio_debug.c
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Author: Jan Glauber (jang@linux.vnet.ibm.com)
+ */
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <asm/qdio.h>
+#include <asm/debug.h>
+#include "qdio_debug.h"
+#include "qdio.h"
+
+debug_info_t *qdio_dbf_setup;
+debug_info_t *qdio_dbf_trace;
+
+static struct dentry *debugfs_root;
+#define MAX_DEBUGFS_QUEUES 32
+static struct dentry *debugfs_queues[MAX_DEBUGFS_QUEUES] = { NULL };
+static DEFINE_MUTEX(debugfs_mutex);
+
+void qdio_allocate_do_dbf(struct qdio_initialize *init_data)
+{
+ char dbf_text[20];
+
+ sprintf(dbf_text, "qfmt:%x", init_data->q_format);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ QDIO_DBF_HEX0(0, setup, init_data->adapter_name, 8);
+ sprintf(dbf_text, "qpff%4x", init_data->qib_param_field_format);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ QDIO_DBF_HEX0(0, setup, &init_data->qib_param_field, sizeof(void *));
+ QDIO_DBF_HEX0(0, setup, &init_data->input_slib_elements, sizeof(void *));
+ QDIO_DBF_HEX0(0, setup, &init_data->output_slib_elements, sizeof(void *));
+ sprintf(dbf_text, "niq:%4x", init_data->no_input_qs);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ sprintf(dbf_text, "noq:%4x", init_data->no_output_qs);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ QDIO_DBF_HEX0(0, setup, &init_data->input_handler, sizeof(void *));
+ QDIO_DBF_HEX0(0, setup, &init_data->output_handler, sizeof(void *));
+ QDIO_DBF_HEX0(0, setup, &init_data->int_parm, sizeof(long));
+ QDIO_DBF_HEX0(0, setup, &init_data->flags, sizeof(long));
+ QDIO_DBF_HEX0(0, setup, &init_data->input_sbal_addr_array, sizeof(void *));
+ QDIO_DBF_HEX0(0, setup, &init_data->output_sbal_addr_array, sizeof(void *));
+}
+
+static void qdio_unregister_dbf_views(void)
+{
+ if (qdio_dbf_setup)
+ debug_unregister(qdio_dbf_setup);
+ if (qdio_dbf_trace)
+ debug_unregister(qdio_dbf_trace);
+}
+
+static int qdio_register_dbf_views(void)
+{
+ qdio_dbf_setup = debug_register("qdio_setup", QDIO_DBF_SETUP_PAGES,
+ QDIO_DBF_SETUP_NR_AREAS,
+ QDIO_DBF_SETUP_LEN);
+ if (!qdio_dbf_setup)
+ goto oom;
+ debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view);
+ debug_set_level(qdio_dbf_setup, QDIO_DBF_SETUP_LEVEL);
+
+ qdio_dbf_trace = debug_register("qdio_trace", QDIO_DBF_TRACE_PAGES,
+ QDIO_DBF_TRACE_NR_AREAS,
+ QDIO_DBF_TRACE_LEN);
+ if (!qdio_dbf_trace)
+ goto oom;
+ debug_register_view(qdio_dbf_trace, &debug_hex_ascii_view);
+ debug_set_level(qdio_dbf_trace, QDIO_DBF_TRACE_LEVEL);
+ return 0;
+oom:
+ qdio_unregister_dbf_views();
+ return -ENOMEM;
+}
+
+static int qstat_show(struct seq_file *m, void *v)
+{
+ unsigned char state;
+ struct qdio_q *q = m->private;
+ int i;
+
+ if (!q)
+ return 0;
+
+ seq_printf(m, "device state indicator: %d\n", *q->irq_ptr->dsci);
+ seq_printf(m, "nr_used: %d\n", atomic_read(&q->nr_buf_used));
+ seq_printf(m, "ftc: %d\n", q->first_to_check);
+ seq_printf(m, "last_move_ftc: %d\n", q->last_move_ftc);
+ seq_printf(m, "polling: %d\n", q->u.in.polling);
+ seq_printf(m, "slsb buffer states:\n");
+
+ qdio_siga_sync_q(q);
+ for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
+ get_buf_state(q, i, &state);
+ switch (state) {
+ case SLSB_P_INPUT_NOT_INIT:
+ case SLSB_P_OUTPUT_NOT_INIT:
+ seq_printf(m, "N");
+ break;
+ case SLSB_P_INPUT_PRIMED:
+ case SLSB_CU_OUTPUT_PRIMED:
+ seq_printf(m, "+");
+ break;
+ case SLSB_P_INPUT_ACK:
+ seq_printf(m, "A");
+ break;
+ case SLSB_P_INPUT_ERROR:
+ case SLSB_P_OUTPUT_ERROR:
+ seq_printf(m, "x");
+ break;
+ case SLSB_CU_INPUT_EMPTY:
+ case SLSB_P_OUTPUT_EMPTY:
+ seq_printf(m, "-");
+ break;
+ case SLSB_P_INPUT_HALTED:
+ case SLSB_P_OUTPUT_HALTED:
+ seq_printf(m, ".");
+ break;
+ default:
+ seq_printf(m, "?");
+ }
+ if (i == 63)
+ seq_printf(m, "\n");
+ }
+ seq_printf(m, "\n");
+ return 0;
+}
+
+static ssize_t qstat_seq_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ struct qdio_q *q = seq->private;
+
+ if (!q)
+ return 0;
+
+ if (q->is_input_q)
+ xchg(q->irq_ptr->dsci, 1);
+ local_bh_disable();
+ tasklet_schedule(&q->tasklet);
+ local_bh_enable();
+ return count;
+}
+
+static int qstat_seq_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, qstat_show,
+ filp->f_path.dentry->d_inode->i_private);
+}
+
+static void get_queue_name(struct qdio_q *q, struct ccw_device *cdev, char *name)
+{
+ memset(name, 0, sizeof(name));
+ sprintf(name, "%s", dev_name(&cdev->dev));
+ if (q->is_input_q)
+ sprintf(name + strlen(name), "_input");
+ else
+ sprintf(name + strlen(name), "_output");
+ sprintf(name + strlen(name), "_%d", q->nr);
+}
+
+static void remove_debugfs_entry(struct qdio_q *q)
+{
+ int i;
+
+ for (i = 0; i < MAX_DEBUGFS_QUEUES; i++) {
+ if (!debugfs_queues[i])
+ continue;
+ if (debugfs_queues[i]->d_inode->i_private == q) {
+ debugfs_remove(debugfs_queues[i]);
+ debugfs_queues[i] = NULL;
+ }
+ }
+}
+
+static struct file_operations debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = qstat_seq_open,
+ .read = seq_read,
+ .write = qstat_seq_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void setup_debugfs_entry(struct qdio_q *q, struct ccw_device *cdev)
+{
+ int i = 0;
+ char name[40];
+
+ while (debugfs_queues[i] != NULL) {
+ i++;
+ if (i >= MAX_DEBUGFS_QUEUES)
+ return;
+ }
+ get_queue_name(q, cdev, name);
+ debugfs_queues[i] = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUSR,
+ debugfs_root, q, &debugfs_fops);
+}
+
+void qdio_setup_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev)
+{
+ struct qdio_q *q;
+ int i;
+
+ mutex_lock(&debugfs_mutex);
+ for_each_input_queue(irq_ptr, q, i)
+ setup_debugfs_entry(q, cdev);
+ for_each_output_queue(irq_ptr, q, i)
+ setup_debugfs_entry(q, cdev);
+ mutex_unlock(&debugfs_mutex);
+}
+
+void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev)
+{
+ struct qdio_q *q;
+ int i;
+
+ mutex_lock(&debugfs_mutex);
+ for_each_input_queue(irq_ptr, q, i)
+ remove_debugfs_entry(q);
+ for_each_output_queue(irq_ptr, q, i)
+ remove_debugfs_entry(q);
+ mutex_unlock(&debugfs_mutex);
+}
+
+int __init qdio_debug_init(void)
+{
+ debugfs_root = debugfs_create_dir("qdio_queues", NULL);
+ return qdio_register_dbf_views();
+}
+
+void qdio_debug_exit(void)
+{
+ debugfs_remove(debugfs_root);
+ qdio_unregister_dbf_views();
+}
diff --git a/drivers/s390/cio/qdio_debug.h b/drivers/s390/cio/qdio_debug.h
new file mode 100644
index 00000000000..5a4d85b829a
--- /dev/null
+++ b/drivers/s390/cio/qdio_debug.h
@@ -0,0 +1,91 @@
+/*
+ * drivers/s390/cio/qdio_debug.h
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Author: Jan Glauber (jang@linux.vnet.ibm.com)
+ */
+#ifndef QDIO_DEBUG_H
+#define QDIO_DEBUG_H
+
+#include <asm/debug.h>
+#include <asm/qdio.h>
+#include "qdio.h"
+
+#define QDIO_DBF_HEX(ex, name, level, addr, len) \
+ do { \
+ if (ex) \
+ debug_exception(qdio_dbf_##name, level, (void *)(addr), len); \
+ else \
+ debug_event(qdio_dbf_##name, level, (void *)(addr), len); \
+ } while (0)
+#define QDIO_DBF_TEXT(ex, name, level, text) \
+ do { \
+ if (ex) \
+ debug_text_exception(qdio_dbf_##name, level, text); \
+ else \
+ debug_text_event(qdio_dbf_##name, level, text); \
+ } while (0)
+
+#define QDIO_DBF_HEX0(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 0, addr, len)
+#define QDIO_DBF_HEX1(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 1, addr, len)
+#define QDIO_DBF_HEX2(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 2, addr, len)
+
+#ifdef CONFIG_QDIO_DEBUG
+#define QDIO_DBF_HEX3(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 3, addr, len)
+#define QDIO_DBF_HEX4(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 4, addr, len)
+#define QDIO_DBF_HEX5(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 5, addr, len)
+#define QDIO_DBF_HEX6(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 6, addr, len)
+#else
+#define QDIO_DBF_HEX3(ex, name, addr, len) do {} while (0)
+#define QDIO_DBF_HEX4(ex, name, addr, len) do {} while (0)
+#define QDIO_DBF_HEX5(ex, name, addr, len) do {} while (0)
+#define QDIO_DBF_HEX6(ex, name, addr, len) do {} while (0)
+#endif /* CONFIG_QDIO_DEBUG */
+
+#define QDIO_DBF_TEXT0(ex, name, text) QDIO_DBF_TEXT(ex, name, 0, text)
+#define QDIO_DBF_TEXT1(ex, name, text) QDIO_DBF_TEXT(ex, name, 1, text)
+#define QDIO_DBF_TEXT2(ex, name, text) QDIO_DBF_TEXT(ex, name, 2, text)
+
+#ifdef CONFIG_QDIO_DEBUG
+#define QDIO_DBF_TEXT3(ex, name, text) QDIO_DBF_TEXT(ex, name, 3, text)
+#define QDIO_DBF_TEXT4(ex, name, text) QDIO_DBF_TEXT(ex, name, 4, text)
+#define QDIO_DBF_TEXT5(ex, name, text) QDIO_DBF_TEXT(ex, name, 5, text)
+#define QDIO_DBF_TEXT6(ex, name, text) QDIO_DBF_TEXT(ex, name, 6, text)
+#else
+#define QDIO_DBF_TEXT3(ex, name, text) do {} while (0)
+#define QDIO_DBF_TEXT4(ex, name, text) do {} while (0)
+#define QDIO_DBF_TEXT5(ex, name, text) do {} while (0)
+#define QDIO_DBF_TEXT6(ex, name, text) do {} while (0)
+#endif /* CONFIG_QDIO_DEBUG */
+
+/* s390dbf views */
+#define QDIO_DBF_SETUP_LEN 8
+#define QDIO_DBF_SETUP_PAGES 8
+#define QDIO_DBF_SETUP_NR_AREAS 1
+
+#define QDIO_DBF_TRACE_LEN 8
+#define QDIO_DBF_TRACE_NR_AREAS 2
+
+#ifdef CONFIG_QDIO_DEBUG
+#define QDIO_DBF_TRACE_PAGES 32
+#define QDIO_DBF_SETUP_LEVEL 6
+#define QDIO_DBF_TRACE_LEVEL 4
+#else /* !CONFIG_QDIO_DEBUG */
+#define QDIO_DBF_TRACE_PAGES 8
+#define QDIO_DBF_SETUP_LEVEL 2
+#define QDIO_DBF_TRACE_LEVEL 2
+#endif /* CONFIG_QDIO_DEBUG */
+
+extern debug_info_t *qdio_dbf_setup;
+extern debug_info_t *qdio_dbf_trace;
+
+void qdio_allocate_do_dbf(struct qdio_initialize *init_data);
+void debug_print_bstat(struct qdio_q *q);
+void qdio_setup_debug_entries(struct qdio_irq *irq_ptr,
+ struct ccw_device *cdev);
+void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr,
+ struct ccw_device *cdev);
+int qdio_debug_init(void);
+void qdio_debug_exit(void);
+#endif
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
new file mode 100644
index 00000000000..a50682d2a0f
--- /dev/null
+++ b/drivers/s390/cio/qdio_main.c
@@ -0,0 +1,1769 @@
+/*
+ * linux/drivers/s390/cio/qdio_main.c
+ *
+ * Linux for s390 qdio support, buffer handling, qdio API and module support.
+ *
+ * Copyright 2000,2008 IBM Corp.
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ * Jan Glauber <jang@linux.vnet.ibm.com>
+ * 2.6 cio integration by Cornelia Huck <cornelia.huck@de.ibm.com>
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <asm/atomic.h>
+#include <asm/debug.h>
+#include <asm/qdio.h>
+
+#include "cio.h"
+#include "css.h"
+#include "device.h"
+#include "qdio.h"
+#include "qdio_debug.h"
+#include "qdio_perf.h"
+
+MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>,"\
+ "Jan Glauber <jang@linux.vnet.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)
+{
+ register unsigned long __fc asm ("0") = 2;
+ register struct subchannel_id __schid asm ("1") = schid;
+ register unsigned long out asm ("2") = out_mask;
+ register unsigned long in asm ("3") = in_mask;
+ int cc;
+
+ asm volatile(
+ " siga 0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
+ : "=d" (cc)
+ : "d" (__fc), "d" (__schid), "d" (out), "d" (in) : "cc");
+ return cc;
+}
+
+static inline int do_siga_input(struct subchannel_id schid, unsigned int mask)
+{
+ register unsigned long __fc asm ("0") = 1;
+ register struct subchannel_id __schid asm ("1") = schid;
+ register unsigned long __mask asm ("2") = mask;
+ int cc;
+
+ asm volatile(
+ " siga 0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
+ : "=d" (cc)
+ : "d" (__fc), "d" (__schid), "d" (__mask) : "cc", "memory");
+ return cc;
+}
+
+/**
+ * do_siga_output - perform SIGA-w/wt function
+ * @schid: subchannel id or in case of QEBSM the subchannel token
+ * @mask: which output queues to process
+ * @bb: busy bit indicator, set only if SIGA-w/wt could not access a buffer
+ * @fc: function code to perform
+ *
+ * Returns cc or QDIO_ERROR_SIGA_ACCESS_EXCEPTION.
+ * Note: For IQDC unicast queues only the highest priority queue is processed.
+ */
+static inline int do_siga_output(unsigned long schid, unsigned long mask,
+ u32 *bb, unsigned int fc)
+{
+ register unsigned long __fc asm("0") = fc;
+ register unsigned long __schid asm("1") = schid;
+ register unsigned long __mask asm("2") = mask;
+ int cc = QDIO_ERROR_SIGA_ACCESS_EXCEPTION;
+
+ asm volatile(
+ " siga 0\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (cc), "+d" (__fc), "+d" (__schid), "+d" (__mask)
+ : : "cc", "memory");
+ *bb = ((unsigned int) __fc) >> 31;
+ return cc;
+}
+
+static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
+{
+ char dbf_text[15];
+
+ /* all done or next buffer state different */
+ if (ccq == 0 || ccq == 32)
+ return 0;
+ /* not all buffers processed */
+ if (ccq == 96 || ccq == 97)
+ return 1;
+ /* notify devices immediately */
+ sprintf(dbf_text, "%d", ccq);
+ QDIO_DBF_TEXT2(1, trace, dbf_text);
+ return -EIO;
+}
+
+/**
+ * qdio_do_eqbs - extract buffer states for QEBSM
+ * @q: queue to manipulate
+ * @state: state of the extracted buffers
+ * @start: buffer number to start at
+ * @count: count of buffers to examine
+ *
+ * Returns the number of successfull extracted equal buffer states.
+ * Stops processing if a state is different from the last buffers state.
+ */
+static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
+ int start, int count)
+{
+ unsigned int ccq = 0;
+ int tmp_count = count, tmp_start = start;
+ int nr = q->nr;
+ int rc;
+ char dbf_text[15];
+
+ BUG_ON(!q->irq_ptr->sch_token);
+
+ if (!q->is_input_q)
+ nr += q->irq_ptr->nr_input_qs;
+again:
+ ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
+ rc = qdio_check_ccq(q, ccq);
+
+ /* At least one buffer was processed, return and extract the remaining
+ * buffers later.
+ */
+ if ((ccq == 96) && (count != tmp_count))
+ return (count - tmp_count);
+ if (rc == 1) {
+ QDIO_DBF_TEXT5(1, trace, "eqAGAIN");
+ goto again;
+ }
+
+ if (rc < 0) {
+ QDIO_DBF_TEXT2(1, trace, "eqberr");
+ sprintf(dbf_text, "%2x,%2x,%d,%d", count, tmp_count, ccq, nr);
+ QDIO_DBF_TEXT2(1, trace, dbf_text);
+ q->handler(q->irq_ptr->cdev,
+ QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
+ 0, -1, -1, q->irq_ptr->int_parm);
+ return 0;
+ }
+ return count - tmp_count;
+}
+
+/**
+ * qdio_do_sqbs - set buffer states for QEBSM
+ * @q: queue to manipulate
+ * @state: new state of the buffers
+ * @start: first buffer number to change
+ * @count: how many buffers to change
+ *
+ * Returns the number of successfully changed buffers.
+ * Does retrying until the specified count of buffer states is set or an
+ * error occurs.
+ */
+static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
+ int count)
+{
+ unsigned int ccq = 0;
+ int tmp_count = count, tmp_start = start;
+ int nr = q->nr;
+ int rc;
+ char dbf_text[15];
+
+ BUG_ON(!q->irq_ptr->sch_token);
+
+ if (!q->is_input_q)
+ nr += q->irq_ptr->nr_input_qs;
+again:
+ ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
+ rc = qdio_check_ccq(q, ccq);
+ if (rc == 1) {
+ QDIO_DBF_TEXT5(1, trace, "sqAGAIN");
+ goto again;
+ }
+ if (rc < 0) {
+ QDIO_DBF_TEXT3(1, trace, "sqberr");
+ sprintf(dbf_text, "%2x,%2x", count, tmp_count);
+ QDIO_DBF_TEXT3(1, trace, dbf_text);
+ sprintf(dbf_text, "%d,%d", ccq, nr);
+ QDIO_DBF_TEXT3(1, trace, dbf_text);
+
+ q->handler(q->irq_ptr->cdev,
+ QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
+ 0, -1, -1, q->irq_ptr->int_parm);
+ return 0;
+ }
+ WARN_ON(tmp_count);
+ return count - tmp_count;
+}
+
+/* returns number of examined buffers and their common state in *state */
+static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
+ unsigned char *state, unsigned int count)
+{
+ unsigned char __state = 0;
+ int i;
+
+ BUG_ON(bufnr > QDIO_MAX_BUFFERS_MASK);
+ BUG_ON(count > QDIO_MAX_BUFFERS_PER_Q);
+
+ if (is_qebsm(q))
+ return qdio_do_eqbs(q, state, bufnr, count);
+
+ for (i = 0; i < count; i++) {
+ if (!__state)
+ __state = q->slsb.val[bufnr];
+ else if (q->slsb.val[bufnr] != __state)
+ break;
+ bufnr = next_buf(bufnr);
+ }
+ *state = __state;
+ return i;
+}
+
+inline int get_buf_state(struct qdio_q *q, unsigned int bufnr,
+ unsigned char *state)
+{
+ return get_buf_states(q, bufnr, state, 1);
+}
+
+/* wrap-around safe setting of slsb states, returns number of changed buffers */
+static inline int set_buf_states(struct qdio_q *q, int bufnr,
+ unsigned char state, int count)
+{
+ int i;
+
+ BUG_ON(bufnr > QDIO_MAX_BUFFERS_MASK);
+ BUG_ON(count > QDIO_MAX_BUFFERS_PER_Q);
+
+ if (is_qebsm(q))
+ return qdio_do_sqbs(q, state, bufnr, count);
+
+ for (i = 0; i < count; i++) {
+ xchg(&q->slsb.val[bufnr], state);
+ bufnr = next_buf(bufnr);
+ }
+ return count;
+}
+
+static inline int set_buf_state(struct qdio_q *q, int bufnr,
+ unsigned char state)
+{
+ return set_buf_states(q, bufnr, state, 1);
+}
+
+/* set slsb states to initial state */
+void qdio_init_buf_states(struct qdio_irq *irq_ptr)
+{
+ struct qdio_q *q;
+ int i;
+
+ for_each_input_queue(irq_ptr, q, i)
+ set_buf_states(q, 0, SLSB_P_INPUT_NOT_INIT,
+ QDIO_MAX_BUFFERS_PER_Q);
+ for_each_output_queue(irq_ptr, q, i)
+ set_buf_states(q, 0, SLSB_P_OUTPUT_NOT_INIT,
+ QDIO_MAX_BUFFERS_PER_Q);
+}
+
+static int qdio_siga_sync(struct qdio_q *q, unsigned int output,
+ unsigned int input)
+{
+ int cc;
+
+ if (!need_siga_sync(q))
+ return 0;
+
+ qdio_perf_stat_inc(&perf_stats.siga_sync);
+
+ cc = do_siga_sync(q->irq_ptr->schid, output, input);
+ if (cc) {
+ QDIO_DBF_TEXT4(0, trace, "sigasync");
+ QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+ QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
+ }
+ return cc;
+}
+
+inline int qdio_siga_sync_q(struct qdio_q *q)
+{
+ if (q->is_input_q)
+ return qdio_siga_sync(q, 0, q->mask);
+ else
+ 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 inline int qdio_do_siga_output(struct qdio_q *q, unsigned int *busy_bit)
+{
+ unsigned int fc = 0;
+ unsigned long schid;
+
+ if (q->u.out.use_enh_siga) {
+ fc = 3;
+ }
+ if (!is_qebsm(q))
+ schid = *((u32 *)&q->irq_ptr->schid);
+ else {
+ schid = q->irq_ptr->sch_token;
+ fc |= 0x80;
+ }
+ return do_siga_output(schid, q->mask, busy_bit, fc);
+}
+
+static int qdio_siga_output(struct qdio_q *q)
+{
+ int cc;
+ u32 busy_bit;
+ u64 start_time = 0;
+ char dbf_text[15];
+
+ QDIO_DBF_TEXT5(0, trace, "sigaout");
+ QDIO_DBF_HEX5(0, trace, &q, sizeof(void *));
+
+ qdio_perf_stat_inc(&perf_stats.siga_out);
+again:
+ cc = qdio_do_siga_output(q, &busy_bit);
+ if (queue_type(q) == QDIO_IQDIO_QFMT && cc == 2 && busy_bit) {
+ sprintf(dbf_text, "bb%4x%2x", q->irq_ptr->schid.sch_no, q->nr);
+ QDIO_DBF_TEXT3(0, trace, dbf_text);
+
+ if (!start_time)
+ start_time = get_usecs();
+ else if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE)
+ goto again;
+ }
+
+ if (cc == 2 && busy_bit)
+ cc |= QDIO_ERROR_SIGA_BUSY;
+ if (cc)
+ QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
+ return cc;
+}
+
+static inline int qdio_siga_input(struct qdio_q *q)
+{
+ int cc;
+
+ QDIO_DBF_TEXT4(0, trace, "sigain");
+ QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+
+ qdio_perf_stat_inc(&perf_stats.siga_in);
+
+ cc = do_siga_input(q->irq_ptr->schid, q->mask);
+ if (cc)
+ QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
+ return cc;
+}
+
+/* called from thinint inbound handler */
+void qdio_sync_after_thinint(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
+ qdio_siga_sync_q(q);
+}
+
+inline void qdio_stop_polling(struct qdio_q *q)
+{
+ spin_lock_bh(&q->u.in.lock);
+ if (!q->u.in.polling) {
+ spin_unlock_bh(&q->u.in.lock);
+ return;
+ }
+ q->u.in.polling = 0;
+ qdio_perf_stat_inc(&perf_stats.debug_stop_polling);
+
+ /* show the card that we are not polling anymore */
+ set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT);
+ spin_unlock_bh(&q->u.in.lock);
+}
+
+static void announce_buffer_error(struct qdio_q *q)
+{
+ char dbf_text[15];
+
+ if (q->is_input_q)
+ QDIO_DBF_TEXT3(1, trace, "inperr");
+ else
+ QDIO_DBF_TEXT3(0, trace, "outperr");
+
+ sprintf(dbf_text, "%x-%x-%x", q->first_to_check,
+ q->sbal[q->first_to_check]->element[14].flags,
+ q->sbal[q->first_to_check]->element[15].flags);
+ QDIO_DBF_TEXT3(1, trace, dbf_text);
+ QDIO_DBF_HEX2(1, trace, q->sbal[q->first_to_check], 256);
+
+ q->qdio_error = QDIO_ERROR_SLSB_STATE;
+}
+
+static int get_inbound_buffer_frontier(struct qdio_q *q)
+{
+ int count, stop;
+ unsigned char state;
+
+ /*
+ * If we still poll don't update last_move_ftc, keep the
+ * previously ACK buffer there.
+ */
+ if (!q->u.in.polling)
+ q->last_move_ftc = q->first_to_check;
+
+ /*
+ * Don't check 128 buffers, as otherwise qdio_inbound_q_moved
+ * would return 0.
+ */
+ count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK);
+ stop = add_buf(q->first_to_check, count);
+
+ /*
+ * No siga sync here, as a PCI or we after a thin interrupt
+ * will sync the queues.
+ */
+
+ /* need to set count to 1 for non-qebsm */
+ if (!is_qebsm(q))
+ count = 1;
+
+check_next:
+ if (q->first_to_check == stop)
+ goto out;
+
+ count = get_buf_states(q, q->first_to_check, &state, count);
+ if (!count)
+ goto out;
+
+ switch (state) {
+ case SLSB_P_INPUT_PRIMED:
+ QDIO_DBF_TEXT5(0, trace, "inptprim");
+
+ /*
+ * Only ACK the first buffer. The ACK will be removed in
+ * qdio_stop_polling.
+ */
+ if (q->u.in.polling)
+ state = SLSB_P_INPUT_NOT_INIT;
+ else {
+ q->u.in.polling = 1;
+ state = SLSB_P_INPUT_ACK;
+ }
+ set_buf_state(q, q->first_to_check, state);
+
+ /*
+ * Need to change all PRIMED buffers to NOT_INIT, otherwise
+ * we're loosing initiative in the thinint code.
+ */
+ if (count > 1)
+ set_buf_states(q, next_buf(q->first_to_check),
+ SLSB_P_INPUT_NOT_INIT, count - 1);
+
+ /*
+ * No siga-sync needed for non-qebsm here, as the inbound queue
+ * will be synced on the next siga-r, resp.
+ * tiqdio_is_inbound_q_done will do the siga-sync.
+ */
+ q->first_to_check = add_buf(q->first_to_check, count);
+ atomic_sub(count, &q->nr_buf_used);
+ goto check_next;
+ case SLSB_P_INPUT_ERROR:
+ announce_buffer_error(q);
+ /* process the buffer, the upper layer will take care of it */
+ q->first_to_check = add_buf(q->first_to_check, count);
+ atomic_sub(count, &q->nr_buf_used);
+ break;
+ case SLSB_CU_INPUT_EMPTY:
+ case SLSB_P_INPUT_NOT_INIT:
+ case SLSB_P_INPUT_ACK:
+ QDIO_DBF_TEXT5(0, trace, "inpnipro");
+ break;
+ default:
+ BUG();
+ }
+out:
+ QDIO_DBF_HEX4(0, trace, &q->first_to_check, sizeof(int));
+ return q->first_to_check;
+}
+
+int qdio_inbound_q_moved(struct qdio_q *q)
+{
+ int bufnr;
+
+ bufnr = get_inbound_buffer_frontier(q);
+
+ if ((bufnr != q->last_move_ftc) || q->qdio_error) {
+ if (!need_siga_sync(q) && !pci_out_supported(q))
+ q->u.in.timestamp = get_usecs();
+
+ QDIO_DBF_TEXT4(0, trace, "inhasmvd");
+ QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+ return 1;
+ } else
+ return 0;
+}
+
+static int qdio_inbound_q_done(struct qdio_q *q)
+{
+ unsigned char state;
+#ifdef CONFIG_QDIO_DEBUG
+ char dbf_text[15];
+#endif
+
+ if (!atomic_read(&q->nr_buf_used))
+ return 1;
+
+ /*
+ * We need that one for synchronization with the adapter, as it
+ * does a kind of PCI avoidance.
+ */
+ qdio_siga_sync_q(q);
+
+ get_buf_state(q, q->first_to_check, &state);
+ if (state == SLSB_P_INPUT_PRIMED)
+ /* we got something to do */
+ return 0;
+
+ /* on VM, we don't poll, so the q is always done here */
+ if (need_siga_sync(q) || pci_out_supported(q))
+ return 1;
+
+ /*
+ * At this point we know, that inbound first_to_check
+ * has (probably) not moved (see qdio_inbound_processing).
+ */
+ if (get_usecs() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) {
+#ifdef CONFIG_QDIO_DEBUG
+ QDIO_DBF_TEXT4(0, trace, "inqisdon");
+ QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+ sprintf(dbf_text, "pf%02x", q->first_to_check);
+ QDIO_DBF_TEXT4(0, trace, dbf_text);
+#endif /* CONFIG_QDIO_DEBUG */
+ return 1;
+ } else {
+#ifdef CONFIG_QDIO_DEBUG
+ QDIO_DBF_TEXT4(0, trace, "inqisntd");
+ QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+ sprintf(dbf_text, "pf%02x", q->first_to_check);
+ QDIO_DBF_TEXT4(0, trace, dbf_text);
+#endif /* CONFIG_QDIO_DEBUG */
+ return 0;
+ }
+}
+
+void qdio_kick_inbound_handler(struct qdio_q *q)
+{
+ int count, start, end;
+#ifdef CONFIG_QDIO_DEBUG
+ char dbf_text[15];
+#endif
+
+ qdio_perf_stat_inc(&perf_stats.inbound_handler);
+
+ start = q->first_to_kick;
+ end = q->first_to_check;
+ if (end >= start)
+ count = end - start;
+ else
+ count = end + QDIO_MAX_BUFFERS_PER_Q - start;
+
+#ifdef CONFIG_QDIO_DEBUG
+ sprintf(dbf_text, "s=%2xc=%2x", start, count);
+ QDIO_DBF_TEXT4(0, trace, dbf_text);
+#endif /* CONFIG_QDIO_DEBUG */
+
+ if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
+ return;
+
+ q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr,
+ start, count, q->irq_ptr->int_parm);
+
+ /* for the next time */
+ q->first_to_kick = q->first_to_check;
+ q->qdio_error = 0;
+}
+
+static void __qdio_inbound_processing(struct qdio_q *q)
+{
+ qdio_perf_stat_inc(&perf_stats.tasklet_inbound);
+again:
+ if (!qdio_inbound_q_moved(q))
+ return;
+
+ qdio_kick_inbound_handler(q);
+
+ if (!qdio_inbound_q_done(q))
+ /* means poll time is not yet over */
+ goto again;
+
+ qdio_stop_polling(q);
+ /*
+ * We need to check again to not lose initiative after
+ * resetting the ACK state.
+ */
+ if (!qdio_inbound_q_done(q))
+ goto again;
+}
+
+/* inbound tasklet */
+void qdio_inbound_processing(unsigned long data)
+{
+ struct qdio_q *q = (struct qdio_q *)data;
+ __qdio_inbound_processing(q);
+}
+
+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);
+
+ /*
+ * Don't check 128 buffers, as otherwise qdio_inbound_q_moved
+ * would return 0.
+ */
+ count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK);
+ stop = add_buf(q->first_to_check, count);
+
+ /* need to set count to 1 for non-qebsm */
+ if (!is_qebsm(q))
+ count = 1;
+
+check_next:
+ if (q->first_to_check == stop)
+ return q->first_to_check;
+
+ count = get_buf_states(q, q->first_to_check, &state, count);
+ if (!count)
+ return q->first_to_check;
+
+ switch (state) {
+ case SLSB_P_OUTPUT_EMPTY:
+ /* the adapter got it */
+ QDIO_DBF_TEXT5(0, trace, "outpempt");
+
+ atomic_sub(count, &q->nr_buf_used);
+ q->first_to_check = add_buf(q->first_to_check, count);
+ /*
+ * We fetch all buffer states at once. get_buf_states may
+ * return count < stop. For QEBSM we do not loop.
+ */
+ if (is_qebsm(q))
+ break;
+ goto check_next;
+ case SLSB_P_OUTPUT_ERROR:
+ announce_buffer_error(q);
+ /* process the buffer, the upper layer will take care of it */
+ q->first_to_check = add_buf(q->first_to_check, count);
+ atomic_sub(count, &q->nr_buf_used);
+ break;
+ case SLSB_CU_OUTPUT_PRIMED:
+ /* the adapter has not fetched the output yet */
+ QDIO_DBF_TEXT5(0, trace, "outpprim");
+ break;
+ case SLSB_P_OUTPUT_NOT_INIT:
+ case SLSB_P_OUTPUT_HALTED:
+ break;
+ default:
+ BUG();
+ }
+ return q->first_to_check;
+}
+
+/* all buffers processed? */
+static inline int qdio_outbound_q_done(struct qdio_q *q)
+{
+ return atomic_read(&q->nr_buf_used) == 0;
+}
+
+static inline int qdio_outbound_q_moved(struct qdio_q *q)
+{
+ int bufnr;
+
+ bufnr = get_outbound_buffer_frontier(q);
+
+ if ((bufnr != q->last_move_ftc) || q->qdio_error) {
+ q->last_move_ftc = bufnr;
+ QDIO_DBF_TEXT4(0, trace, "oqhasmvd");
+ QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+ return 1;
+ } else
+ return 0;
+}
+
+/*
+ * VM could present us cc=2 and busy bit set on SIGA-write
+ * during reconfiguration of their Guest LAN (only in iqdio mode,
+ * otherwise qdio is asynchronous and cc=2 and busy bit there will take
+ * the queues down immediately).
+ *
+ * Therefore qdio_siga_output will try for a short time constantly,
+ * if such a condition occurs. If it doesn't change, it will
+ * increase the busy_siga_counter and save the timestamp, and
+ * schedule the queue for later processing. qdio_outbound_processing
+ * will check out the counter. If non-zero, it will call qdio_kick_outbound_q
+ * as often as the value of the counter. This will attempt further SIGA
+ * instructions. For each successful SIGA, the counter is
+ * decreased, for failing SIGAs the counter remains the same, after
+ * all. After some time of no movement, qdio_kick_outbound_q will
+ * finally fail and reflect corresponding error codes to call
+ * the upper layer module and have it take the queues down.
+ *
+ * Note that this is a change from the original HiperSockets design
+ * (saying cc=2 and busy bit means take the queues down), but in
+ * these days Guest LAN didn't exist... excessive cc=2 with busy bit
+ * conditions will still take the queues down, but the threshold is
+ * higher due to the Guest LAN environment.
+ *
+ * Called from outbound tasklet and do_QDIO handler.
+ */
+static void qdio_kick_outbound_q(struct qdio_q *q)
+{
+ int rc;
+#ifdef CONFIG_QDIO_DEBUG
+ char dbf_text[15];
+
+ QDIO_DBF_TEXT5(0, trace, "kickoutq");
+ QDIO_DBF_HEX5(0, trace, &q, sizeof(void *));
+#endif /* CONFIG_QDIO_DEBUG */
+
+ if (!need_siga_out(q))
+ return;
+
+ rc = qdio_siga_output(q);
+ switch (rc) {
+ case 0:
+ /* TODO: improve error handling for CC=0 case */
+#ifdef CONFIG_QDIO_DEBUG
+ if (q->u.out.timestamp) {
+ QDIO_DBF_TEXT3(0, trace, "cc2reslv");
+ sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no,
+ q->nr,
+ atomic_read(&q->u.out.busy_siga_counter));
+ QDIO_DBF_TEXT3(0, trace, dbf_text);
+ }
+#endif /* CONFIG_QDIO_DEBUG */
+ /* went smooth this time, reset timestamp */
+ q->u.out.timestamp = 0;
+ break;
+ /* cc=2 and busy bit */
+ case (2 | QDIO_ERROR_SIGA_BUSY):
+ atomic_inc(&q->u.out.busy_siga_counter);
+
+ /* if the last siga was successful, save timestamp here */
+ if (!q->u.out.timestamp)
+ q->u.out.timestamp = get_usecs();
+
+ /* if we're in time, don't touch qdio_error */
+ if (get_usecs() - q->u.out.timestamp < QDIO_BUSY_BIT_GIVE_UP) {
+ tasklet_schedule(&q->tasklet);
+ break;
+ }
+ QDIO_DBF_TEXT2(0, trace, "cc2REPRT");
+#ifdef CONFIG_QDIO_DEBUG
+ sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no, q->nr,
+ atomic_read(&q->u.out.busy_siga_counter));
+ QDIO_DBF_TEXT3(0, trace, dbf_text);
+#endif /* CONFIG_QDIO_DEBUG */
+ default:
+ /* for plain cc=1, 2 or 3 */
+ q->qdio_error = rc;
+ }
+}
+
+static void qdio_kick_outbound_handler(struct qdio_q *q)
+{
+ int start, end, count;
+#ifdef CONFIG_QDIO_DEBUG
+ char dbf_text[15];
+#endif
+
+ start = q->first_to_kick;
+ end = q->last_move_ftc;
+ if (end >= start)
+ count = end - start;
+ else
+ count = end + QDIO_MAX_BUFFERS_PER_Q - start;
+
+#ifdef CONFIG_QDIO_DEBUG
+ QDIO_DBF_TEXT4(0, trace, "kickouth");
+ QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+
+ sprintf(dbf_text, "s=%2xc=%2x", start, count);
+ QDIO_DBF_TEXT4(0, trace, dbf_text);
+#endif /* CONFIG_QDIO_DEBUG */
+
+ if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
+ return;
+
+ q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr, start, count,
+ q->irq_ptr->int_parm);
+
+ /* for the next time: */
+ q->first_to_kick = q->last_move_ftc;
+ q->qdio_error = 0;
+}
+
+static void __qdio_outbound_processing(struct qdio_q *q)
+{
+ int siga_attempts;
+
+ qdio_perf_stat_inc(&perf_stats.tasklet_outbound);
+
+ /* see comment in qdio_kick_outbound_q */
+ siga_attempts = atomic_read(&q->u.out.busy_siga_counter);
+ while (siga_attempts--) {
+ atomic_dec(&q->u.out.busy_siga_counter);
+ qdio_kick_outbound_q(q);
+ }
+
+ BUG_ON(atomic_read(&q->nr_buf_used) < 0);
+
+ if (qdio_outbound_q_moved(q))
+ qdio_kick_outbound_handler(q);
+
+ if (queue_type(q) == QDIO_ZFCP_QFMT) {
+ if (!pci_out_supported(q) && !qdio_outbound_q_done(q))
+ tasklet_schedule(&q->tasklet);
+ return;
+ }
+
+ /* bail out for HiperSockets unicast queues */
+ if (queue_type(q) == QDIO_IQDIO_QFMT && !multicast_outbound(q))
+ return;
+
+ if ((queue_type(q) == QDIO_IQDIO_QFMT) &&
+ (atomic_read(&q->nr_buf_used)) > QDIO_IQDIO_POLL_LVL) {
+ tasklet_schedule(&q->tasklet);
+ return;
+ }
+
+ if (q->u.out.pci_out_enabled)
+ return;
+
+ /*
+ * Now we know that queue type is either qeth without pci enabled
+ * or HiperSockets multicast. Make sure buffer switch from PRIMED to
+ * EMPTY is noticed and outbound_handler is called after some time.
+ */
+ if (qdio_outbound_q_done(q))
+ del_timer(&q->u.out.timer);
+ else {
+ if (!timer_pending(&q->u.out.timer)) {
+ mod_timer(&q->u.out.timer, jiffies + 10 * HZ);
+ qdio_perf_stat_inc(&perf_stats.debug_tl_out_timer);
+ }
+ }
+}
+
+/* outbound tasklet */
+void qdio_outbound_processing(unsigned long data)
+{
+ struct qdio_q *q = (struct qdio_q *)data;
+ __qdio_outbound_processing(q);
+}
+
+void qdio_outbound_timer(unsigned long data)
+{
+ struct qdio_q *q = (struct qdio_q *)data;
+ tasklet_schedule(&q->tasklet);
+}
+
+/* called from thinint inbound tasklet */
+void qdio_check_outbound_after_thinint(struct qdio_q *q)
+{
+ struct qdio_q *out;
+ int i;
+
+ if (!pci_out_supported(q))
+ return;
+
+ for_each_output_queue(q->irq_ptr, out, i)
+ if (!qdio_outbound_q_done(out))
+ tasklet_schedule(&out->tasklet);
+}
+
+static inline void qdio_set_state(struct qdio_irq *irq_ptr,
+ enum qdio_irq_states state)
+{
+#ifdef CONFIG_QDIO_DEBUG
+ char dbf_text[15];
+
+ QDIO_DBF_TEXT5(0, trace, "newstate");
+ sprintf(dbf_text, "%4x%4x", irq_ptr->schid.sch_no, state);
+ QDIO_DBF_TEXT5(0, trace, dbf_text);
+#endif /* CONFIG_QDIO_DEBUG */
+
+ irq_ptr->state = state;
+ mb();
+}
+
+static void qdio_irq_check_sense(struct subchannel_id schid, struct irb *irb)
+{
+ char dbf_text[15];
+
+ if (irb->esw.esw0.erw.cons) {
+ sprintf(dbf_text, "sens%4x", schid.sch_no);
+ QDIO_DBF_TEXT2(1, trace, dbf_text);
+ QDIO_DBF_HEX0(0, trace, irb, 64);
+ QDIO_DBF_HEX0(0, trace, irb->ecw, 64);
+ }
+}
+
+/* PCI interrupt handler */
+static void qdio_int_handler_pci(struct qdio_irq *irq_ptr)
+{
+ int i;
+ struct qdio_q *q;
+
+ qdio_perf_stat_inc(&perf_stats.pci_int);
+
+ for_each_input_queue(irq_ptr, q, i)
+ tasklet_schedule(&q->tasklet);
+
+ if (!(irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED))
+ return;
+
+ for_each_output_queue(irq_ptr, q, i) {
+ if (qdio_outbound_q_done(q))
+ continue;
+
+ if (!siga_syncs_out_pci(q))
+ qdio_siga_sync_q(q);
+
+ tasklet_schedule(&q->tasklet);
+ }
+}
+
+static void qdio_handle_activate_check(struct ccw_device *cdev,
+ unsigned long intparm, int cstat, int dstat)
+{
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
+ struct qdio_q *q;
+ char dbf_text[15];
+
+ QDIO_DBF_TEXT2(1, trace, "ick2");
+ sprintf(dbf_text, "%s", dev_name(&cdev->dev));
+ QDIO_DBF_TEXT2(1, trace, dbf_text);
+ QDIO_DBF_HEX2(0, trace, &intparm, sizeof(int));
+ QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int));
+ QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int));
+
+ if (irq_ptr->nr_input_qs) {
+ q = irq_ptr->input_qs[0];
+ } else if (irq_ptr->nr_output_qs) {
+ q = irq_ptr->output_qs[0];
+ } else {
+ dump_stack();
+ goto no_handler;
+ }
+ q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
+ 0, -1, -1, irq_ptr->int_parm);
+no_handler:
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
+}
+
+static void qdio_call_shutdown(struct work_struct *work)
+{
+ struct ccw_device_private *priv;
+ struct ccw_device *cdev;
+
+ priv = container_of(work, struct ccw_device_private, kick_work);
+ cdev = priv->cdev;
+ qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
+ put_device(&cdev->dev);
+}
+
+static void qdio_int_error(struct ccw_device *cdev)
+{
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
+
+ switch (irq_ptr->state) {
+ case QDIO_IRQ_STATE_INACTIVE:
+ case QDIO_IRQ_STATE_CLEANUP:
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
+ break;
+ case QDIO_IRQ_STATE_ESTABLISHED:
+ case QDIO_IRQ_STATE_ACTIVE:
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
+ if (get_device(&cdev->dev)) {
+ /* Can't call shutdown from interrupt context. */
+ PREPARE_WORK(&cdev->private->kick_work,
+ qdio_call_shutdown);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
+ }
+ break;
+ default:
+ WARN_ON(1);
+ }
+ wake_up(&cdev->private->wait_q);
+}
+
+static int qdio_establish_check_errors(struct ccw_device *cdev, int cstat,
+ int dstat)
+{
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
+
+ if (cstat || (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))) {
+ QDIO_DBF_TEXT2(1, setup, "eq:ckcon");
+ goto error;
+ }
+
+ if (!(dstat & DEV_STAT_DEV_END)) {
+ QDIO_DBF_TEXT2(1, setup, "eq:no de");
+ goto error;
+ }
+
+ if (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) {
+ QDIO_DBF_TEXT2(1, setup, "eq:badio");
+ goto error;
+ }
+ return 0;
+error:
+ QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int));
+ QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int));
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
+ return 1;
+}
+
+static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat,
+ int dstat)
+{
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
+ char dbf_text[15];
+
+ sprintf(dbf_text, "qehi%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ QDIO_DBF_TEXT0(0, trace, dbf_text);
+
+ if (!qdio_establish_check_errors(cdev, cstat, dstat))
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED);
+}
+
+/* qdio interrupt handler */
+void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
+ struct irb *irb)
+{
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
+ int cstat, dstat;
+ char dbf_text[15];
+
+ qdio_perf_stat_inc(&perf_stats.qdio_int);
+
+ if (!intparm || !irq_ptr) {
+ sprintf(dbf_text, "qihd%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT2(1, setup, dbf_text);
+ return;
+ }
+
+ if (IS_ERR(irb)) {
+ switch (PTR_ERR(irb)) {
+ case -EIO:
+ sprintf(dbf_text, "ierr%4x", irq_ptr->schid.sch_no);
+ QDIO_DBF_TEXT2(1, setup, dbf_text);
+ qdio_int_error(cdev);
+ return;
+ case -ETIMEDOUT:
+ sprintf(dbf_text, "qtoh%4x", irq_ptr->schid.sch_no);
+ QDIO_DBF_TEXT2(1, setup, dbf_text);
+ qdio_int_error(cdev);
+ return;
+ default:
+ WARN_ON(1);
+ return;
+ }
+ }
+ qdio_irq_check_sense(irq_ptr->schid, irb);
+
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
+
+ switch (irq_ptr->state) {
+ case QDIO_IRQ_STATE_INACTIVE:
+ qdio_establish_handle_irq(cdev, cstat, dstat);
+ break;
+
+ case QDIO_IRQ_STATE_CLEANUP:
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
+ break;
+
+ case QDIO_IRQ_STATE_ESTABLISHED:
+ case QDIO_IRQ_STATE_ACTIVE:
+ if (cstat & SCHN_STAT_PCI) {
+ qdio_int_handler_pci(irq_ptr);
+ /* no state change so no need to wake up wait_q */
+ return;
+ }
+ if ((cstat & ~SCHN_STAT_PCI) || dstat) {
+ qdio_handle_activate_check(cdev, intparm, cstat,
+ dstat);
+ break;
+ }
+ default:
+ WARN_ON(1);
+ }
+ wake_up(&cdev->private->wait_q);
+}
+
+/**
+ * qdio_get_ssqd_desc - get qdio subchannel description
+ * @cdev: ccw device to get description for
+ *
+ * Returns a pointer to the saved qdio subchannel description,
+ * or NULL for not setup qdio devices.
+ */
+struct qdio_ssqd_desc *qdio_get_ssqd_desc(struct ccw_device *cdev)
+{
+ struct qdio_irq *irq_ptr;
+ char dbf_text[15];
+
+ sprintf(dbf_text, "qssq%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
+ irq_ptr = cdev->private->qdio_data;
+ if (!irq_ptr)
+ return NULL;
+
+ return &irq_ptr->ssqd_desc;
+}
+EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc);
+
+/**
+ * qdio_cleanup - shutdown queues and free data structures
+ * @cdev: associated ccw device
+ * @how: use halt or clear to shutdown
+ *
+ * This function calls qdio_shutdown() for @cdev with method @how
+ * and on success qdio_free() for @cdev.
+ */
+int qdio_cleanup(struct ccw_device *cdev, int how)
+{
+ struct qdio_irq *irq_ptr;
+ char dbf_text[15];
+ int rc;
+
+ sprintf(dbf_text, "qcln%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
+ irq_ptr = cdev->private->qdio_data;
+ if (!irq_ptr)
+ return -ENODEV;
+
+ rc = qdio_shutdown(cdev, how);
+ if (rc == 0)
+ rc = qdio_free(cdev);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qdio_cleanup);
+
+static void qdio_shutdown_queues(struct ccw_device *cdev)
+{
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
+ struct qdio_q *q;
+ int i;
+
+ for_each_input_queue(irq_ptr, q, i)
+ tasklet_disable(&q->tasklet);
+
+ for_each_output_queue(irq_ptr, q, i) {
+ tasklet_disable(&q->tasklet);
+ del_timer(&q->u.out.timer);
+ }
+}
+
+/**
+ * qdio_shutdown - shut down a qdio subchannel
+ * @cdev: associated ccw device
+ * @how: use halt or clear to shutdown
+ */
+int qdio_shutdown(struct ccw_device *cdev, int how)
+{
+ struct qdio_irq *irq_ptr;
+ int rc;
+ unsigned long flags;
+ char dbf_text[15];
+
+ sprintf(dbf_text, "qshu%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
+ irq_ptr = cdev->private->qdio_data;
+ if (!irq_ptr)
+ return -ENODEV;
+
+ mutex_lock(&irq_ptr->setup_mutex);
+ /*
+ * Subchannel was already shot down. We cannot prevent being called
+ * twice since cio may trigger a shutdown asynchronously.
+ */
+ if (irq_ptr->state == QDIO_IRQ_STATE_INACTIVE) {
+ mutex_unlock(&irq_ptr->setup_mutex);
+ return 0;
+ }
+
+ tiqdio_remove_input_queues(irq_ptr);
+ qdio_shutdown_queues(cdev);
+ qdio_shutdown_debug_entries(irq_ptr, cdev);
+
+ /* cleanup subchannel */
+ spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+
+ if (how & QDIO_FLAG_CLEANUP_USING_CLEAR)
+ rc = ccw_device_clear(cdev, QDIO_DOING_CLEANUP);
+ else
+ /* default behaviour is halt */
+ rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
+ if (rc) {
+ sprintf(dbf_text, "sher%4x", irq_ptr->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ sprintf(dbf_text, "rc=%d", rc);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ goto no_cleanup;
+ }
+
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_CLEANUP);
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+ wait_event_interruptible_timeout(cdev->private->wait_q,
+ irq_ptr->state == QDIO_IRQ_STATE_INACTIVE ||
+ irq_ptr->state == QDIO_IRQ_STATE_ERR,
+ 10 * HZ);
+ spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+
+no_cleanup:
+ qdio_shutdown_thinint(irq_ptr);
+
+ /* restore interrupt handler */
+ if ((void *)cdev->handler == (void *)qdio_int_handler)
+ cdev->handler = irq_ptr->orig_handler;
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
+ mutex_unlock(&irq_ptr->setup_mutex);
+ if (rc)
+ return rc;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qdio_shutdown);
+
+/**
+ * qdio_free - free data structures for a qdio subchannel
+ * @cdev: associated ccw device
+ */
+int qdio_free(struct ccw_device *cdev)
+{
+ struct qdio_irq *irq_ptr;
+ char dbf_text[15];
+
+ sprintf(dbf_text, "qfre%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
+ irq_ptr = cdev->private->qdio_data;
+ if (!irq_ptr)
+ return -ENODEV;
+
+ mutex_lock(&irq_ptr->setup_mutex);
+ cdev->private->qdio_data = NULL;
+ mutex_unlock(&irq_ptr->setup_mutex);
+
+ qdio_release_memory(irq_ptr);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qdio_free);
+
+/**
+ * qdio_initialize - allocate and establish queues for a qdio subchannel
+ * @init_data: initialization data
+ *
+ * This function first allocates queues via qdio_allocate() and on success
+ * establishes them via qdio_establish().
+ */
+int qdio_initialize(struct qdio_initialize *init_data)
+{
+ int rc;
+ char dbf_text[15];
+
+ sprintf(dbf_text, "qini%4x", init_data->cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
+ rc = qdio_allocate(init_data);
+ if (rc)
+ return rc;
+
+ rc = qdio_establish(init_data);
+ if (rc)
+ qdio_free(init_data->cdev);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qdio_initialize);
+
+/**
+ * qdio_allocate - allocate qdio queues and associated data
+ * @init_data: initialization data
+ */
+int qdio_allocate(struct qdio_initialize *init_data)
+{
+ struct qdio_irq *irq_ptr;
+ char dbf_text[15];
+
+ sprintf(dbf_text, "qalc%4x", init_data->cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
+ if ((init_data->no_input_qs && !init_data->input_handler) ||
+ (init_data->no_output_qs && !init_data->output_handler))
+ return -EINVAL;
+
+ if ((init_data->no_input_qs > QDIO_MAX_QUEUES_PER_IRQ) ||
+ (init_data->no_output_qs > QDIO_MAX_QUEUES_PER_IRQ))
+ return -EINVAL;
+
+ if ((!init_data->input_sbal_addr_array) ||
+ (!init_data->output_sbal_addr_array))
+ return -EINVAL;
+
+ qdio_allocate_do_dbf(init_data);
+
+ /* irq_ptr must be in GFP_DMA since it contains ccw1.cda */
+ irq_ptr = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!irq_ptr)
+ goto out_err;
+ QDIO_DBF_TEXT0(0, setup, "irq_ptr:");
+ QDIO_DBF_HEX0(0, setup, &irq_ptr, sizeof(void *));
+
+ mutex_init(&irq_ptr->setup_mutex);
+
+ /*
+ * Allocate a page for the chsc calls in qdio_establish.
+ * Must be pre-allocated since a zfcp recovery will call
+ * qdio_establish. In case of low memory and swap on a zfcp disk
+ * we may not be able to allocate memory otherwise.
+ */
+ irq_ptr->chsc_page = get_zeroed_page(GFP_KERNEL);
+ if (!irq_ptr->chsc_page)
+ goto out_rel;
+
+ /* qdr is used in ccw1.cda which is u32 */
+ irq_ptr->qdr = (struct qdr *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!irq_ptr->qdr)
+ goto out_rel;
+ WARN_ON((unsigned long)irq_ptr->qdr & 0xfff);
+
+ QDIO_DBF_TEXT0(0, setup, "qdr:");
+ QDIO_DBF_HEX0(0, setup, &irq_ptr->qdr, sizeof(void *));
+
+ if (qdio_allocate_qs(irq_ptr, init_data->no_input_qs,
+ init_data->no_output_qs))
+ goto out_rel;
+
+ init_data->cdev->private->qdio_data = irq_ptr;
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
+ return 0;
+out_rel:
+ qdio_release_memory(irq_ptr);
+out_err:
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(qdio_allocate);
+
+/**
+ * qdio_establish - establish queues on a qdio subchannel
+ * @init_data: initialization data
+ */
+int qdio_establish(struct qdio_initialize *init_data)
+{
+ char dbf_text[20];
+ struct qdio_irq *irq_ptr;
+ struct ccw_device *cdev = init_data->cdev;
+ unsigned long saveflags;
+ int rc;
+
+ sprintf(dbf_text, "qest%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
+ irq_ptr = cdev->private->qdio_data;
+ if (!irq_ptr)
+ return -ENODEV;
+
+ if (cdev->private->state != DEV_STATE_ONLINE)
+ return -EINVAL;
+
+ mutex_lock(&irq_ptr->setup_mutex);
+ qdio_setup_irq(init_data);
+
+ rc = qdio_establish_thinint(irq_ptr);
+ if (rc) {
+ mutex_unlock(&irq_ptr->setup_mutex);
+ qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
+ return rc;
+ }
+
+ /* establish q */
+ irq_ptr->ccw.cmd_code = irq_ptr->equeue.cmd;
+ irq_ptr->ccw.flags = CCW_FLAG_SLI;
+ irq_ptr->ccw.count = irq_ptr->equeue.count;
+ irq_ptr->ccw.cda = (u32)((addr_t)irq_ptr->qdr);
+
+ spin_lock_irqsave(get_ccwdev_lock(cdev), saveflags);
+ ccw_device_set_options_mask(cdev, 0);
+
+ rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ESTABLISH, 0, 0);
+ if (rc) {
+ sprintf(dbf_text, "eq:io%4x", irq_ptr->schid.sch_no);
+ QDIO_DBF_TEXT2(1, setup, dbf_text);
+ sprintf(dbf_text, "eq:rc%4x", rc);
+ QDIO_DBF_TEXT2(1, setup, dbf_text);
+ }
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags);
+
+ if (rc) {
+ mutex_unlock(&irq_ptr->setup_mutex);
+ qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
+ return rc;
+ }
+
+ wait_event_interruptible_timeout(cdev->private->wait_q,
+ irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED ||
+ irq_ptr->state == QDIO_IRQ_STATE_ERR, HZ);
+
+ if (irq_ptr->state != QDIO_IRQ_STATE_ESTABLISHED) {
+ mutex_unlock(&irq_ptr->setup_mutex);
+ qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
+ return -EIO;
+ }
+
+ qdio_setup_ssqd_info(irq_ptr);
+ sprintf(dbf_text, "qDmmwc%2x", irq_ptr->ssqd_desc.mmwc);
+ QDIO_DBF_TEXT2(0, setup, dbf_text);
+ sprintf(dbf_text, "qib ac%2x", irq_ptr->qib.ac);
+ QDIO_DBF_TEXT2(0, setup, dbf_text);
+
+ /* qebsm is now setup if available, initialize buffer states */
+ qdio_init_buf_states(irq_ptr);
+
+ mutex_unlock(&irq_ptr->setup_mutex);
+ qdio_print_subchannel_info(irq_ptr, cdev);
+ qdio_setup_debug_entries(irq_ptr, cdev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qdio_establish);
+
+/**
+ * qdio_activate - activate queues on a qdio subchannel
+ * @cdev: associated cdev
+ */
+int qdio_activate(struct ccw_device *cdev)
+{
+ struct qdio_irq *irq_ptr;
+ int rc;
+ unsigned long saveflags;
+ char dbf_text[20];
+
+ sprintf(dbf_text, "qact%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
+ irq_ptr = cdev->private->qdio_data;
+ if (!irq_ptr)
+ return -ENODEV;
+
+ if (cdev->private->state != DEV_STATE_ONLINE)
+ return -EINVAL;
+
+ mutex_lock(&irq_ptr->setup_mutex);
+ if (irq_ptr->state == QDIO_IRQ_STATE_INACTIVE) {
+ rc = -EBUSY;
+ goto out;
+ }
+
+ irq_ptr->ccw.cmd_code = irq_ptr->aqueue.cmd;
+ irq_ptr->ccw.flags = CCW_FLAG_SLI;
+ irq_ptr->ccw.count = irq_ptr->aqueue.count;
+ irq_ptr->ccw.cda = 0;
+
+ spin_lock_irqsave(get_ccwdev_lock(cdev), saveflags);
+ ccw_device_set_options(cdev, CCWDEV_REPORT_ALL);
+
+ rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ACTIVATE,
+ 0, DOIO_DENY_PREFETCH);
+ if (rc) {
+ sprintf(dbf_text, "aq:io%4x", irq_ptr->schid.sch_no);
+ QDIO_DBF_TEXT2(1, setup, dbf_text);
+ sprintf(dbf_text, "aq:rc%4x", rc);
+ QDIO_DBF_TEXT2(1, setup, dbf_text);
+ }
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags);
+
+ if (rc)
+ goto out;
+
+ if (is_thinint_irq(irq_ptr))
+ tiqdio_add_input_queues(irq_ptr);
+
+ /* wait for subchannel to become active */
+ msleep(5);
+
+ switch (irq_ptr->state) {
+ case QDIO_IRQ_STATE_STOPPED:
+ case QDIO_IRQ_STATE_ERR:
+ mutex_unlock(&irq_ptr->setup_mutex);
+ qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
+ return -EIO;
+ default:
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ACTIVE);
+ rc = 0;
+ }
+out:
+ mutex_unlock(&irq_ptr->setup_mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qdio_activate);
+
+static inline int buf_in_between(int bufnr, int start, int count)
+{
+ int end = add_buf(start, count);
+
+ if (end > start) {
+ if (bufnr >= start && bufnr < end)
+ return 1;
+ else
+ return 0;
+ }
+
+ /* wrap-around case */
+ if ((bufnr >= start && bufnr <= QDIO_MAX_BUFFERS_PER_Q) ||
+ (bufnr < end))
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * handle_inbound - reset processed input buffers
+ * @q: queue containing the buffers
+ * @callflags: flags
+ * @bufnr: first buffer to process
+ * @count: how many buffers are emptied
+ */
+static void handle_inbound(struct qdio_q *q, unsigned int callflags,
+ int bufnr, int count)
+{
+ unsigned long flags;
+ int used, rc;
+
+ /*
+ * do_QDIO could run in parallel with the queue tasklet so the
+ * upper-layer programm could empty the ACK'ed buffer here.
+ * If that happens we must clear the polling flag, otherwise
+ * qdio_stop_polling() could set the buffer to NOT_INIT after
+ * it was set to EMPTY which would kill us.
+ */
+ spin_lock_irqsave(&q->u.in.lock, flags);
+ if (q->u.in.polling)
+ if (buf_in_between(q->last_move_ftc, bufnr, count))
+ q->u.in.polling = 0;
+
+ count = set_buf_states(q, bufnr, SLSB_CU_INPUT_EMPTY, count);
+ spin_unlock_irqrestore(&q->u.in.lock, flags);
+
+ used = atomic_add_return(count, &q->nr_buf_used) - count;
+ BUG_ON(used + count > QDIO_MAX_BUFFERS_PER_Q);
+
+ /* no need to signal as long as the adapter had free buffers */
+ if (used)
+ return;
+
+ if (need_siga_in(q)) {
+ rc = qdio_siga_input(q);
+ if (rc)
+ q->qdio_error = rc;
+ }
+}
+
+/**
+ * handle_outbound - process filled outbound buffers
+ * @q: queue containing the buffers
+ * @callflags: flags
+ * @bufnr: first buffer to process
+ * @count: how many buffers are filled
+ */
+static void handle_outbound(struct qdio_q *q, unsigned int callflags,
+ int bufnr, int count)
+{
+ unsigned char state;
+ int used;
+
+ qdio_perf_stat_inc(&perf_stats.outbound_handler);
+
+ count = set_buf_states(q, bufnr, SLSB_CU_OUTPUT_PRIMED, count);
+ used = atomic_add_return(count, &q->nr_buf_used);
+ BUG_ON(used > QDIO_MAX_BUFFERS_PER_Q);
+
+ if (callflags & QDIO_FLAG_PCI_OUT)
+ q->u.out.pci_out_enabled = 1;
+ else
+ q->u.out.pci_out_enabled = 0;
+
+ if (queue_type(q) == QDIO_IQDIO_QFMT) {
+ if (multicast_outbound(q))
+ 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;
+ qdio_kick_outbound_q(q);
+ } else {
+ /*
+ * One siga-w per buffer required for unicast
+ * HiperSockets.
+ */
+ q->u.out.use_enh_siga = 0;
+ while (count--)
+ qdio_kick_outbound_q(q);
+ }
+ goto out;
+ }
+
+ if (need_siga_sync(q)) {
+ qdio_siga_sync_q(q);
+ goto out;
+ }
+
+ /* try to fast requeue buffers */
+ get_buf_state(q, prev_buf(bufnr), &state);
+ if (state != SLSB_CU_OUTPUT_PRIMED)
+ qdio_kick_outbound_q(q);
+ else {
+ QDIO_DBF_TEXT5(0, trace, "fast-req");
+ qdio_perf_stat_inc(&perf_stats.fast_requeue);
+ }
+out:
+ /* Fixme: could wait forever if called from process context */
+ tasklet_schedule(&q->tasklet);
+}
+
+/**
+ * do_QDIO - process input or output buffers
+ * @cdev: associated ccw_device for the qdio subchannel
+ * @callflags: input or output and special flags from the program
+ * @q_nr: queue number
+ * @bufnr: buffer number
+ * @count: how many buffers to process
+ */
+int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
+ int q_nr, int bufnr, int count)
+{
+ struct qdio_irq *irq_ptr;
+#ifdef CONFIG_QDIO_DEBUG
+ char dbf_text[20];
+
+ sprintf(dbf_text, "doQD%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT3(0, trace, dbf_text);
+#endif /* CONFIG_QDIO_DEBUG */
+
+ if ((bufnr > QDIO_MAX_BUFFERS_PER_Q) ||
+ (count > QDIO_MAX_BUFFERS_PER_Q) ||
+ (q_nr > QDIO_MAX_QUEUES_PER_IRQ))
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ irq_ptr = cdev->private->qdio_data;
+ if (!irq_ptr)
+ return -ENODEV;
+
+#ifdef CONFIG_QDIO_DEBUG
+ if (callflags & QDIO_FLAG_SYNC_INPUT)
+ QDIO_DBF_HEX3(0, trace, &irq_ptr->input_qs[q_nr],
+ sizeof(void *));
+ else
+ QDIO_DBF_HEX3(0, trace, &irq_ptr->output_qs[q_nr],
+ sizeof(void *));
+
+ sprintf(dbf_text, "flag%04x", callflags);
+ QDIO_DBF_TEXT3(0, trace, dbf_text);
+ sprintf(dbf_text, "qi%02xct%02x", bufnr, count);
+ QDIO_DBF_TEXT3(0, trace, dbf_text);
+#endif /* CONFIG_QDIO_DEBUG */
+
+ if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)
+ return -EBUSY;
+
+ if (callflags & QDIO_FLAG_SYNC_INPUT)
+ handle_inbound(irq_ptr->input_qs[q_nr],
+ callflags, bufnr, count);
+ else if (callflags & QDIO_FLAG_SYNC_OUTPUT)
+ handle_outbound(irq_ptr->output_qs[q_nr],
+ callflags, bufnr, count);
+ else {
+ QDIO_DBF_TEXT3(1, trace, "doQD:inv");
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(do_QDIO);
+
+static int __init init_QDIO(void)
+{
+ int rc;
+
+ rc = qdio_setup_init();
+ if (rc)
+ return rc;
+ rc = tiqdio_allocate_memory();
+ if (rc)
+ goto out_cache;
+ rc = qdio_debug_init();
+ if (rc)
+ goto out_ti;
+ rc = qdio_setup_perf_stats();
+ if (rc)
+ goto out_debug;
+ rc = tiqdio_register_thinints();
+ if (rc)
+ goto out_perf;
+ return 0;
+
+out_perf:
+ qdio_remove_perf_stats();
+out_debug:
+ qdio_debug_exit();
+out_ti:
+ tiqdio_free_memory();
+out_cache:
+ qdio_setup_exit();
+ return rc;
+}
+
+static void __exit exit_QDIO(void)
+{
+ tiqdio_unregister_thinints();
+ tiqdio_free_memory();
+ qdio_remove_perf_stats();
+ qdio_debug_exit();
+ qdio_setup_exit();
+}
+
+module_init(init_QDIO);
+module_exit(exit_QDIO);
diff --git a/drivers/s390/cio/qdio_perf.c b/drivers/s390/cio/qdio_perf.c
new file mode 100644
index 00000000000..ec5c4a41423
--- /dev/null
+++ b/drivers/s390/cio/qdio_perf.c
@@ -0,0 +1,151 @@
+/*
+ * drivers/s390/cio/qdio_perf.c
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Author: Jan Glauber (jang@linux.vnet.ibm.com)
+ */
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/ccwdev.h>
+
+#include "cio.h"
+#include "css.h"
+#include "device.h"
+#include "ioasm.h"
+#include "chsc.h"
+#include "qdio_debug.h"
+#include "qdio_perf.h"
+
+int qdio_performance_stats;
+struct qdio_perf_stats perf_stats;
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *qdio_perf_pde;
+#endif
+
+inline void qdio_perf_stat_inc(atomic_long_t *count)
+{
+ if (qdio_performance_stats)
+ atomic_long_inc(count);
+}
+
+inline void qdio_perf_stat_dec(atomic_long_t *count)
+{
+ if (qdio_performance_stats)
+ atomic_long_dec(count);
+}
+
+/*
+ * procfs functions
+ */
+static int qdio_perf_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "Number of qdio interrupts\t\t\t: %li\n",
+ (long)atomic_long_read(&perf_stats.qdio_int));
+ seq_printf(m, "Number of PCI interrupts\t\t\t: %li\n",
+ (long)atomic_long_read(&perf_stats.pci_int));
+ seq_printf(m, "Number of adapter interrupts\t\t\t: %li\n",
+ (long)atomic_long_read(&perf_stats.thin_int));
+ seq_printf(m, "\n");
+ seq_printf(m, "Inbound tasklet runs\t\t\t\t: %li\n",
+ (long)atomic_long_read(&perf_stats.tasklet_inbound));
+ seq_printf(m, "Outbound tasklet runs\t\t\t\t: %li\n",
+ (long)atomic_long_read(&perf_stats.tasklet_outbound));
+ seq_printf(m, "Adapter interrupt tasklet runs/loops\t\t: %li/%li\n",
+ (long)atomic_long_read(&perf_stats.tasklet_thinint),
+ (long)atomic_long_read(&perf_stats.tasklet_thinint_loop));
+ seq_printf(m, "Adapter interrupt inbound tasklet runs/loops\t: %li/%li\n",
+ (long)atomic_long_read(&perf_stats.thinint_inbound),
+ (long)atomic_long_read(&perf_stats.thinint_inbound_loop));
+ seq_printf(m, "\n");
+ seq_printf(m, "Number of SIGA In issued\t\t\t: %li\n",
+ (long)atomic_long_read(&perf_stats.siga_in));
+ seq_printf(m, "Number of SIGA Out issued\t\t\t: %li\n",
+ (long)atomic_long_read(&perf_stats.siga_out));
+ seq_printf(m, "Number of SIGA Sync issued\t\t\t: %li\n",
+ (long)atomic_long_read(&perf_stats.siga_sync));
+ seq_printf(m, "\n");
+ seq_printf(m, "Number of inbound transfers\t\t\t: %li\n",
+ (long)atomic_long_read(&perf_stats.inbound_handler));
+ seq_printf(m, "Number of outbound transfers\t\t\t: %li\n",
+ (long)atomic_long_read(&perf_stats.outbound_handler));
+ seq_printf(m, "\n");
+ seq_printf(m, "Number of fast requeues (outg. SBAL w/o SIGA)\t: %li\n",
+ (long)atomic_long_read(&perf_stats.fast_requeue));
+ seq_printf(m, "Number of outbound tasklet mod_timer calls\t: %li\n",
+ (long)atomic_long_read(&perf_stats.debug_tl_out_timer));
+ seq_printf(m, "Number of stop polling calls\t\t\t: %li\n",
+ (long)atomic_long_read(&perf_stats.debug_stop_polling));
+ seq_printf(m, "AI inbound tasklet loops after stop polling\t: %li\n",
+ (long)atomic_long_read(&perf_stats.thinint_inbound_loop2));
+ seq_printf(m, "\n");
+ return 0;
+}
+static int qdio_perf_seq_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, qdio_perf_proc_show, NULL);
+}
+
+static struct file_operations qdio_perf_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = qdio_perf_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/*
+ * sysfs functions
+ */
+static ssize_t qdio_perf_stats_show(struct bus_type *bus, char *buf)
+{
+ return sprintf(buf, "%i\n", qdio_performance_stats ? 1 : 0);
+}
+
+static ssize_t qdio_perf_stats_store(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ unsigned long i;
+
+ if (strict_strtoul(buf, 16, &i) != 0)
+ return -EINVAL;
+ if ((i != 0) && (i != 1))
+ return -EINVAL;
+ if (i == qdio_performance_stats)
+ return count;
+
+ qdio_performance_stats = i;
+ /* reset performance statistics */
+ if (i == 0)
+ memset(&perf_stats, 0, sizeof(struct qdio_perf_stats));
+ return count;
+}
+
+static BUS_ATTR(qdio_performance_stats, 0644, qdio_perf_stats_show,
+ qdio_perf_stats_store);
+
+int __init qdio_setup_perf_stats(void)
+{
+ int rc;
+
+ rc = bus_create_file(&ccw_bus_type, &bus_attr_qdio_performance_stats);
+ if (rc)
+ return rc;
+
+#ifdef CONFIG_PROC_FS
+ memset(&perf_stats, 0, sizeof(struct qdio_perf_stats));
+ qdio_perf_pde = proc_create("qdio_perf", S_IFREG | S_IRUGO,
+ NULL, &qdio_perf_proc_fops);
+#endif
+ return 0;
+}
+
+void qdio_remove_perf_stats(void)
+{
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("qdio_perf", NULL);
+#endif
+ bus_remove_file(&ccw_bus_type, &bus_attr_qdio_performance_stats);
+}
diff --git a/drivers/s390/cio/qdio_perf.h b/drivers/s390/cio/qdio_perf.h
new file mode 100644
index 00000000000..5c406a8b738
--- /dev/null
+++ b/drivers/s390/cio/qdio_perf.h
@@ -0,0 +1,54 @@
+/*
+ * drivers/s390/cio/qdio_perf.h
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Author: Jan Glauber (jang@linux.vnet.ibm.com)
+ */
+#ifndef QDIO_PERF_H
+#define QDIO_PERF_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <asm/atomic.h>
+
+struct qdio_perf_stats {
+ /* interrupt handler calls */
+ atomic_long_t qdio_int;
+ atomic_long_t pci_int;
+ atomic_long_t thin_int;
+
+ /* tasklet runs */
+ atomic_long_t tasklet_inbound;
+ atomic_long_t tasklet_outbound;
+ atomic_long_t tasklet_thinint;
+ atomic_long_t tasklet_thinint_loop;
+ atomic_long_t thinint_inbound;
+ atomic_long_t thinint_inbound_loop;
+ atomic_long_t thinint_inbound_loop2;
+
+ /* signal adapter calls */
+ atomic_long_t siga_out;
+ atomic_long_t siga_in;
+ atomic_long_t siga_sync;
+
+ /* misc */
+ atomic_long_t inbound_handler;
+ atomic_long_t outbound_handler;
+ atomic_long_t fast_requeue;
+
+ /* for debugging */
+ atomic_long_t debug_tl_out_timer;
+ atomic_long_t debug_stop_polling;
+};
+
+extern struct qdio_perf_stats perf_stats;
+extern int qdio_performance_stats;
+
+int qdio_setup_perf_stats(void);
+void qdio_remove_perf_stats(void);
+
+extern void qdio_perf_stat_inc(atomic_long_t *count);
+extern void qdio_perf_stat_dec(atomic_long_t *count);
+
+#endif
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
new file mode 100644
index 00000000000..a0b6b46e746
--- /dev/null
+++ b/drivers/s390/cio/qdio_setup.c
@@ -0,0 +1,506 @@
+/*
+ * driver/s390/cio/qdio_setup.c
+ *
+ * qdio queue initialization
+ *
+ * Copyright (C) IBM Corp. 2008
+ * Author(s): Jan Glauber <jang@linux.vnet.ibm.com>
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <asm/qdio.h>
+
+#include "cio.h"
+#include "css.h"
+#include "device.h"
+#include "ioasm.h"
+#include "chsc.h"
+#include "qdio.h"
+#include "qdio_debug.h"
+
+static struct kmem_cache *qdio_q_cache;
+
+/*
+ * qebsm is only available under 64bit but the adapter sets the feature
+ * flag anyway, so we manually override it.
+ */
+static inline int qebsm_possible(void)
+{
+#ifdef CONFIG_64BIT
+ return css_general_characteristics.qebsm;
+#endif
+ return 0;
+}
+
+/*
+ * qib_param_field: pointer to 128 bytes or NULL, if no param field
+ * nr_input_qs: pointer to nr_queues*128 words of data or NULL
+ */
+static void set_impl_params(struct qdio_irq *irq_ptr,
+ unsigned int qib_param_field_format,
+ unsigned char *qib_param_field,
+ unsigned long *input_slib_elements,
+ unsigned long *output_slib_elements)
+{
+ struct qdio_q *q;
+ int i, j;
+
+ if (!irq_ptr)
+ return;
+
+ WARN_ON((unsigned long)&irq_ptr->qib & 0xff);
+ irq_ptr->qib.pfmt = qib_param_field_format;
+ if (qib_param_field)
+ memcpy(irq_ptr->qib.parm, qib_param_field,
+ QDIO_MAX_BUFFERS_PER_Q);
+
+ if (!input_slib_elements)
+ goto output;
+
+ for_each_input_queue(irq_ptr, q, i) {
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
+ q->slib->slibe[j].parms =
+ input_slib_elements[i * QDIO_MAX_BUFFERS_PER_Q + j];
+ }
+output:
+ if (!output_slib_elements)
+ return;
+
+ for_each_output_queue(irq_ptr, q, i) {
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
+ q->slib->slibe[j].parms =
+ output_slib_elements[i * QDIO_MAX_BUFFERS_PER_Q + j];
+ }
+}
+
+static int __qdio_allocate_qs(struct qdio_q **irq_ptr_qs, int nr_queues)
+{
+ struct qdio_q *q;
+ int i;
+
+ for (i = 0; i < nr_queues; i++) {
+ q = kmem_cache_alloc(qdio_q_cache, GFP_KERNEL);
+ if (!q)
+ return -ENOMEM;
+ WARN_ON((unsigned long)q & 0xff);
+
+ q->slib = (struct slib *) __get_free_page(GFP_KERNEL);
+ if (!q->slib) {
+ kmem_cache_free(qdio_q_cache, q);
+ return -ENOMEM;
+ }
+ WARN_ON((unsigned long)q->slib & 0x7ff);
+ irq_ptr_qs[i] = q;
+ }
+ return 0;
+}
+
+int qdio_allocate_qs(struct qdio_irq *irq_ptr, int nr_input_qs, int nr_output_qs)
+{
+ int rc;
+
+ rc = __qdio_allocate_qs(irq_ptr->input_qs, nr_input_qs);
+ if (rc)
+ return rc;
+ rc = __qdio_allocate_qs(irq_ptr->output_qs, nr_output_qs);
+ return rc;
+}
+
+static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr,
+ qdio_handler_t *handler, int i)
+{
+ /* must be cleared by every qdio_establish */
+ memset(q, 0, ((char *)&q->slib) - ((char *)q));
+ memset(q->slib, 0, PAGE_SIZE);
+
+ q->irq_ptr = irq_ptr;
+ q->mask = 1 << (31 - i);
+ q->nr = i;
+ q->handler = handler;
+}
+
+static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
+ void **sbals_array, char *dbf_text, int i)
+{
+ struct qdio_q *prev;
+ int j;
+
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ QDIO_DBF_HEX0(0, setup, &q, sizeof(void *));
+
+ q->sl = (struct sl *)((char *)q->slib + PAGE_SIZE / 2);
+
+ /* fill in sbal */
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) {
+ q->sbal[j] = *sbals_array++;
+ WARN_ON((unsigned long)q->sbal[j] & 0xff);
+ }
+
+ /* fill in slib */
+ if (i > 0) {
+ prev = (q->is_input_q) ? irq_ptr->input_qs[i - 1]
+ : irq_ptr->output_qs[i - 1];
+ prev->slib->nsliba = (unsigned long)q->slib;
+ }
+
+ q->slib->sla = (unsigned long)q->sl;
+ q->slib->slsba = (unsigned long)&q->slsb.val[0];
+
+ /* fill in sl */
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
+ q->sl->element[j].sbal = (unsigned long)q->sbal[j];
+
+ QDIO_DBF_TEXT2(0, setup, "sl-sb-b0");
+ QDIO_DBF_HEX2(0, setup, q->sl, sizeof(void *));
+ QDIO_DBF_HEX2(0, setup, &q->slsb, sizeof(void *));
+ QDIO_DBF_HEX2(0, setup, q->sbal, sizeof(void *));
+}
+
+static void setup_queues(struct qdio_irq *irq_ptr,
+ struct qdio_initialize *qdio_init)
+{
+ char dbf_text[20];
+ struct qdio_q *q;
+ void **input_sbal_array = qdio_init->input_sbal_addr_array;
+ void **output_sbal_array = qdio_init->output_sbal_addr_array;
+ int i;
+
+ sprintf(dbf_text, "qset%4x", qdio_init->cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
+ for_each_input_queue(irq_ptr, q, i) {
+ sprintf(dbf_text, "in-q%4x", i);
+ setup_queues_misc(q, irq_ptr, qdio_init->input_handler, i);
+
+ q->is_input_q = 1;
+ spin_lock_init(&q->u.in.lock);
+ setup_storage_lists(q, irq_ptr, input_sbal_array, dbf_text, i);
+ input_sbal_array += QDIO_MAX_BUFFERS_PER_Q;
+
+ if (is_thinint_irq(irq_ptr))
+ tasklet_init(&q->tasklet, tiqdio_inbound_processing,
+ (unsigned long) q);
+ else
+ tasklet_init(&q->tasklet, qdio_inbound_processing,
+ (unsigned long) q);
+ }
+
+ for_each_output_queue(irq_ptr, q, i) {
+ sprintf(dbf_text, "outq%4x", i);
+ setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i);
+
+ q->is_input_q = 0;
+ setup_storage_lists(q, irq_ptr, output_sbal_array,
+ dbf_text, i);
+ output_sbal_array += QDIO_MAX_BUFFERS_PER_Q;
+
+ tasklet_init(&q->tasklet, qdio_outbound_processing,
+ (unsigned long) q);
+ setup_timer(&q->u.out.timer, (void(*)(unsigned long))
+ &qdio_outbound_timer, (unsigned long)q);
+ }
+}
+
+static void process_ac_flags(struct qdio_irq *irq_ptr, unsigned char qdioac)
+{
+ if (qdioac & AC1_SIGA_INPUT_NEEDED)
+ irq_ptr->siga_flag.input = 1;
+ if (qdioac & AC1_SIGA_OUTPUT_NEEDED)
+ 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;
+}
+
+static void check_and_setup_qebsm(struct qdio_irq *irq_ptr,
+ unsigned char qdioac, unsigned long token)
+{
+ char dbf_text[15];
+
+ if (!(irq_ptr->qib.rflags & QIB_RFLAGS_ENABLE_QEBSM))
+ goto no_qebsm;
+ if (!(qdioac & AC1_SC_QEBSM_AVAILABLE) ||
+ (!(qdioac & AC1_SC_QEBSM_ENABLED)))
+ goto no_qebsm;
+
+ irq_ptr->sch_token = token;
+
+ QDIO_DBF_TEXT0(0, setup, "V=V:1");
+ sprintf(dbf_text, "%8lx", irq_ptr->sch_token);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ return;
+
+no_qebsm:
+ irq_ptr->sch_token = 0;
+ irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM;
+ QDIO_DBF_TEXT0(0, setup, "noV=V");
+}
+
+static int __get_ssqd_info(struct qdio_irq *irq_ptr)
+{
+ struct chsc_ssqd_area *ssqd;
+ int rc;
+
+ QDIO_DBF_TEXT0(0, setup, "getssqd");
+ ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
+ memset(ssqd, 0, PAGE_SIZE);
+
+ ssqd->request = (struct chsc_header) {
+ .length = 0x0010,
+ .code = 0x0024,
+ };
+ ssqd->first_sch = irq_ptr->schid.sch_no;
+ ssqd->last_sch = irq_ptr->schid.sch_no;
+ ssqd->ssid = irq_ptr->schid.ssid;
+
+ if (chsc(ssqd))
+ return -EIO;
+ rc = chsc_error_from_response(ssqd->response.code);
+ if (rc)
+ return rc;
+
+ if (!(ssqd->qdio_ssqd.flags & CHSC_FLAG_QDIO_CAPABILITY) ||
+ !(ssqd->qdio_ssqd.flags & CHSC_FLAG_VALIDITY) ||
+ (ssqd->qdio_ssqd.sch != irq_ptr->schid.sch_no))
+ return -EINVAL;
+
+ memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd,
+ sizeof(struct qdio_ssqd_desc));
+ return 0;
+}
+
+void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
+{
+ unsigned char qdioac;
+ char dbf_text[15];
+ int rc;
+
+ rc = __get_ssqd_info(irq_ptr);
+ if (rc) {
+ QDIO_DBF_TEXT2(0, setup, "ssqdasig");
+ sprintf(dbf_text, "schn%4x", irq_ptr->schid.sch_no);
+ QDIO_DBF_TEXT2(0, setup, dbf_text);
+ sprintf(dbf_text, "rc:%d", rc);
+ QDIO_DBF_TEXT2(0, setup, dbf_text);
+ /* all flags set, worst case */
+ qdioac = AC1_SIGA_INPUT_NEEDED | AC1_SIGA_OUTPUT_NEEDED |
+ AC1_SIGA_SYNC_NEEDED;
+ } else
+ qdioac = irq_ptr->ssqd_desc.qdioac1;
+
+ check_and_setup_qebsm(irq_ptr, qdioac, irq_ptr->ssqd_desc.sch_token);
+ process_ac_flags(irq_ptr, qdioac);
+
+ sprintf(dbf_text, "qdioac%2x", qdioac);
+ QDIO_DBF_TEXT2(0, setup, dbf_text);
+}
+
+void qdio_release_memory(struct qdio_irq *irq_ptr)
+{
+ struct qdio_q *q;
+ int i;
+
+ /*
+ * Must check queue array manually since irq_ptr->nr_input_queues /
+ * irq_ptr->nr_input_queues may not yet be set.
+ */
+ for (i = 0; i < QDIO_MAX_QUEUES_PER_IRQ; i++) {
+ q = irq_ptr->input_qs[i];
+ if (q) {
+ free_page((unsigned long) q->slib);
+ kmem_cache_free(qdio_q_cache, q);
+ }
+ }
+ for (i = 0; i < QDIO_MAX_QUEUES_PER_IRQ; i++) {
+ q = irq_ptr->output_qs[i];
+ if (q) {
+ free_page((unsigned long) q->slib);
+ kmem_cache_free(qdio_q_cache, q);
+ }
+ }
+ free_page((unsigned long) irq_ptr->qdr);
+ free_page(irq_ptr->chsc_page);
+ free_page((unsigned long) irq_ptr);
+}
+
+static void __qdio_allocate_fill_qdr(struct qdio_irq *irq_ptr,
+ struct qdio_q **irq_ptr_qs,
+ int i, int nr)
+{
+ irq_ptr->qdr->qdf0[i + nr].sliba =
+ (unsigned long)irq_ptr_qs[i]->slib;
+
+ irq_ptr->qdr->qdf0[i + nr].sla =
+ (unsigned long)irq_ptr_qs[i]->sl;
+
+ irq_ptr->qdr->qdf0[i + nr].slsba =
+ (unsigned long)&irq_ptr_qs[i]->slsb.val[0];
+
+ irq_ptr->qdr->qdf0[i + nr].akey = PAGE_DEFAULT_KEY;
+ irq_ptr->qdr->qdf0[i + nr].bkey = PAGE_DEFAULT_KEY;
+ irq_ptr->qdr->qdf0[i + nr].ckey = PAGE_DEFAULT_KEY;
+ irq_ptr->qdr->qdf0[i + nr].dkey = PAGE_DEFAULT_KEY;
+}
+
+static void setup_qdr(struct qdio_irq *irq_ptr,
+ struct qdio_initialize *qdio_init)
+{
+ int i;
+
+ irq_ptr->qdr->qfmt = qdio_init->q_format;
+ irq_ptr->qdr->iqdcnt = qdio_init->no_input_qs;
+ irq_ptr->qdr->oqdcnt = qdio_init->no_output_qs;
+ irq_ptr->qdr->iqdsz = sizeof(struct qdesfmt0) / 4; /* size in words */
+ irq_ptr->qdr->oqdsz = sizeof(struct qdesfmt0) / 4;
+ irq_ptr->qdr->qiba = (unsigned long)&irq_ptr->qib;
+ irq_ptr->qdr->qkey = PAGE_DEFAULT_KEY;
+
+ for (i = 0; i < qdio_init->no_input_qs; i++)
+ __qdio_allocate_fill_qdr(irq_ptr, irq_ptr->input_qs, i, 0);
+
+ for (i = 0; i < qdio_init->no_output_qs; i++)
+ __qdio_allocate_fill_qdr(irq_ptr, irq_ptr->output_qs, i,
+ qdio_init->no_input_qs);
+}
+
+static void setup_qib(struct qdio_irq *irq_ptr,
+ struct qdio_initialize *init_data)
+{
+ if (qebsm_possible())
+ irq_ptr->qib.rflags |= QIB_RFLAGS_ENABLE_QEBSM;
+
+ irq_ptr->qib.qfmt = init_data->q_format;
+ if (init_data->no_input_qs)
+ irq_ptr->qib.isliba =
+ (unsigned long)(irq_ptr->input_qs[0]->slib);
+ if (init_data->no_output_qs)
+ irq_ptr->qib.osliba =
+ (unsigned long)(irq_ptr->output_qs[0]->slib);
+ memcpy(irq_ptr->qib.ebcnam, init_data->adapter_name, 8);
+}
+
+int qdio_setup_irq(struct qdio_initialize *init_data)
+{
+ struct ciw *ciw;
+ struct qdio_irq *irq_ptr = init_data->cdev->private->qdio_data;
+ int rc;
+
+ memset(irq_ptr, 0, ((char *)&irq_ptr->qdr) - ((char *)irq_ptr));
+ /* wipes qib.ac, required by ar7063 */
+ memset(irq_ptr->qdr, 0, sizeof(struct qdr));
+
+ irq_ptr->int_parm = init_data->int_parm;
+ irq_ptr->nr_input_qs = init_data->no_input_qs;
+ irq_ptr->nr_output_qs = init_data->no_output_qs;
+
+ irq_ptr->schid = ccw_device_get_subchannel_id(init_data->cdev);
+ irq_ptr->cdev = init_data->cdev;
+ setup_queues(irq_ptr, init_data);
+
+ setup_qib(irq_ptr, init_data);
+ qdio_setup_thinint(irq_ptr);
+ set_impl_params(irq_ptr, init_data->qib_param_field_format,
+ init_data->qib_param_field,
+ init_data->input_slib_elements,
+ init_data->output_slib_elements);
+
+ /* fill input and output descriptors */
+ setup_qdr(irq_ptr, init_data);
+
+ /* qdr, qib, sls, slsbs, slibs, sbales are filled now */
+
+ /* get qdio commands */
+ ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_EQUEUE);
+ if (!ciw) {
+ QDIO_DBF_TEXT2(1, setup, "no eq");
+ rc = -EINVAL;
+ goto out_err;
+ }
+ irq_ptr->equeue = *ciw;
+
+ ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_AQUEUE);
+ if (!ciw) {
+ QDIO_DBF_TEXT2(1, setup, "no aq");
+ rc = -EINVAL;
+ goto out_err;
+ }
+ irq_ptr->aqueue = *ciw;
+
+ /* set new interrupt handler */
+ irq_ptr->orig_handler = init_data->cdev->handler;
+ init_data->cdev->handler = qdio_int_handler;
+ return 0;
+out_err:
+ qdio_release_memory(irq_ptr);
+ return rc;
+}
+
+void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
+ struct ccw_device *cdev)
+{
+ char s[80];
+
+ sprintf(s, "qdio: %s ", dev_name(&cdev->dev));
+ switch (irq_ptr->qib.qfmt) {
+ case QDIO_QETH_QFMT:
+ sprintf(s + strlen(s), "OSA ");
+ break;
+ case QDIO_ZFCP_QFMT:
+ sprintf(s + strlen(s), "ZFCP ");
+ break;
+ case QDIO_IQDIO_QFMT:
+ sprintf(s + strlen(s), "HS ");
+ break;
+ }
+ sprintf(s + strlen(s), "on SC %x using ", irq_ptr->schid.sch_no);
+ sprintf(s + strlen(s), "AI:%d ", is_thinint_irq(irq_ptr));
+ sprintf(s + strlen(s), "QEBSM:%d ", (irq_ptr->sch_token) ? 1 : 0);
+ sprintf(s + strlen(s), "PCI:%d ",
+ (irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) ? 1 : 0);
+ sprintf(s + strlen(s), "TDD:%d ", css_general_characteristics.aif_tdd);
+ sprintf(s + strlen(s), "SIGA:");
+ sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.input) ? "R" : " ");
+ sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.output) ? "W" : " ");
+ sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.sync) ? "S" : " ");
+ sprintf(s + strlen(s), "%s",
+ (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " ");
+ sprintf(s + strlen(s), "%s",
+ (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " ");
+ sprintf(s + strlen(s), "%s",
+ (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " ");
+ sprintf(s + strlen(s), "\n");
+ printk(KERN_INFO "%s", s);
+}
+
+int __init qdio_setup_init(void)
+{
+ char dbf_text[15];
+
+ qdio_q_cache = kmem_cache_create("qdio_q", sizeof(struct qdio_q),
+ 256, 0, NULL);
+ if (!qdio_q_cache)
+ return -ENOMEM;
+
+ /* Check for OSA/FCP thin interrupts (bit 67). */
+ sprintf(dbf_text, "thini%1x",
+ (css_general_characteristics.aif_osa) ? 1 : 0);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
+ /* Check for QEBSM support in general (bit 58). */
+ sprintf(dbf_text, "cssQBS:%1x",
+ (qebsm_possible()) ? 1 : 0);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ return 0;
+}
+
+void qdio_setup_exit(void)
+{
+ kmem_cache_destroy(qdio_q_cache);
+}
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
new file mode 100644
index 00000000000..ea7f6140026
--- /dev/null
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -0,0 +1,384 @@
+/*
+ * linux/drivers/s390/cio/thinint_qdio.c
+ *
+ * thin interrupt support for qdio
+ *
+ * Copyright 2000-2008 IBM Corp.
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ * Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Jan Glauber <jang@linux.vnet.ibm.com>
+ */
+#include <linux/io.h>
+#include <asm/atomic.h>
+#include <asm/debug.h>
+#include <asm/qdio.h>
+#include <asm/airq.h>
+#include <asm/isc.h>
+
+#include "cio.h"
+#include "ioasm.h"
+#include "qdio.h"
+#include "qdio_debug.h"
+#include "qdio_perf.h"
+
+/*
+ * Restriction: only 63 iqdio subchannels would have its own indicator,
+ * after that, subsequent subchannels share one indicator
+ */
+#define TIQDIO_NR_NONSHARED_IND 63
+#define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1)
+#define TIQDIO_SHARED_IND 63
+
+/* list of thin interrupt input queues */
+static LIST_HEAD(tiq_list);
+
+/* adapter local summary indicator */
+static unsigned char *tiqdio_alsi;
+
+/* device state change indicators */
+struct indicator_t {
+ u32 ind; /* u32 because of compare-and-swap performance */
+ atomic_t count; /* use count, 0 or 1 for non-shared indicators */
+};
+static struct indicator_t *q_indicators;
+
+static void tiqdio_tasklet_fn(unsigned long data);
+static DECLARE_TASKLET(tiqdio_tasklet, tiqdio_tasklet_fn, 0);
+
+static int css_qdio_omit_svs;
+
+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)
+{
+ int i;
+
+ for (i = 0; i < TIQDIO_NR_NONSHARED_IND; i++)
+ if (!atomic_read(&q_indicators[i].count)) {
+ atomic_set(&q_indicators[i].count, 1);
+ return &q_indicators[i].ind;
+ }
+
+ /* use the shared indicator */
+ atomic_inc(&q_indicators[TIQDIO_SHARED_IND].count);
+ return &q_indicators[TIQDIO_SHARED_IND].ind;
+}
+
+static void put_indicator(u32 *addr)
+{
+ int i;
+
+ if (!addr)
+ return;
+ i = ((unsigned long)addr - (unsigned long)q_indicators) /
+ sizeof(struct indicator_t);
+ atomic_dec(&q_indicators[i].count);
+}
+
+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;
+
+ for_each_input_queue(irq_ptr, q, i) {
+ list_add_rcu(&q->entry, &tiq_list);
+ synchronize_rcu();
+ }
+ xchg(irq_ptr->dsci, 1);
+ tasklet_schedule(&tiqdio_tasklet);
+}
+
+/*
+ * we cannot stop the tiqdio tasklet here since it is for all
+ * thinint qdio devices and it must run as long as there is a
+ * thinint device left
+ */
+void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
+{
+ struct qdio_q *q;
+ int i;
+
+ for (i = 0; i < irq_ptr->nr_input_qs; i++) {
+ q = irq_ptr->input_qs[i];
+ /* if establish triggered an error */
+ if (!q || !q->entry.prev || !q->entry.next)
+ continue;
+ list_del_rcu(&q->entry);
+ synchronize_rcu();
+ }
+}
+
+static inline int tiqdio_inbound_q_done(struct qdio_q *q)
+{
+ unsigned char state;
+
+ if (!atomic_read(&q->nr_buf_used))
+ return 1;
+
+ qdio_siga_sync_q(q);
+ get_buf_state(q, q->first_to_check, &state);
+
+ if (state == SLSB_P_INPUT_PRIMED)
+ /* more work coming */
+ return 0;
+ return 1;
+}
+
+static inline int shared_ind(struct qdio_irq *irq_ptr)
+{
+ return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
+}
+
+static void __tiqdio_inbound_processing(struct qdio_q *q)
+{
+ qdio_perf_stat_inc(&perf_stats.thinint_inbound);
+ qdio_sync_after_thinint(q);
+
+ /*
+ * Maybe we have work on our outbound queues... at least
+ * we have to check the PCI capable queues.
+ */
+ qdio_check_outbound_after_thinint(q);
+
+again:
+ if (!qdio_inbound_q_moved(q))
+ return;
+
+ qdio_kick_inbound_handler(q);
+
+ if (!tiqdio_inbound_q_done(q)) {
+ qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop);
+ goto again;
+ }
+
+ qdio_stop_polling(q);
+ /*
+ * We need to check again to not lose initiative after
+ * resetting the ACK state.
+ */
+ if (!tiqdio_inbound_q_done(q)) {
+ qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop2);
+ goto again;
+ }
+}
+
+void tiqdio_inbound_processing(unsigned long data)
+{
+ struct qdio_q *q = (struct qdio_q *)data;
+
+ __tiqdio_inbound_processing(q);
+}
+
+/* check for work on all inbound thinint queues */
+static void tiqdio_tasklet_fn(unsigned long data)
+{
+ struct qdio_q *q;
+
+ qdio_perf_stat_inc(&perf_stats.tasklet_thinint);
+again:
+
+ /* protect tiq_list entries, only changed in activate or shutdown */
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(q, &tiq_list, entry)
+ /* only process queues from changed sets */
+ if (*q->irq_ptr->dsci) {
+
+ /* only clear it if the indicator is non-shared */
+ if (!shared_ind(q->irq_ptr))
+ xchg(q->irq_ptr->dsci, 0);
+ /*
+ * don't call inbound processing directly since
+ * that could starve other thinint queues
+ */
+ tasklet_schedule(&q->tasklet);
+ }
+
+ rcu_read_unlock();
+
+ /*
+ * if we used the shared indicator clear it now after all queues
+ * were processed
+ */
+ if (atomic_read(&q_indicators[TIQDIO_SHARED_IND].count)) {
+ xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
+
+ /* prevent racing */
+ if (*tiqdio_alsi)
+ xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1);
+ }
+
+ /* check for more work */
+ if (*tiqdio_alsi) {
+ xchg(tiqdio_alsi, 0);
+ qdio_perf_stat_inc(&perf_stats.tasklet_thinint_loop);
+ goto again;
+ }
+}
+
+/**
+ * tiqdio_thinint_handler - thin interrupt handler for qdio
+ * @ind: pointer to adapter local summary indicator
+ * @drv_data: NULL
+ */
+static void tiqdio_thinint_handler(void *ind, void *drv_data)
+{
+ qdio_perf_stat_inc(&perf_stats.thin_int);
+
+ /*
+ * 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 (tiqdio_alsi) to stop adapter
+ * interrupts for now, the tasklet will clean all dsci's
+ */
+ xchg((u8 *)ind, 0);
+ tasklet_hi_schedule(&tiqdio_tasklet);
+}
+
+static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
+{
+ struct scssc_area *scssc_area;
+ char dbf_text[15];
+ void *ptr;
+ int rc;
+
+ scssc_area = (struct scssc_area *)irq_ptr->chsc_page;
+ memset(scssc_area, 0, PAGE_SIZE);
+
+ if (reset) {
+ scssc_area->summary_indicator_addr = 0;
+ scssc_area->subchannel_indicator_addr = 0;
+ } else {
+ scssc_area->summary_indicator_addr = virt_to_phys(tiqdio_alsi);
+ scssc_area->subchannel_indicator_addr =
+ virt_to_phys(irq_ptr->dsci);
+ }
+
+ scssc_area->request = (struct chsc_header) {
+ .length = 0x0fe0,
+ .code = 0x0021,
+ };
+ scssc_area->operation_code = 0;
+ scssc_area->ks = PAGE_DEFAULT_KEY;
+ scssc_area->kc = PAGE_DEFAULT_KEY;
+ scssc_area->isc = QDIO_AIRQ_ISC;
+ scssc_area->schid = irq_ptr->schid;
+
+ /* enable the time delay disablement facility */
+ if (css_general_characteristics.aif_tdd)
+ scssc_area->word_with_d_bit = 0x10000000;
+
+ rc = chsc(scssc_area);
+ if (rc)
+ return -EIO;
+
+ rc = chsc_error_from_response(scssc_area->response.code);
+ if (rc) {
+ sprintf(dbf_text, "sidR%4x", scssc_area->response.code);
+ QDIO_DBF_TEXT1(0, trace, dbf_text);
+ QDIO_DBF_TEXT1(0, setup, dbf_text);
+ ptr = &scssc_area->response;
+ QDIO_DBF_HEX2(1, setup, &ptr, QDIO_DBF_SETUP_LEN);
+ return rc;
+ }
+
+ QDIO_DBF_TEXT2(0, setup, "setscind");
+ QDIO_DBF_HEX2(0, setup, &scssc_area->summary_indicator_addr,
+ sizeof(unsigned long));
+ QDIO_DBF_HEX2(0, setup, &scssc_area->subchannel_indicator_addr,
+ sizeof(unsigned long));
+ return 0;
+}
+
+/* allocate non-shared indicators and shared indicator */
+int __init tiqdio_allocate_memory(void)
+{
+ q_indicators = kzalloc(sizeof(struct indicator_t) * TIQDIO_NR_INDICATORS,
+ GFP_KERNEL);
+ if (!q_indicators)
+ return -ENOMEM;
+ return 0;
+}
+
+void tiqdio_free_memory(void)
+{
+ kfree(q_indicators);
+}
+
+int __init tiqdio_register_thinints(void)
+{
+ char dbf_text[20];
+
+ isc_register(QDIO_AIRQ_ISC);
+ tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler,
+ NULL, QDIO_AIRQ_ISC);
+ if (IS_ERR(tiqdio_alsi)) {
+ sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_alsi));
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ tiqdio_alsi = NULL;
+ isc_unregister(QDIO_AIRQ_ISC);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+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);
+}
+
+void qdio_setup_thinint(struct qdio_irq *irq_ptr)
+{
+ if (!is_thinint_irq(irq_ptr))
+ return;
+ irq_ptr->dsci = get_indicator();
+ QDIO_DBF_HEX1(0, setup, &irq_ptr->dsci, sizeof(void *));
+}
+
+void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
+{
+ if (!is_thinint_irq(irq_ptr))
+ return;
+
+ /* reset adapter interrupt indicators */
+ put_indicator(irq_ptr->dsci);
+ set_subchannel_ind(irq_ptr, 1);
+}
+
+void __exit tiqdio_unregister_thinints(void)
+{
+ tasklet_disable(&tiqdio_tasklet);
+
+ if (tiqdio_alsi) {
+ s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC);
+ isc_unregister(QDIO_AIRQ_ISC);
+ }
+}
diff --git a/drivers/s390/cio/schid.h b/drivers/s390/cio/schid.h
deleted file mode 100644
index 54328fec5ad..00000000000
--- a/drivers/s390/cio/schid.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef S390_SCHID_H
-#define S390_SCHID_H
-
-struct subchannel_id {
- __u32 reserved:13;
- __u32 ssid:2;
- __u32 one:1;
- __u32 sch_no:16;
-} __attribute__ ((packed,aligned(4)));
-
-
-/* Helper function for sane state of pre-allocated subchannel_id. */
-static inline void
-init_subchannel_id(struct subchannel_id *schid)
-{
- memset(schid, 0, sizeof(struct subchannel_id));
- schid->one = 1;
-}
-
-static inline int
-schid_equal(struct subchannel_id *schid1, struct subchannel_id *schid2)
-{
- return !memcmp(schid1, schid2, sizeof(struct subchannel_id));
-}
-
-#endif /* S390_SCHID_H */
diff --git a/drivers/s390/cio/scsw.c b/drivers/s390/cio/scsw.c
new file mode 100644
index 00000000000..f8da25ab576
--- /dev/null
+++ b/drivers/s390/cio/scsw.c
@@ -0,0 +1,843 @@
+/*
+ * Helper functions for scsw access.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <asm/cio.h>
+#include "css.h"
+#include "chsc.h"
+
+/**
+ * scsw_is_tm - check for transport mode scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the specified scsw is a transport mode scsw, zero
+ * otherwise.
+ */
+int scsw_is_tm(union scsw *scsw)
+{
+ return css_general_characteristics.fcx && (scsw->tm.x == 1);
+}
+EXPORT_SYMBOL(scsw_is_tm);
+
+/**
+ * scsw_key - return scsw key field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the key field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_key(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.key;
+ else
+ return scsw->cmd.key;
+}
+EXPORT_SYMBOL(scsw_key);
+
+/**
+ * scsw_eswf - return scsw eswf field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the eswf field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_eswf(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.eswf;
+ else
+ return scsw->cmd.eswf;
+}
+EXPORT_SYMBOL(scsw_eswf);
+
+/**
+ * scsw_cc - return scsw cc field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the cc field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_cc(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.cc;
+ else
+ return scsw->cmd.cc;
+}
+EXPORT_SYMBOL(scsw_cc);
+
+/**
+ * scsw_ectl - return scsw ectl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the ectl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_ectl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.ectl;
+ else
+ return scsw->cmd.ectl;
+}
+EXPORT_SYMBOL(scsw_ectl);
+
+/**
+ * scsw_pno - return scsw pno field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the pno field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_pno(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.pno;
+ else
+ return scsw->cmd.pno;
+}
+EXPORT_SYMBOL(scsw_pno);
+
+/**
+ * scsw_fctl - return scsw fctl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the fctl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_fctl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.fctl;
+ else
+ return scsw->cmd.fctl;
+}
+EXPORT_SYMBOL(scsw_fctl);
+
+/**
+ * scsw_actl - return scsw actl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the actl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_actl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.actl;
+ else
+ return scsw->cmd.actl;
+}
+EXPORT_SYMBOL(scsw_actl);
+
+/**
+ * scsw_stctl - return scsw stctl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the stctl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_stctl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.stctl;
+ else
+ return scsw->cmd.stctl;
+}
+EXPORT_SYMBOL(scsw_stctl);
+
+/**
+ * scsw_dstat - return scsw dstat field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the dstat field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_dstat(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.dstat;
+ else
+ return scsw->cmd.dstat;
+}
+EXPORT_SYMBOL(scsw_dstat);
+
+/**
+ * scsw_cstat - return scsw cstat field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the cstat field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_cstat(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.cstat;
+ else
+ return scsw->cmd.cstat;
+}
+EXPORT_SYMBOL(scsw_cstat);
+
+/**
+ * scsw_cmd_is_valid_key - check key field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the key field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_key(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_key);
+
+/**
+ * scsw_cmd_is_valid_sctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_sctl(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_sctl);
+
+/**
+ * scsw_cmd_is_valid_eswf - check eswf field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the eswf field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_eswf(union scsw *scsw)
+{
+ return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_eswf);
+
+/**
+ * scsw_cmd_is_valid_cc - check cc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cc field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_cc(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) &&
+ (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_cc);
+
+/**
+ * scsw_cmd_is_valid_fmt - check fmt field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fmt field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_fmt(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_fmt);
+
+/**
+ * scsw_cmd_is_valid_pfch - check pfch field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pfch field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_pfch(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_pfch);
+
+/**
+ * scsw_cmd_is_valid_isic - check isic field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the isic field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_isic(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_isic);
+
+/**
+ * scsw_cmd_is_valid_alcc - check alcc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the alcc field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_alcc(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_alcc);
+
+/**
+ * scsw_cmd_is_valid_ssi - check ssi field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ssi field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_ssi(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_ssi);
+
+/**
+ * scsw_cmd_is_valid_zcc - check zcc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the zcc field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_zcc(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) &&
+ (scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_zcc);
+
+/**
+ * scsw_cmd_is_valid_ectl - check ectl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ectl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_ectl(union scsw *scsw)
+{
+ return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+ !(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) &&
+ (scsw->cmd.stctl & SCSW_STCTL_ALERT_STATUS);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_ectl);
+
+/**
+ * scsw_cmd_is_valid_pno - check pno field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pno field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_pno(union scsw *scsw)
+{
+ return (scsw->cmd.fctl != 0) &&
+ (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (!(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) ||
+ ((scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) &&
+ (scsw->cmd.actl & SCSW_ACTL_SUSPENDED)));
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_pno);
+
+/**
+ * scsw_cmd_is_valid_fctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_fctl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_fctl);
+
+/**
+ * scsw_cmd_is_valid_actl - check actl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the actl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_actl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_actl);
+
+/**
+ * scsw_cmd_is_valid_stctl - check stctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the stctl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_stctl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_stctl);
+
+/**
+ * scsw_cmd_is_valid_dstat - check dstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the dstat field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_dstat(union scsw *scsw)
+{
+ return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (scsw->cmd.cc != 3);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_dstat);
+
+/**
+ * scsw_cmd_is_valid_cstat - check cstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cstat field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_cstat(union scsw *scsw)
+{
+ return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (scsw->cmd.cc != 3);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_cstat);
+
+/**
+ * scsw_tm_is_valid_key - check key field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the key field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_key(union scsw *scsw)
+{
+ return (scsw->tm.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_key);
+
+/**
+ * scsw_tm_is_valid_eswf - check eswf field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the eswf field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_eswf(union scsw *scsw)
+{
+ return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_eswf);
+
+/**
+ * scsw_tm_is_valid_cc - check cc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cc field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_cc(union scsw *scsw)
+{
+ return (scsw->tm.fctl & SCSW_FCTL_START_FUNC) &&
+ (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_cc);
+
+/**
+ * scsw_tm_is_valid_fmt - check fmt field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fmt field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_fmt(union scsw *scsw)
+{
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_fmt);
+
+/**
+ * scsw_tm_is_valid_x - check x field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the x field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_x(union scsw *scsw)
+{
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_x);
+
+/**
+ * scsw_tm_is_valid_q - check q field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the q field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_q(union scsw *scsw)
+{
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_q);
+
+/**
+ * scsw_tm_is_valid_ectl - check ectl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ectl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_ectl(union scsw *scsw)
+{
+ return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+ !(scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) &&
+ (scsw->tm.stctl & SCSW_STCTL_ALERT_STATUS);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_ectl);
+
+/**
+ * scsw_tm_is_valid_pno - check pno field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pno field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_pno(union scsw *scsw)
+{
+ return (scsw->tm.fctl != 0) &&
+ (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (!(scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) ||
+ ((scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) &&
+ (scsw->tm.actl & SCSW_ACTL_SUSPENDED)));
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_pno);
+
+/**
+ * scsw_tm_is_valid_fctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_fctl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_fctl);
+
+/**
+ * scsw_tm_is_valid_actl - check actl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the actl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_actl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_actl);
+
+/**
+ * scsw_tm_is_valid_stctl - check stctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the stctl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_stctl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_stctl);
+
+/**
+ * scsw_tm_is_valid_dstat - check dstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the dstat field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_dstat(union scsw *scsw)
+{
+ return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (scsw->tm.cc != 3);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_dstat);
+
+/**
+ * scsw_tm_is_valid_cstat - check cstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cstat field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_cstat(union scsw *scsw)
+{
+ return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (scsw->tm.cc != 3);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_cstat);
+
+/**
+ * scsw_tm_is_valid_fcxs - check fcxs field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fcxs field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_fcxs(union scsw *scsw)
+{
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_fcxs);
+
+/**
+ * scsw_tm_is_valid_schxs - check schxs field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the schxs field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_schxs(union scsw *scsw)
+{
+ return (scsw->tm.cstat & (SCHN_STAT_PROG_CHECK |
+ SCHN_STAT_INTF_CTRL_CHK |
+ SCHN_STAT_PROT_CHECK |
+ SCHN_STAT_CHN_DATA_CHK));
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_schxs);
+
+/**
+ * scsw_is_valid_actl - check actl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the actl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_actl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_actl(scsw);
+ else
+ return scsw_cmd_is_valid_actl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_actl);
+
+/**
+ * scsw_is_valid_cc - check cc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cc field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_cc(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_cc(scsw);
+ else
+ return scsw_cmd_is_valid_cc(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_cc);
+
+/**
+ * scsw_is_valid_cstat - check cstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cstat field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_cstat(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_cstat(scsw);
+ else
+ return scsw_cmd_is_valid_cstat(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_cstat);
+
+/**
+ * scsw_is_valid_dstat - check dstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the dstat field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_dstat(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_dstat(scsw);
+ else
+ return scsw_cmd_is_valid_dstat(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_dstat);
+
+/**
+ * scsw_is_valid_ectl - check ectl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ectl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_ectl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_ectl(scsw);
+ else
+ return scsw_cmd_is_valid_ectl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_ectl);
+
+/**
+ * scsw_is_valid_eswf - check eswf field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the eswf field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_eswf(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_eswf(scsw);
+ else
+ return scsw_cmd_is_valid_eswf(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_eswf);
+
+/**
+ * scsw_is_valid_fctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_fctl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_fctl(scsw);
+ else
+ return scsw_cmd_is_valid_fctl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_fctl);
+
+/**
+ * scsw_is_valid_key - check key field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the key field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_key(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_key(scsw);
+ else
+ return scsw_cmd_is_valid_key(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_key);
+
+/**
+ * scsw_is_valid_pno - check pno field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pno field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_pno(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_pno(scsw);
+ else
+ return scsw_cmd_is_valid_pno(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_pno);
+
+/**
+ * scsw_is_valid_stctl - check stctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the stctl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_stctl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_stctl(scsw);
+ else
+ return scsw_cmd_is_valid_stctl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_stctl);
+
+/**
+ * scsw_cmd_is_solicited - check for solicited scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the command mode scsw indicates that the associated
+ * status condition is solicited, zero if it is unsolicited.
+ */
+int scsw_cmd_is_solicited(union scsw *scsw)
+{
+ return (scsw->cmd.cc != 0) || (scsw->cmd.stctl !=
+ (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS));
+}
+EXPORT_SYMBOL(scsw_cmd_is_solicited);
+
+/**
+ * scsw_tm_is_solicited - check for solicited scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the transport mode scsw indicates that the associated
+ * status condition is solicited, zero if it is unsolicited.
+ */
+int scsw_tm_is_solicited(union scsw *scsw)
+{
+ return (scsw->tm.cc != 0) || (scsw->tm.stctl !=
+ (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS));
+}
+EXPORT_SYMBOL(scsw_tm_is_solicited);
+
+/**
+ * scsw_is_solicited - check for solicited scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the transport or command mode scsw indicates that the
+ * associated status condition is solicited, zero if it is unsolicited.
+ */
+int scsw_is_solicited(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_solicited(scsw);
+ else
+ return scsw_cmd_is_solicited(scsw);
+}
+EXPORT_SYMBOL(scsw_is_solicited);
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index a1ab3e3efd1..326db1e827c 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -34,13 +34,15 @@
#include <linux/mutex.h>
#include <asm/s390_rdev.h>
#include <asm/reset.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
#include "ap_bus.h"
/* Some prototypes. */
static void ap_scan_bus(struct work_struct *);
static void ap_poll_all(unsigned long);
-static void ap_poll_timeout(unsigned long);
+static enum hrtimer_restart ap_poll_timeout(struct hrtimer *);
static int ap_poll_thread_start(void);
static void ap_poll_thread_stop(void);
static void ap_request_timeout(unsigned long);
@@ -80,12 +82,15 @@ static DECLARE_WORK(ap_config_work, ap_scan_bus);
/*
* Tasklet & timer for AP request polling.
*/
-static struct timer_list ap_poll_timer = TIMER_INITIALIZER(ap_poll_timeout,0,0);
static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0);
static atomic_t ap_poll_requests = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
static struct task_struct *ap_poll_kthread = NULL;
static DEFINE_MUTEX(ap_poll_thread_mutex);
+static struct hrtimer ap_poll_timer;
+/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
+ * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/
+static unsigned long long poll_timeout = 250000;
/**
* ap_intructions_available() - Test if AP instructions are available.
@@ -636,11 +641,39 @@ static ssize_t ap_poll_thread_store(struct bus_type *bus,
static BUS_ATTR(poll_thread, 0644, ap_poll_thread_show, ap_poll_thread_store);
+static ssize_t poll_timeout_show(struct bus_type *bus, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%llu\n", poll_timeout);
+}
+
+static ssize_t poll_timeout_store(struct bus_type *bus, const char *buf,
+ size_t count)
+{
+ unsigned long long time;
+ ktime_t hr_time;
+
+ /* 120 seconds = maximum poll interval */
+ if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || time > 120000000000)
+ return -EINVAL;
+ poll_timeout = time;
+ hr_time = ktime_set(0, poll_timeout);
+
+ if (!hrtimer_is_queued(&ap_poll_timer) ||
+ !hrtimer_forward(&ap_poll_timer, ap_poll_timer.expires, hr_time)) {
+ ap_poll_timer.expires = hr_time;
+ hrtimer_start(&ap_poll_timer, hr_time, HRTIMER_MODE_ABS);
+ }
+ return count;
+}
+
+static BUS_ATTR(poll_timeout, 0644, poll_timeout_show, poll_timeout_store);
+
static struct bus_attribute *const ap_bus_attrs[] = {
&bus_attr_ap_domain,
&bus_attr_config_time,
&bus_attr_poll_thread,
- NULL
+ &bus_attr_poll_timeout,
+ NULL,
};
/**
@@ -859,8 +892,8 @@ static void ap_scan_bus(struct work_struct *unused)
ap_dev->device.bus = &ap_bus_type;
ap_dev->device.parent = ap_root_device;
- snprintf(ap_dev->device.bus_id, BUS_ID_SIZE, "card%02x",
- AP_QID_DEVICE(ap_dev->qid));
+ dev_set_name(&ap_dev->device, "card%02x",
+ AP_QID_DEVICE(ap_dev->qid));
ap_dev->device.release = ap_device_release;
rc = device_register(&ap_dev->device);
if (rc) {
@@ -895,9 +928,10 @@ ap_config_timeout(unsigned long ptr)
*/
static inline void ap_schedule_poll_timer(void)
{
- if (timer_pending(&ap_poll_timer))
+ if (hrtimer_is_queued(&ap_poll_timer))
return;
- mod_timer(&ap_poll_timer, jiffies + AP_POLL_TIME);
+ hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout),
+ HRTIMER_MODE_ABS);
}
/**
@@ -1115,13 +1149,14 @@ EXPORT_SYMBOL(ap_cancel_message);
/**
* ap_poll_timeout(): AP receive polling for finished AP requests.
- * @unused: Unused variable.
+ * @unused: Unused pointer.
*
- * Schedules the AP tasklet.
+ * Schedules the AP tasklet using a high resolution timer.
*/
-static void ap_poll_timeout(unsigned long unused)
+static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
{
tasklet_schedule(&ap_tasklet);
+ return HRTIMER_NORESTART;
}
/**
@@ -1344,6 +1379,14 @@ int __init ap_module_init(void)
ap_config_timer.expires = jiffies + ap_config_time * HZ;
add_timer(&ap_config_timer);
+ /* Setup the high resultion poll timer.
+ * If we are running under z/VM adjust polling to z/VM polling rate.
+ */
+ if (MACHINE_IS_VM)
+ poll_timeout = 1500000;
+ hrtimer_init(&ap_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ ap_poll_timer.function = ap_poll_timeout;
+
/* Start the low priority AP bus poll thread. */
if (ap_thread_flag) {
rc = ap_poll_thread_start();
@@ -1355,7 +1398,7 @@ int __init ap_module_init(void)
out_work:
del_timer_sync(&ap_config_timer);
- del_timer_sync(&ap_poll_timer);
+ hrtimer_cancel(&ap_poll_timer);
destroy_workqueue(ap_work_queue);
out_root:
s390_root_dev_unregister(ap_root_device);
@@ -1386,7 +1429,7 @@ void ap_module_exit(void)
ap_reset_domain();
ap_poll_thread_stop();
del_timer_sync(&ap_config_timer);
- del_timer_sync(&ap_poll_timer);
+ hrtimer_cancel(&ap_poll_timer);
destroy_workqueue(ap_work_queue);
tasklet_kill(&ap_tasklet);
s390_root_dev_unregister(ap_root_device);
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index c1e1200c43f..446378b308f 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -92,6 +92,8 @@ struct ap_queue_status {
#define AP_DEVICE_TYPE_PCIXCC 5
#define AP_DEVICE_TYPE_CEX2A 6
#define AP_DEVICE_TYPE_CEX2C 7
+#define AP_DEVICE_TYPE_CEX2A2 8
+#define AP_DEVICE_TYPE_CEX2C2 9
/*
* AP reset flag states
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 4d36e805a23..cb22b97944b 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -34,6 +34,7 @@
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/compat.h>
+#include <linux/smp_lock.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <linux/hw_random.h>
@@ -300,7 +301,9 @@ static ssize_t zcrypt_write(struct file *filp, const char __user *buf,
*/
static int zcrypt_open(struct inode *inode, struct file *filp)
{
+ lock_kernel();
atomic_inc(&zcrypt_open_count);
+ unlock_kernel();
return 0;
}
@@ -1068,10 +1071,8 @@ static int zcrypt_status_write(struct file *file, const char __user *buffer,
#define LBUFSIZE 1200UL
lbuf = kmalloc(LBUFSIZE, GFP_KERNEL);
- if (!lbuf) {
- PRINTK("kmalloc failed!\n");
+ if (!lbuf)
return 0;
- }
local_count = min(LBUFSIZE - 1, count);
if (copy_from_user(lbuf, buffer, local_count) != 0) {
@@ -1081,23 +1082,15 @@ static int zcrypt_status_write(struct file *file, const char __user *buffer,
lbuf[local_count] = '\0';
ptr = strstr(lbuf, "Online devices");
- if (!ptr) {
- PRINTK("Unable to parse data (missing \"Online devices\")\n");
+ if (!ptr)
goto out;
- }
ptr = strstr(ptr, "\n");
- if (!ptr) {
- PRINTK("Unable to parse data (missing newline "
- "after \"Online devices\")\n");
+ if (!ptr)
goto out;
- }
ptr++;
- if (strstr(ptr, "Waiting work element counts") == NULL) {
- PRINTK("Unable to parse data (missing "
- "\"Waiting work element counts\")\n");
+ if (strstr(ptr, "Waiting work element counts") == NULL)
goto out;
- }
for (j = 0; j < 64 && *ptr; ptr++) {
/*
@@ -1197,16 +1190,12 @@ int __init zcrypt_api_init(void)
/* Register the request sprayer. */
rc = misc_register(&zcrypt_misc_device);
- if (rc < 0) {
- PRINTKW(KERN_ERR "misc_register (minor %d) failed with %d\n",
- zcrypt_misc_device.minor, rc);
+ if (rc < 0)
goto out;
- }
/* Set up the proc file system */
zcrypt_entry = create_proc_entry("driver/z90crypt", 0644, NULL);
if (!zcrypt_entry) {
- PRINTK("Couldn't create z90crypt proc entry\n");
rc = -ENOMEM;
goto out_misc;
}
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 5c6e222b2ac..1d1ec74dadb 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -30,34 +30,6 @@
#ifndef _ZCRYPT_API_H_
#define _ZCRYPT_API_H_
-/**
- * Macro definitions
- *
- * PDEBUG debugs in the form "zcrypt: function_name -> message"
- *
- * PRINTK is like PDEBUG, except that it is always enabled
- * PRINTKN is like PRINTK, except that it does not include the function name
- * PRINTKW is like PRINTK, except that it uses KERN_WARNING
- * PRINTKC is like PRINTK, except that it uses KERN_CRIT
- */
-#define DEV_NAME "zcrypt"
-
-#define PRINTK(fmt, args...) \
- printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __func__ , ## args)
-#define PRINTKN(fmt, args...) \
- printk(KERN_DEBUG DEV_NAME ": " fmt, ## args)
-#define PRINTKW(fmt, args...) \
- printk(KERN_WARNING DEV_NAME ": %s -> " fmt, __func__ , ## args)
-#define PRINTKC(fmt, args...) \
- printk(KERN_CRIT DEV_NAME ": %s -> " fmt, __func__ , ## args)
-
-#ifdef ZCRYPT_DEBUG
-#define PDEBUG(fmt, args...) \
- printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __func__ , ## args)
-#else
-#define PDEBUG(fmt, args...) do {} while (0)
-#endif
-
#include "ap_bus.h"
#include <asm/zcrypt.h>
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index 08657f604b8..54f4cbc3be9 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -49,6 +49,7 @@
static struct ap_device_id zcrypt_cex2a_ids[] = {
{ AP_DEVICE(AP_DEVICE_TYPE_CEX2A) },
+ { AP_DEVICE(AP_DEVICE_TYPE_CEX2A2) },
{ /* end of list */ },
};
@@ -242,9 +243,6 @@ static int convert_response(struct zcrypt_device *zdev,
return convert_type80(zdev, reply,
outputdata, outputdatalength);
default: /* Unknown response type, this should NEVER EVER happen */
- PRINTK("Unrecognized Message Header: %08x%08x\n",
- *(unsigned int *) reply->message,
- *(unsigned int *) (reply->message+4));
zdev->online = 0;
return -EAGAIN; /* repeat the request on a different device. */
}
diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h
index 3e27fe77d20..03ba27f05f9 100644
--- a/drivers/s390/crypto/zcrypt_error.h
+++ b/drivers/s390/crypto/zcrypt_error.h
@@ -92,10 +92,6 @@ static inline int convert_error(struct zcrypt_device *zdev,
{
struct error_hdr *ehdr = reply->message;
- PRINTK("Hardware error : Type %02x Message Header: %08x%08x\n",
- ehdr->type, *(unsigned int *) reply->message,
- *(unsigned int *) (reply->message + 4));
-
switch (ehdr->reply_code) {
case REP82_ERROR_OPERAND_INVALID:
case REP82_ERROR_OPERAND_SIZE:
@@ -123,8 +119,6 @@ static inline int convert_error(struct zcrypt_device *zdev,
zdev->online = 0;
return -EAGAIN;
default:
- PRINTKW("unknown type %02x reply code = %d\n",
- ehdr->type, ehdr->reply_code);
zdev->online = 0;
return -EAGAIN; /* repeat the request on a different device. */
}
diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c
index 6e93b475178..12da4815ba8 100644
--- a/drivers/s390/crypto/zcrypt_pcica.c
+++ b/drivers/s390/crypto/zcrypt_pcica.c
@@ -226,9 +226,6 @@ static int convert_response(struct zcrypt_device *zdev,
return convert_type84(zdev, reply,
outputdata, outputdatalength);
default: /* Unknown response type, this should NEVER EVER happen */
- PRINTK("Unrecognized Message Header: %08x%08x\n",
- *(unsigned int *) reply->message,
- *(unsigned int *) (reply->message+4));
zdev->online = 0;
return -EAGAIN; /* repeat the request on a different device. */
}
diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c
index 17ea56ce1c1..779952cb19f 100644
--- a/drivers/s390/crypto/zcrypt_pcicc.c
+++ b/drivers/s390/crypto/zcrypt_pcicc.c
@@ -361,26 +361,18 @@ static int convert_type86(struct zcrypt_device *zdev,
service_rc = le16_to_cpu(msg->cprb.ccp_rtcode);
if (unlikely(service_rc != 0)) {
service_rs = le16_to_cpu(msg->cprb.ccp_rscode);
- if (service_rc == 8 && service_rs == 66) {
- PDEBUG("Bad block format on PCICC\n");
+ if (service_rc == 8 && service_rs == 66)
return -EINVAL;
- }
- if (service_rc == 8 && service_rs == 65) {
- PDEBUG("Probably an even modulus on PCICC\n");
+ if (service_rc == 8 && service_rs == 65)
return -EINVAL;
- }
if (service_rc == 8 && service_rs == 770) {
- PDEBUG("Invalid key length on PCICC\n");
zdev->max_mod_size = PCICC_MAX_MOD_SIZE_OLD;
return -EAGAIN;
}
if (service_rc == 8 && service_rs == 783) {
- PDEBUG("Extended bitlengths not enabled on PCICC\n");
zdev->max_mod_size = PCICC_MAX_MOD_SIZE_OLD;
return -EAGAIN;
}
- PRINTK("Unknown service rc/rs (PCICC): %d/%d\n",
- service_rc, service_rs);
zdev->online = 0;
return -EAGAIN; /* repeat the request on a different device. */
}
@@ -434,9 +426,6 @@ static int convert_response(struct zcrypt_device *zdev,
outputdata, outputdatalength);
/* no break, incorrect cprb version is an unknown response */
default: /* Unknown response type, this should NEVER EVER happen */
- PRINTK("Unrecognized Message Header: %08x%08x\n",
- *(unsigned int *) reply->message,
- *(unsigned int *) (reply->message+4));
zdev->online = 0;
return -EAGAIN; /* repeat the request on a different device. */
}
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index 0bc9b3188e6..d8ad36f8154 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -72,6 +72,7 @@ struct response_type {
static struct ap_device_id zcrypt_pcixcc_ids[] = {
{ AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) },
{ AP_DEVICE(AP_DEVICE_TYPE_CEX2C) },
+ { AP_DEVICE(AP_DEVICE_TYPE_CEX2C2) },
{ /* end of list */ },
};
@@ -289,38 +290,19 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
ap_msg->length = sizeof(struct type6_hdr) +
CEIL4(xcRB->request_control_blk_length) +
xcRB->request_data_length;
- if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE) {
- PRINTK("Combined message is too large (%ld/%d/%d).\n",
- sizeof(struct type6_hdr),
- xcRB->request_control_blk_length,
- xcRB->request_data_length);
+ if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE)
return -EFAULT;
- }
- if (CEIL4(xcRB->reply_control_blk_length) >
- PCIXCC_MAX_XCRB_REPLY_SIZE) {
- PDEBUG("Reply CPRB length is too large (%d).\n",
- xcRB->request_control_blk_length);
+ if (CEIL4(xcRB->reply_control_blk_length) > PCIXCC_MAX_XCRB_REPLY_SIZE)
return -EFAULT;
- }
- if (CEIL4(xcRB->reply_data_length) > PCIXCC_MAX_XCRB_DATA_SIZE) {
- PDEBUG("Reply data block length is too large (%d).\n",
- xcRB->reply_data_length);
+ if (CEIL4(xcRB->reply_data_length) > PCIXCC_MAX_XCRB_DATA_SIZE)
return -EFAULT;
- }
replylen = CEIL4(xcRB->reply_control_blk_length) +
CEIL4(xcRB->reply_data_length) +
sizeof(struct type86_fmt2_msg);
if (replylen > PCIXCC_MAX_XCRB_RESPONSE_SIZE) {
- PDEBUG("Reply CPRB + data block > PCIXCC_MAX_XCRB_RESPONSE_SIZE"
- " (%d/%d/%d).\n",
- sizeof(struct type86_fmt2_msg),
- xcRB->reply_control_blk_length,
- xcRB->reply_data_length);
xcRB->reply_control_blk_length = PCIXCC_MAX_XCRB_RESPONSE_SIZE -
(sizeof(struct type86_fmt2_msg) +
CEIL4(xcRB->reply_data_length));
- PDEBUG("Capping Reply CPRB length at %d\n",
- xcRB->reply_control_blk_length);
}
/* prepare type6 header */
@@ -339,11 +321,8 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
xcRB->request_control_blk_length))
return -EFAULT;
if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) >
- xcRB->request_control_blk_length) {
- PDEBUG("cprb_len too large (%d/%d)\n", msg->cprbx.cprb_len,
- xcRB->request_control_blk_length);
+ xcRB->request_control_blk_length)
return -EFAULT;
- }
function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code));
@@ -471,29 +450,18 @@ static int convert_type86_ica(struct zcrypt_device *zdev,
service_rc = msg->cprbx.ccp_rtcode;
if (unlikely(service_rc != 0)) {
service_rs = msg->cprbx.ccp_rscode;
- if (service_rc == 8 && service_rs == 66) {
- PDEBUG("Bad block format on PCIXCC/CEX2C\n");
+ if (service_rc == 8 && service_rs == 66)
return -EINVAL;
- }
- if (service_rc == 8 && service_rs == 65) {
- PDEBUG("Probably an even modulus on PCIXCC/CEX2C\n");
+ if (service_rc == 8 && service_rs == 65)
return -EINVAL;
- }
- if (service_rc == 8 && service_rs == 770) {
- PDEBUG("Invalid key length on PCIXCC/CEX2C\n");
+ if (service_rc == 8 && service_rs == 770)
return -EINVAL;
- }
if (service_rc == 8 && service_rs == 783) {
- PDEBUG("Extended bitlengths not enabled on PCIXCC/CEX2C\n");
zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD;
return -EAGAIN;
}
- if (service_rc == 12 && service_rs == 769) {
- PDEBUG("Invalid key on PCIXCC/CEX2C\n");
+ if (service_rc == 12 && service_rs == 769)
return -EINVAL;
- }
- PRINTK("Unknown service rc/rs (PCIXCC/CEX2C): %d/%d\n",
- service_rc, service_rs);
zdev->online = 0;
return -EAGAIN; /* repeat the request on a different device. */
}
@@ -569,11 +537,8 @@ static int convert_type86_rng(struct zcrypt_device *zdev,
} __attribute__((packed)) *msg = reply->message;
char *data = reply->message;
- if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0) {
- PDEBUG("RNG response error on PCIXCC/CEX2C rc=%hu/rs=%hu\n",
- rc, rs);
+ if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0)
return -EINVAL;
- }
memcpy(buffer, data + msg->fmt2.offset2, msg->fmt2.count2);
return msg->fmt2.count2;
}
@@ -598,9 +563,6 @@ static int convert_response_ica(struct zcrypt_device *zdev,
outputdata, outputdatalength);
/* no break, incorrect cprb version is an unknown response */
default: /* Unknown response type, this should NEVER EVER happen */
- PRINTK("Unrecognized Message Header: %08x%08x\n",
- *(unsigned int *) reply->message,
- *(unsigned int *) (reply->message+4));
zdev->online = 0;
return -EAGAIN; /* repeat the request on a different device. */
}
@@ -627,9 +589,6 @@ static int convert_response_xcrb(struct zcrypt_device *zdev,
return convert_type86_xcrb(zdev, reply, xcRB);
/* no break, incorrect cprb version is an unknown response */
default: /* Unknown response type, this should NEVER EVER happen */
- PRINTK("Unrecognized Message Header: %08x%08x\n",
- *(unsigned int *) reply->message,
- *(unsigned int *) (reply->message+4));
xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
zdev->online = 0;
return -EAGAIN; /* repeat the request on a different device. */
@@ -653,9 +612,6 @@ static int convert_response_rng(struct zcrypt_device *zdev,
return convert_type86_rng(zdev, reply, data);
/* no break, incorrect cprb version is an unknown response */
default: /* Unknown response type, this should NEVER EVER happen */
- PRINTK("Unrecognized Message Header: %08x%08x\n",
- *(unsigned int *) reply->message,
- *(unsigned int *) (reply->message+4));
zdev->online = 0;
return -EAGAIN; /* repeat the request on a different device. */
}
@@ -700,10 +656,7 @@ static void zcrypt_pcixcc_receive(struct ap_device *ap_dev,
memcpy(msg->message, reply->message, length);
break;
default:
- PRINTK("Invalid internal response type: %i\n",
- resp_type->type);
- memcpy(msg->message, &error_reply,
- sizeof error_reply);
+ memcpy(msg->message, &error_reply, sizeof error_reply);
}
} else
memcpy(msg->message, reply->message, sizeof error_reply);
diff --git a/drivers/s390/kvm/Makefile b/drivers/s390/kvm/Makefile
index 4a5ec39f9ca..0815690ac1e 100644
--- a/drivers/s390/kvm/Makefile
+++ b/drivers/s390/kvm/Makefile
@@ -6,4 +6,4 @@
# it under the terms of the GNU General Public License (version 2 only)
# as published by the Free Software Foundation.
-obj-$(CONFIG_VIRTIO) += kvm_virtio.o
+obj-$(CONFIG_S390_GUEST) += kvm_virtio.o
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 5ab34340919..ff4a6931bb8 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -15,6 +15,7 @@
#include <linux/err.h>
#include <linux/virtio.h>
#include <linux/virtio_config.h>
+#include <linux/virtio_console.h>
#include <linux/interrupt.h>
#include <linux/virtio_ring.h>
#include <linux/pfn.h>
@@ -23,6 +24,7 @@
#include <asm/kvm_virtio.h>
#include <asm/setup.h>
#include <asm/s390_ext.h>
+#include <asm/s390_rdev.h>
#define VIRTIO_SUBCODE_64 0x0D00
@@ -87,16 +89,20 @@ static u32 kvm_get_features(struct virtio_device *vdev)
return features;
}
-static void kvm_set_features(struct virtio_device *vdev, u32 features)
+static void kvm_finalize_features(struct virtio_device *vdev)
{
- unsigned int i;
+ unsigned int i, bits;
struct kvm_device_desc *desc = to_kvmdev(vdev)->desc;
/* Second half of bitmap is features we accept. */
u8 *out_features = kvm_vq_features(desc) + desc->feature_len;
+ /* Give virtio_ring a chance to accept features. */
+ vring_transport_features(vdev);
+
memset(out_features, 0, desc->feature_len);
- for (i = 0; i < min(desc->feature_len * 8, 32); i++) {
- if (features & (1 << i))
+ bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8;
+ for (i = 0; i < bits; i++) {
+ if (test_bit(i, vdev->features))
out_features[i / 8] |= (1 << (i % 8));
}
}
@@ -222,7 +228,7 @@ static void kvm_del_vq(struct virtqueue *vq)
*/
static struct virtio_config_ops kvm_vq_configspace_ops = {
.get_features = kvm_get_features,
- .set_features = kvm_set_features,
+ .finalize_features = kvm_finalize_features,
.get = kvm_get,
.set = kvm_set,
.get_status = kvm_get_status,
@@ -236,10 +242,7 @@ static struct virtio_config_ops kvm_vq_configspace_ops = {
* The root device for the kvm virtio devices.
* This makes them appear as /sys/devices/kvm_s390/0,1,2 not /sys/devices/0,1,2.
*/
-static struct device kvm_root = {
- .parent = NULL,
- .bus_id = "kvm_s390",
-};
+static struct device *kvm_root;
/*
* adds a new device and register it with virtio
@@ -256,7 +259,7 @@ static void add_kvm_device(struct kvm_device_desc *d, unsigned int offset)
return;
}
- kdev->vdev.dev.parent = &kvm_root;
+ kdev->vdev.dev.parent = kvm_root;
kdev->vdev.id.device = d->type;
kdev->vdev.config = &kvm_vq_configspace_ops;
kdev->desc = d;
@@ -312,15 +315,16 @@ static int __init kvm_devices_init(void)
if (!MACHINE_IS_KVM)
return -ENODEV;
- rc = device_register(&kvm_root);
- if (rc) {
+ kvm_root = s390_root_dev_register("kvm_s390");
+ if (IS_ERR(kvm_root)) {
+ rc = PTR_ERR(kvm_root);
printk(KERN_ERR "Could not register kvm_s390 root device");
return rc;
}
rc = vmem_add_mapping(PFN_PHYS(max_pfn), PAGE_SIZE);
if (rc) {
- device_unregister(&kvm_root);
+ s390_root_dev_unregister(kvm_root);
return rc;
}
@@ -333,6 +337,25 @@ static int __init kvm_devices_init(void)
return 0;
}
+/* code for early console output with virtio_console */
+static __init int early_put_chars(u32 vtermno, const char *buf, int count)
+{
+ char scratch[17];
+ unsigned int len = count;
+
+ if (len > sizeof(scratch) - 1)
+ len = sizeof(scratch) - 1;
+ scratch[len] = '\0';
+ memcpy(scratch, buf, len);
+ kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, __pa(scratch));
+ return len;
+}
+
+void __init s390_virtio_console_init(void)
+{
+ virtio_cons_early_init(early_put_chars);
+}
+
/*
* We do this after core stuff, but before the drivers.
*/
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index 04a1d7bf678..f5e618562c5 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -58,13 +58,13 @@
* 1.10 Changes for Buffer allocation
* 1.15 Changed for 2.6 Kernel No longer compiles on 2.4 or lower
* 1.25 Added Packing support
+ * 1.5
*/
#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>
#include <asm/debug.h>
#include <asm/idals.h>
#include <asm/io.h>
-
#include <linux/bitops.h>
#include <linux/ctype.h>
#include <linux/delay.h>
@@ -90,36 +90,10 @@
#include "cu3088.h"
#include "claw.h"
-MODULE_AUTHOR("Andy Richter <richtera@us.ibm.com>");
-MODULE_DESCRIPTION("Linux for zSeries CLAW Driver\n" \
- "Copyright 2000,2005 IBM Corporation\n");
-MODULE_LICENSE("GPL");
-
-/* Debugging is based on DEBUGMSG, IOTRACE, or FUNCTRACE options:
- DEBUGMSG - Enables output of various debug messages in the code
- IOTRACE - Enables output of CCW and other IO related traces
- FUNCTRACE - Enables output of function entry/exit trace
- Define any combination of above options to enable tracing
-
- CLAW also uses the s390dbf file system see claw_trace and claw_setup
+/*
+ CLAW uses the s390dbf file system see claw_trace and claw_setup
*/
-/* following enables tracing */
-//#define DEBUGMSG
-//#define IOTRACE
-//#define FUNCTRACE
-
-#ifdef DEBUGMSG
-#define DEBUG
-#endif
-
-#ifdef IOTRACE
-#define DEBUG
-#endif
-
-#ifdef FUNCTRACE
-#define DEBUG
-#endif
static char debug_buffer[255];
/**
@@ -146,7 +120,6 @@ claw_register_debug_facility(void)
claw_dbf_setup = debug_register("claw_setup", 2, 1, 8);
claw_dbf_trace = debug_register("claw_trace", 2, 2, 8);
if (claw_dbf_setup == NULL || claw_dbf_trace == NULL) {
- printk(KERN_WARNING "Not enough memory for debug facility.\n");
claw_unregister_debug_facility();
return -ENOMEM;
}
@@ -160,14 +133,14 @@ claw_register_debug_facility(void)
static inline void
claw_set_busy(struct net_device *dev)
{
- ((struct claw_privbk *) dev->priv)->tbusy=1;
+ ((struct claw_privbk *)dev->ml_priv)->tbusy = 1;
eieio();
}
static inline void
claw_clear_busy(struct net_device *dev)
{
- clear_bit(0, &(((struct claw_privbk *) dev->priv)->tbusy));
+ clear_bit(0, &(((struct claw_privbk *) dev->ml_priv)->tbusy));
netif_wake_queue(dev);
eieio();
}
@@ -176,20 +149,20 @@ static inline int
claw_check_busy(struct net_device *dev)
{
eieio();
- return ((struct claw_privbk *) dev->priv)->tbusy;
+ return ((struct claw_privbk *) dev->ml_priv)->tbusy;
}
static inline void
claw_setbit_busy(int nr,struct net_device *dev)
{
netif_stop_queue(dev);
- set_bit(nr, (void *)&(((struct claw_privbk *)dev->priv)->tbusy));
+ set_bit(nr, (void *)&(((struct claw_privbk *)dev->ml_priv)->tbusy));
}
static inline void
claw_clearbit_busy(int nr,struct net_device *dev)
{
- clear_bit(nr,(void *)&(((struct claw_privbk *)dev->priv)->tbusy));
+ clear_bit(nr, (void *)&(((struct claw_privbk *)dev->ml_priv)->tbusy));
netif_wake_queue(dev);
}
@@ -198,7 +171,7 @@ claw_test_and_setbit_busy(int nr,struct net_device *dev)
{
netif_stop_queue(dev);
return test_and_set_bit(nr,
- (void *)&(((struct claw_privbk *) dev->priv)->tbusy));
+ (void *)&(((struct claw_privbk *) dev->ml_priv)->tbusy));
}
@@ -232,9 +205,6 @@ static void probe_error( struct ccwgroup_device *cgdev);
static struct net_device_stats *claw_stats(struct net_device *dev);
static int pages_to_order_of_mag(int num_of_pages);
static struct sk_buff *claw_pack_skb(struct claw_privbk *privptr);
-#ifdef DEBUG
-static void dumpit (char *buf, int len);
-#endif
/* sysfs Functions */
static ssize_t claw_hname_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t claw_hname_write(struct device *dev, struct device_attribute *attr,
@@ -263,12 +233,12 @@ static int claw_snd_disc(struct net_device *dev, struct clawctl * p_ctl);
static int claw_snd_sys_validate_rsp(struct net_device *dev,
struct clawctl * p_ctl, __u32 return_code);
static int claw_strt_conn_req(struct net_device *dev );
-static void claw_strt_read ( struct net_device *dev, int lock );
-static void claw_strt_out_IO( struct net_device *dev );
-static void claw_free_wrt_buf( struct net_device *dev );
+static void claw_strt_read(struct net_device *dev, int lock);
+static void claw_strt_out_IO(struct net_device *dev);
+static void claw_free_wrt_buf(struct net_device *dev);
/* Functions for unpack reads */
-static void unpack_read (struct net_device *dev );
+static void unpack_read(struct net_device *dev);
/* ccwgroup table */
@@ -284,7 +254,6 @@ static struct ccwgroup_driver claw_group_driver = {
};
/*
-*
* Key functions
*/
@@ -298,23 +267,15 @@ claw_probe(struct ccwgroup_device *cgdev)
int rc;
struct claw_privbk *privptr=NULL;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s Enter\n",__func__);
-#endif
- CLAW_DBF_TEXT(2,setup,"probe");
+ CLAW_DBF_TEXT(2, setup, "probe");
if (!get_device(&cgdev->dev))
return -ENODEV;
-#ifdef DEBUGMSG
- printk(KERN_INFO "claw: variable cgdev =\n");
- dumpit((char *)cgdev, sizeof(struct ccwgroup_device));
-#endif
privptr = kzalloc(sizeof(struct claw_privbk), GFP_KERNEL);
+ cgdev->dev.driver_data = privptr;
if (privptr == NULL) {
probe_error(cgdev);
put_device(&cgdev->dev);
- printk(KERN_WARNING "Out of memory %s %s Exit Line %d \n",
- cgdev->cdev[0]->dev.bus_id,__func__,__LINE__);
- CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM);
+ CLAW_DBF_TEXT_(2, setup, "probex%d", -ENOMEM);
return -ENOMEM;
}
privptr->p_mtc_envelope= kzalloc( MAX_ENVELOPE_SIZE, GFP_KERNEL);
@@ -322,9 +283,7 @@ claw_probe(struct ccwgroup_device *cgdev)
if ((privptr->p_mtc_envelope==NULL) || (privptr->p_env==NULL)) {
probe_error(cgdev);
put_device(&cgdev->dev);
- printk(KERN_WARNING "Out of memory %s %s Exit Line %d \n",
- cgdev->cdev[0]->dev.bus_id,__func__,__LINE__);
- CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM);
+ CLAW_DBF_TEXT_(2, setup, "probex%d", -ENOMEM);
return -ENOMEM;
}
memcpy(privptr->p_env->adapter_name,WS_NAME_NOT_DEF,8);
@@ -340,20 +299,14 @@ claw_probe(struct ccwgroup_device *cgdev)
probe_error(cgdev);
put_device(&cgdev->dev);
printk(KERN_WARNING "add_files failed %s %s Exit Line %d \n",
- cgdev->cdev[0]->dev.bus_id,__func__,__LINE__);
- CLAW_DBF_TEXT_(2,setup,"probex%d",rc);
+ dev_name(&cgdev->cdev[0]->dev), __func__, __LINE__);
+ CLAW_DBF_TEXT_(2, setup, "probex%d", rc);
return rc;
}
- printk(KERN_INFO "claw: sysfs files added for %s\n",cgdev->cdev[0]->dev.bus_id);
privptr->p_env->p_priv = privptr;
cgdev->cdev[0]->handler = claw_irq_handler;
cgdev->cdev[1]->handler = claw_irq_handler;
- cgdev->dev.driver_data = privptr;
-#ifdef FUNCTRACE
- printk(KERN_INFO "claw:%s exit on line %d, "
- "rc = 0\n",__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(2,setup,"prbext 0");
+ CLAW_DBF_TEXT(2, setup, "prbext 0");
return 0;
} /* end of claw_probe */
@@ -366,41 +319,22 @@ static int
claw_tx(struct sk_buff *skb, struct net_device *dev)
{
int rc;
- struct claw_privbk *privptr=dev->priv;
+ struct claw_privbk *privptr = dev->ml_priv;
unsigned long saveflags;
struct chbk *p_ch;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s enter\n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(4,trace,"claw_tx");
+ CLAW_DBF_TEXT(4, trace, "claw_tx");
p_ch=&privptr->channel[WRITE];
if (skb == NULL) {
- printk(KERN_WARNING "%s: null pointer passed as sk_buffer\n",
- dev->name);
privptr->stats.tx_dropped++;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() exit on line %d, rc = EIO\n",
- dev->name,__func__, __LINE__);
-#endif
- CLAW_DBF_TEXT_(2,trace,"clawtx%d",-EIO);
+ privptr->stats.tx_errors++;
+ CLAW_DBF_TEXT_(2, trace, "clawtx%d", -EIO);
return -EIO;
}
-
-#ifdef IOTRACE
- printk(KERN_INFO "%s: variable sk_buff=\n",dev->name);
- dumpit((char *) skb, sizeof(struct sk_buff));
- printk(KERN_INFO "%s: variable dev=\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
-#endif
spin_lock_irqsave(get_ccwdev_lock(p_ch->cdev), saveflags);
rc=claw_hw_tx( skb, dev, 1 );
spin_unlock_irqrestore(get_ccwdev_lock(p_ch->cdev), saveflags);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s exit on line %d, rc = %d\n",
- dev->name, __func__, __LINE__, rc);
-#endif
- CLAW_DBF_TEXT_(4,trace,"clawtx%d",rc);
+ CLAW_DBF_TEXT_(4, trace, "clawtx%d", rc);
return rc;
} /* end of claw_tx */
@@ -419,7 +353,7 @@ claw_pack_skb(struct claw_privbk *privptr)
new_skb = NULL; /* assume no dice */
pkt_cnt = 0;
- CLAW_DBF_TEXT(4,trace,"PackSKBe");
+ CLAW_DBF_TEXT(4, trace, "PackSKBe");
if (!skb_queue_empty(&p_ch->collect_queue)) {
/* some data */
held_skb = skb_dequeue(&p_ch->collect_queue);
@@ -457,13 +391,8 @@ claw_pack_skb(struct claw_privbk *privptr)
skb_queue_head(&p_ch->collect_queue,held_skb);
}
}
-#ifdef IOTRACE
- printk(KERN_INFO "%s: %s() Packed %d len %d\n",
- p_env->ndev->name,
- __func__,pkt_cnt,new_skb->len);
-#endif
}
- CLAW_DBF_TEXT(4,trace,"PackSKBx");
+ CLAW_DBF_TEXT(4, trace, "PackSKBx");
return new_skb;
}
@@ -475,31 +404,14 @@ claw_pack_skb(struct claw_privbk *privptr)
static int
claw_change_mtu(struct net_device *dev, int new_mtu)
{
- struct claw_privbk *privptr=dev->priv;
+ struct claw_privbk *privptr = dev->ml_priv;
int buff_size;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__);
-#endif
-#ifdef DEBUGMSG
- printk(KERN_INFO "variable dev =\n");
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "variable new_mtu = %d\n", new_mtu);
-#endif
- CLAW_DBF_TEXT(4,trace,"setmtu");
+ CLAW_DBF_TEXT(4, trace, "setmtu");
buff_size = privptr->p_env->write_size;
if ((new_mtu < 60) || (new_mtu > buff_size)) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc=EINVAL\n",
- dev->name,
- __func__, __LINE__);
-#endif
return -EINVAL;
}
dev->mtu = new_mtu;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",dev->name,
- __func__, __LINE__);
-#endif
return 0;
} /* end of claw_change_mtu */
@@ -521,24 +433,13 @@ claw_open(struct net_device *dev)
struct timer_list timer;
struct ccwbk *p_buf;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(4,trace,"open");
- if (!dev || (dev->name[0] == 0x00)) {
- CLAW_DBF_TEXT(2,trace,"BadDev");
- printk(KERN_WARNING "claw: Bad device at open failing \n");
- return -ENODEV;
- }
- privptr = (struct claw_privbk *)dev->priv;
+ CLAW_DBF_TEXT(4, trace, "open");
+ privptr = (struct claw_privbk *)dev->ml_priv;
/* allocate and initialize CCW blocks */
if (privptr->buffs_alloc == 0) {
rc=init_ccw_bk(dev);
if (rc) {
- printk(KERN_INFO "%s:%s Exit on line %d, rc=ENOMEM\n",
- dev->name,
- __func__, __LINE__);
- CLAW_DBF_TEXT(2,trace,"openmem");
+ CLAW_DBF_TEXT(2, trace, "openmem");
return -ENOMEM;
}
}
@@ -557,7 +458,7 @@ claw_open(struct net_device *dev)
tasklet_init(&privptr->channel[READ].tasklet, claw_irq_tasklet,
(unsigned long) &privptr->channel[READ]);
for ( i = 0; i < 2; i++) {
- CLAW_DBF_TEXT_(2,trace,"opn_ch%d",i);
+ CLAW_DBF_TEXT_(2, trace, "opn_ch%d", i);
init_waitqueue_head(&privptr->channel[i].wait);
/* skb_queue_head_init(&p_ch->io_queue); */
if (i == WRITE)
@@ -595,15 +496,8 @@ claw_open(struct net_device *dev)
~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) != 0x00) ||
(((privptr->channel[READ].flag |
privptr->channel[WRITE].flag) & CLAW_TIMER) != 0x00)) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: channel problems during open - read:"
- " %02x - write: %02x\n",
- dev->name,
- privptr->channel[READ].last_dstat,
- privptr->channel[WRITE].last_dstat);
-#endif
printk(KERN_INFO "%s: remote side is not ready\n", dev->name);
- CLAW_DBF_TEXT(2,trace,"notrdy");
+ CLAW_DBF_TEXT(2, trace, "notrdy");
for ( i = 0; i < 2; i++) {
spin_lock_irqsave(
@@ -659,23 +553,14 @@ claw_open(struct net_device *dev)
privptr->p_buff_read=NULL;
privptr->p_buff_write=NULL;
claw_clear_busy(dev);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc=EIO\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(2,trace,"open EIO");
+ CLAW_DBF_TEXT(2, trace, "open EIO");
return -EIO;
}
/* Send SystemValidate command */
claw_clear_busy(dev);
-
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc=0\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"openok");
+ CLAW_DBF_TEXT(4, trace, "openok");
return 0;
} /* end of claw_open */
@@ -694,21 +579,14 @@ claw_irq_handler(struct ccw_device *cdev,
struct claw_env *p_env;
struct chbk *p_ch_r=NULL;
-
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s enter \n",__func__);
-#endif
- CLAW_DBF_TEXT(4,trace,"clawirq");
+ CLAW_DBF_TEXT(4, trace, "clawirq");
/* Bypass all 'unsolicited interrupts' */
if (!cdev->dev.driver_data) {
printk(KERN_WARNING "claw: unsolicited interrupt for device:"
"%s received c-%02x d-%02x\n",
- cdev->dev.bus_id,irb->scsw.cstat, irb->scsw.dstat);
-#ifdef FUNCTRACE
- printk(KERN_INFO "claw: %s() "
- "exit on line %d\n",__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(2,trace,"badirq");
+ dev_name(&cdev->dev), irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
+ CLAW_DBF_TEXT(2, trace, "badirq");
return;
}
privptr = (struct claw_privbk *)cdev->dev.driver_data;
@@ -720,268 +598,169 @@ claw_irq_handler(struct ccw_device *cdev,
p_ch = &privptr->channel[WRITE];
else {
printk(KERN_WARNING "claw: Can't determine channel for "
- "interrupt, device %s\n", cdev->dev.bus_id);
- CLAW_DBF_TEXT(2,trace,"badchan");
+ "interrupt, device %s\n", dev_name(&cdev->dev));
+ CLAW_DBF_TEXT(2, trace, "badchan");
return;
}
- CLAW_DBF_TEXT_(4,trace,"IRQCH=%d",p_ch->flag);
+ CLAW_DBF_TEXT_(4, trace, "IRQCH=%d", p_ch->flag);
dev = (struct net_device *) (p_ch->ndev);
p_env=privptr->p_env;
-#ifdef IOTRACE
- printk(KERN_INFO "%s: interrupt for device: %04x "
- "received c-%02x d-%02x state-%02x\n",
- dev->name, p_ch->devno, irb->scsw.cstat,
- irb->scsw.dstat, p_ch->claw_state);
-#endif
-
/* Copy interruption response block. */
memcpy(p_ch->irb, irb, sizeof(struct irb));
- /* Check for good subchannel return code, otherwise error message */
- if (irb->scsw.cstat && !(irb->scsw.cstat & SCHN_STAT_PCI)) {
+ /* Check for good subchannel return code, otherwise info message */
+ if (irb->scsw.cmd.cstat && !(irb->scsw.cmd.cstat & SCHN_STAT_PCI)) {
printk(KERN_INFO "%s: subchannel check for device: %04x -"
" Sch Stat %02x Dev Stat %02x CPA - %04x\n",
dev->name, p_ch->devno,
- irb->scsw.cstat, irb->scsw.dstat,irb->scsw.cpa);
-#ifdef IOTRACE
- dumpit((char *)irb,sizeof(struct irb));
- dumpit((char *)(unsigned long)irb->scsw.cpa,
- sizeof(struct ccw1));
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(2,trace,"chanchk");
+ irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
+ irb->scsw.cmd.cpa);
+ CLAW_DBF_TEXT(2, trace, "chanchk");
/* return; */
}
/* Check the reason-code of a unit check */
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
ccw_check_unit_check(p_ch, irb->ecw[0]);
- }
/* State machine to bring the connection up, down and to restart */
- p_ch->last_dstat = irb->scsw.dstat;
+ p_ch->last_dstat = irb->scsw.cmd.dstat;
switch (p_ch->claw_state) {
- case CLAW_STOP:/* HALT_IO by claw_release (halt sequence) */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: CLAW_STOP enter\n", dev->name);
-#endif
- if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- return;
- }
- wake_up(&p_ch->wait); /* wake up claw_release */
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: CLAW_STOP exit\n", dev->name);
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"stop");
- return;
-
- case CLAW_START_HALT_IO: /* HALT_IO issued by claw_open */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: process CLAW_STAT_HALT_IO\n",
- dev->name);
-#endif
- if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"haltio");
- return;
- }
- if (p_ch->flag == CLAW_READ) {
- p_ch->claw_state = CLAW_START_READ;
- wake_up(&p_ch->wait); /* wake claw_open (READ)*/
- }
+ case CLAW_STOP:/* HALT_IO by claw_release (halt sequence) */
+ if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))))
+ return;
+ wake_up(&p_ch->wait); /* wake up claw_release */
+ CLAW_DBF_TEXT(4, trace, "stop");
+ return;
+ case CLAW_START_HALT_IO: /* HALT_IO issued by claw_open */
+ if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+ CLAW_DBF_TEXT(4, trace, "haltio");
+ return;
+ }
+ if (p_ch->flag == CLAW_READ) {
+ p_ch->claw_state = CLAW_START_READ;
+ wake_up(&p_ch->wait); /* wake claw_open (READ)*/
+ } else if (p_ch->flag == CLAW_WRITE) {
+ p_ch->claw_state = CLAW_START_WRITE;
+ /* send SYSTEM_VALIDATE */
+ claw_strt_read(dev, LOCK_NO);
+ claw_send_control(dev,
+ SYSTEM_VALIDATE_REQUEST,
+ 0, 0, 0,
+ p_env->host_name,
+ p_env->adapter_name);
+ } else {
+ printk(KERN_WARNING "claw: unsolicited "
+ "interrupt for device:"
+ "%s received c-%02x d-%02x\n",
+ dev_name(&cdev->dev),
+ irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
+ return;
+ }
+ CLAW_DBF_TEXT(4, trace, "haltio");
+ return;
+ case CLAW_START_READ:
+ CLAW_DBF_TEXT(4, trace, "ReadIRQ");
+ if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
+ clear_bit(0, (void *)&p_ch->IO_active);
+ if ((p_ch->irb->ecw[0] & 0x41) == 0x41 ||
+ (p_ch->irb->ecw[0] & 0x40) == 0x40 ||
+ (p_ch->irb->ecw[0]) == 0) {
+ privptr->stats.rx_errors++;
+ printk(KERN_INFO "%s: Restart is "
+ "required after remote "
+ "side recovers \n",
+ dev->name);
+ }
+ CLAW_DBF_TEXT(4, trace, "notrdy");
+ return;
+ }
+ if ((p_ch->irb->scsw.cmd.cstat & SCHN_STAT_PCI) &&
+ (p_ch->irb->scsw.cmd.dstat == 0)) {
+ if (test_and_set_bit(CLAW_BH_ACTIVE,
+ (void *)&p_ch->flag_a) == 0)
+ tasklet_schedule(&p_ch->tasklet);
else
- if (p_ch->flag == CLAW_WRITE) {
- p_ch->claw_state = CLAW_START_WRITE;
- /* send SYSTEM_VALIDATE */
- claw_strt_read(dev, LOCK_NO);
- claw_send_control(dev,
- SYSTEM_VALIDATE_REQUEST,
- 0, 0, 0,
- p_env->host_name,
- p_env->adapter_name );
- } else {
- printk(KERN_WARNING "claw: unsolicited "
- "interrupt for device:"
- "%s received c-%02x d-%02x\n",
- cdev->dev.bus_id,
- irb->scsw.cstat,
- irb->scsw.dstat);
- return;
- }
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: process CLAW_STAT_HALT_IO exit\n",
- dev->name);
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"haltio");
- return;
- case CLAW_START_READ:
- CLAW_DBF_TEXT(4,trace,"ReadIRQ");
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
- clear_bit(0, (void *)&p_ch->IO_active);
- if ((p_ch->irb->ecw[0] & 0x41) == 0x41 ||
- (p_ch->irb->ecw[0] & 0x40) == 0x40 ||
- (p_ch->irb->ecw[0]) == 0)
- {
- privptr->stats.rx_errors++;
- printk(KERN_INFO "%s: Restart is "
- "required after remote "
- "side recovers \n",
- dev->name);
- }
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"notrdy");
- return;
- }
- if ((p_ch->irb->scsw.cstat & SCHN_STAT_PCI) &&
- (p_ch->irb->scsw.dstat==0)) {
- if (test_and_set_bit(CLAW_BH_ACTIVE,
- (void *)&p_ch->flag_a) == 0) {
- tasklet_schedule(&p_ch->tasklet);
- }
- else {
- CLAW_DBF_TEXT(4,trace,"PCINoBH");
- }
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"PCI_read");
- return;
- }
- if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"SPend_rd");
- return;
- }
- clear_bit(0, (void *)&p_ch->IO_active);
- claw_clearbit_busy(TB_RETRY,dev);
- if (test_and_set_bit(CLAW_BH_ACTIVE,
- (void *)&p_ch->flag_a) == 0) {
- tasklet_schedule(&p_ch->tasklet);
- }
- else {
- CLAW_DBF_TEXT(4,trace,"RdBHAct");
- }
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: process CLAW_START_READ exit\n",
- dev->name);
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"RdIRQXit");
- return;
- case CLAW_START_WRITE:
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
- printk(KERN_INFO "%s: Unit Check Occured in "
- "write channel\n",dev->name);
- clear_bit(0, (void *)&p_ch->IO_active);
- if (p_ch->irb->ecw[0] & 0x80 ) {
- printk(KERN_INFO "%s: Resetting Event "
- "occurred:\n",dev->name);
- init_timer(&p_ch->timer);
- p_ch->timer.function =
- (void *)claw_write_retry;
- p_ch->timer.data = (unsigned long)p_ch;
- p_ch->timer.expires = jiffies + 10*HZ;
- add_timer(&p_ch->timer);
- printk(KERN_INFO "%s: write connection "
- "restarting\n",dev->name);
- }
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"rstrtwrt");
- return;
- }
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) {
- clear_bit(0, (void *)&p_ch->IO_active);
- printk(KERN_INFO "%s: Unit Exception "
- "Occured in write channel\n",
- dev->name);
- }
- if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"writeUE");
- return;
- }
- clear_bit(0, (void *)&p_ch->IO_active);
- if (claw_test_and_setbit_busy(TB_TX,dev)==0) {
- claw_write_next(p_ch);
- claw_clearbit_busy(TB_TX,dev);
- claw_clear_busy(dev);
- }
- p_ch_r=(struct chbk *)&privptr->channel[READ];
- if (test_and_set_bit(CLAW_BH_ACTIVE,
- (void *)&p_ch_r->flag_a) == 0) {
- tasklet_schedule(&p_ch_r->tasklet);
- }
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: process CLAW_START_WRITE exit\n",
- dev->name);
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"StWtExit");
- return;
- default:
- printk(KERN_WARNING "%s: wrong selection code - irq "
- "state=%d\n",dev->name,p_ch->claw_state);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(2,trace,"badIRQ");
- return;
+ CLAW_DBF_TEXT(4, trace, "PCINoBH");
+ CLAW_DBF_TEXT(4, trace, "PCI_read");
+ return;
+ }
+ if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+ CLAW_DBF_TEXT(4, trace, "SPend_rd");
+ return;
+ }
+ clear_bit(0, (void *)&p_ch->IO_active);
+ claw_clearbit_busy(TB_RETRY, dev);
+ if (test_and_set_bit(CLAW_BH_ACTIVE,
+ (void *)&p_ch->flag_a) == 0)
+ tasklet_schedule(&p_ch->tasklet);
+ else
+ CLAW_DBF_TEXT(4, trace, "RdBHAct");
+ CLAW_DBF_TEXT(4, trace, "RdIRQXit");
+ return;
+ case CLAW_START_WRITE:
+ if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
+ printk(KERN_INFO "%s: Unit Check Occured in "
+ "write channel\n", dev->name);
+ clear_bit(0, (void *)&p_ch->IO_active);
+ if (p_ch->irb->ecw[0] & 0x80) {
+ printk(KERN_INFO "%s: Resetting Event "
+ "occurred:\n", dev->name);
+ init_timer(&p_ch->timer);
+ p_ch->timer.function =
+ (void *)claw_write_retry;
+ p_ch->timer.data = (unsigned long)p_ch;
+ p_ch->timer.expires = jiffies + 10*HZ;
+ add_timer(&p_ch->timer);
+ printk(KERN_INFO "%s: write connection "
+ "restarting\n", dev->name);
+ }
+ CLAW_DBF_TEXT(4, trace, "rstrtwrt");
+ return;
+ }
+ if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) {
+ clear_bit(0, (void *)&p_ch->IO_active);
+ printk(KERN_INFO "%s: Unit Exception "
+ "Occured in write channel\n",
+ dev->name);
+ }
+ if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+ CLAW_DBF_TEXT(4, trace, "writeUE");
+ return;
+ }
+ clear_bit(0, (void *)&p_ch->IO_active);
+ if (claw_test_and_setbit_busy(TB_TX, dev) == 0) {
+ claw_write_next(p_ch);
+ claw_clearbit_busy(TB_TX, dev);
+ claw_clear_busy(dev);
+ }
+ p_ch_r = (struct chbk *)&privptr->channel[READ];
+ if (test_and_set_bit(CLAW_BH_ACTIVE,
+ (void *)&p_ch_r->flag_a) == 0)
+ tasklet_schedule(&p_ch_r->tasklet);
+ CLAW_DBF_TEXT(4, trace, "StWtExit");
+ return;
+ default:
+ printk(KERN_WARNING "%s: wrong selection code - irq "
+ "state=%d\n", dev->name, p_ch->claw_state);
+ CLAW_DBF_TEXT(2, trace, "badIRQ");
+ return;
}
} /* end of claw_irq_handler */
@@ -1000,29 +779,11 @@ claw_irq_tasklet ( unsigned long data )
p_ch = (struct chbk *) data;
dev = (struct net_device *)p_ch->ndev;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__);
-#endif
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable p_ch =\n",dev->name);
- dumpit((char *) p_ch, sizeof(struct chbk));
-#endif
- CLAW_DBF_TEXT(4,trace,"IRQtask");
-
- privptr = (struct claw_privbk *) dev->priv;
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: bh routine - state-%02x\n" ,
- dev->name, p_ch->claw_state);
-#endif
-
+ CLAW_DBF_TEXT(4, trace, "IRQtask");
+ privptr = (struct claw_privbk *)dev->ml_priv;
unpack_read(dev);
clear_bit(CLAW_BH_ACTIVE, (void *)&p_ch->flag_a);
- CLAW_DBF_TEXT(4,trace,"TskletXt");
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
+ CLAW_DBF_TEXT(4, trace, "TskletXt");
return;
} /* end of claw_irq_bh */
@@ -1044,19 +805,10 @@ claw_release(struct net_device *dev)
if (!dev)
return 0;
- privptr = (struct claw_privbk *) dev->priv;
+ privptr = (struct claw_privbk *)dev->ml_priv;
if (!privptr)
return 0;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(4,trace,"release");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "Priv Buffalloc %d\n",privptr->buffs_alloc);
- printk(KERN_INFO "Priv p_buff_ccw = %p\n",&privptr->p_buff_ccw);
-#endif
+ CLAW_DBF_TEXT(4, trace, "release");
privptr->release_pend=1;
claw_setbit_busy(TB_STOP,dev);
for ( i = 1; i >=0 ; i--) {
@@ -1088,19 +840,15 @@ claw_release(struct net_device *dev)
privptr->pk_skb = NULL;
}
if(privptr->buffs_alloc != 1) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"none2fre");
+ CLAW_DBF_TEXT(4, trace, "none2fre");
return 0;
}
- CLAW_DBF_TEXT(4,trace,"freebufs");
+ CLAW_DBF_TEXT(4, trace, "freebufs");
if (privptr->p_buff_ccw != NULL) {
free_pages((unsigned long)privptr->p_buff_ccw,
(int)pages_to_order_of_mag(privptr->p_buff_ccw_num));
}
- CLAW_DBF_TEXT(4,trace,"freeread");
+ CLAW_DBF_TEXT(4, trace, "freeread");
if (privptr->p_env->read_size < PAGE_SIZE) {
if (privptr->p_buff_read != NULL) {
free_pages((unsigned long)privptr->p_buff_read,
@@ -1116,7 +864,7 @@ claw_release(struct net_device *dev)
p_buf=p_buf->next;
}
}
- CLAW_DBF_TEXT(4,trace,"freewrit");
+ CLAW_DBF_TEXT(4, trace, "freewrit");
if (privptr->p_env->write_size < PAGE_SIZE ) {
free_pages((unsigned long)privptr->p_buff_write,
(int)pages_to_order_of_mag(privptr->p_buff_write_num));
@@ -1130,7 +878,7 @@ claw_release(struct net_device *dev)
p_buf=p_buf->next;
}
}
- CLAW_DBF_TEXT(4,trace,"clearptr");
+ CLAW_DBF_TEXT(4, trace, "clearptr");
privptr->buffs_alloc = 0;
privptr->p_buff_ccw=NULL;
privptr->p_buff_read=NULL;
@@ -1167,18 +915,12 @@ claw_release(struct net_device *dev)
dev->name,
privptr->channel[READ].last_dstat,
privptr->channel[WRITE].last_dstat);
- CLAW_DBF_TEXT(2,trace,"badclose");
+ CLAW_DBF_TEXT(2, trace, "badclose");
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"rlsexit");
+ CLAW_DBF_TEXT(4, trace, "rlsexit");
return 0;
} /* end of claw_release */
-
-
/*-------------------------------------------------------------------*
* claw_write_retry *
* *
@@ -1190,32 +932,12 @@ claw_write_retry ( struct chbk *p_ch )
struct net_device *dev=p_ch->ndev;
-
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__);
- printk(KERN_INFO "claw: variable p_ch =\n");
- dumpit((char *) p_ch, sizeof(struct chbk));
-#endif
- CLAW_DBF_TEXT(4,trace,"w_retry");
+ CLAW_DBF_TEXT(4, trace, "w_retry");
if (p_ch->claw_state == CLAW_STOP) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
return;
}
-#ifdef DEBUGMSG
- printk( KERN_INFO "%s:%s state-%02x\n" ,
- dev->name,
- __func__,
- p_ch->claw_state);
-#endif
claw_strt_out_IO( dev );
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"rtry_xit");
+ CLAW_DBF_TEXT(4, trace, "rtry_xit");
return;
} /* end of claw_write_retry */
@@ -1234,16 +956,11 @@ claw_write_next ( struct chbk * p_ch )
struct sk_buff *pk_skb;
int rc;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",p_ch->ndev->name,__func__);
- printk(KERN_INFO "%s: variable p_ch =\n",p_ch->ndev->name);
- dumpit((char *) p_ch, sizeof(struct chbk));
-#endif
- CLAW_DBF_TEXT(4,trace,"claw_wrt");
+ CLAW_DBF_TEXT(4, trace, "claw_wrt");
if (p_ch->claw_state == CLAW_STOP)
return;
dev = (struct net_device *) p_ch->ndev;
- privptr = (struct claw_privbk *) dev->priv;
+ privptr = (struct claw_privbk *) dev->ml_priv;
claw_free_wrt_buf( dev );
if ((privptr->write_free_count > 0) &&
!skb_queue_empty(&p_ch->collect_queue)) {
@@ -1259,11 +976,6 @@ claw_write_next ( struct chbk * p_ch )
if (privptr->p_write_active_first!=NULL) {
claw_strt_out_IO(dev);
}
-
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
return;
} /* end of claw_write_next */
@@ -1275,22 +987,12 @@ claw_write_next ( struct chbk * p_ch )
static void
claw_timer ( struct chbk * p_ch )
{
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Entry\n",p_ch->ndev->name,__func__);
- printk(KERN_INFO "%s: variable p_ch =\n",p_ch->ndev->name);
- dumpit((char *) p_ch, sizeof(struct chbk));
-#endif
- CLAW_DBF_TEXT(4,trace,"timer");
+ CLAW_DBF_TEXT(4, trace, "timer");
p_ch->flag |= CLAW_TIMER;
wake_up(&p_ch->wait);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- p_ch->ndev->name,__func__,__LINE__);
-#endif
return;
} /* end of claw_timer */
-
/*
*
* functions
@@ -1311,10 +1013,8 @@ pages_to_order_of_mag(int num_of_pages)
{
int order_of_mag=1; /* assume 2 pages */
int nump=2;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s Enter pages = %d \n",__func__,num_of_pages);
-#endif
- CLAW_DBF_TEXT_(5,trace,"pages%d",num_of_pages);
+
+ CLAW_DBF_TEXT_(5, trace, "pages%d", num_of_pages);
if (num_of_pages == 1) {return 0; } /* magnitude of 0 = 1 page */
/* 512 pages = 2Meg on 4k page systems */
if (num_of_pages >= 512) {return 9; }
@@ -1325,11 +1025,7 @@ pages_to_order_of_mag(int num_of_pages)
order_of_mag +=1;
}
if (order_of_mag > 9) { order_of_mag = 9; } /* I know it's paranoid */
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s Exit on line %d, order = %d\n",
- __func__,__LINE__, order_of_mag);
-#endif
- CLAW_DBF_TEXT_(5,trace,"mag%d",order_of_mag);
+ CLAW_DBF_TEXT_(5, trace, "mag%d", order_of_mag);
return order_of_mag;
}
@@ -1345,33 +1041,15 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first,
struct claw_privbk *privptr;
struct ccw1 temp_ccw;
struct endccw * p_end;
-#ifdef IOTRACE
- struct ccwbk* p_buf;
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__);
-#endif
-#ifdef DEBUGMSG
- printk(KERN_INFO "dev\n");
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "p_first\n");
- dumpit((char *) p_first, sizeof(struct ccwbk));
- printk(KERN_INFO "p_last\n");
- dumpit((char *) p_last, sizeof(struct ccwbk));
-#endif
- CLAW_DBF_TEXT(4,trace,"addreads");
- privptr = dev->priv;
+ CLAW_DBF_TEXT(4, trace, "addreads");
+ privptr = dev->ml_priv;
p_end = privptr->p_end_ccw;
/* first CCW and last CCW contains a new set of read channel programs
* to apend the running channel programs
*/
if ( p_first==NULL) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"addexit");
+ CLAW_DBF_TEXT(4, trace, "addexit");
return 0;
}
@@ -1398,21 +1076,11 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first,
}
if ( privptr-> p_read_active_first ==NULL ) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s p_read_active_first == NULL \n",
- dev->name,__func__);
- printk(KERN_INFO "%s:%s Read active first/last changed \n",
- dev->name,__func__);
-#endif
privptr-> p_read_active_first= p_first; /* set new first */
privptr-> p_read_active_last = p_last; /* set new last */
}
else {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s Read in progress \n",
- dev->name,__func__);
-#endif
/* set up TIC ccw */
temp_ccw.cda= (__u32)__pa(&p_first->read);
temp_ccw.count=0;
@@ -1449,27 +1117,7 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first,
privptr->p_read_active_last->next = p_first;
privptr->p_read_active_last=p_last;
} /* end of if ( privptr-> p_read_active_first ==NULL) */
-#ifdef IOTRACE
- printk(KERN_INFO "%s:%s dump p_last CCW BK \n",dev->name,__func__);
- dumpit((char *)p_last, sizeof(struct ccwbk));
- printk(KERN_INFO "%s:%s dump p_end CCW BK \n",dev->name,__func__);
- dumpit((char *)p_end, sizeof(struct endccw));
-
- printk(KERN_INFO "%s:%s dump p_first CCW BK \n",dev->name,__func__);
- dumpit((char *)p_first, sizeof(struct ccwbk));
- printk(KERN_INFO "%s:%s Dump Active CCW chain \n",
- dev->name,__func__);
- p_buf=privptr->p_read_active_first;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"addexit");
+ CLAW_DBF_TEXT(4, trace, "addexit");
return 0;
} /* end of add_claw_reads */
@@ -1481,44 +1129,30 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first,
static void
ccw_check_return_code(struct ccw_device *cdev, int return_code)
{
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > enter \n",
- cdev->dev.bus_id,__func__);
-#endif
- CLAW_DBF_TEXT(4,trace,"ccwret");
-#ifdef DEBUGMSG
- printk(KERN_INFO "variable cdev =\n");
- dumpit((char *) cdev, sizeof(struct ccw_device));
- printk(KERN_INFO "variable return_code = %d\n",return_code);
-#endif
+ CLAW_DBF_TEXT(4, trace, "ccwret");
if (return_code != 0) {
switch (return_code) {
- case -EBUSY:
- printk(KERN_INFO "%s: Busy !\n",
- cdev->dev.bus_id);
- break;
- case -ENODEV:
- printk(KERN_EMERG "%s: Missing device called "
- "for IO ENODEV\n", cdev->dev.bus_id);
- break;
- case -EIO:
- printk(KERN_EMERG "%s: Status pending... EIO \n",
- cdev->dev.bus_id);
- break;
- case -EINVAL:
- printk(KERN_EMERG "%s: Invalid Dev State EINVAL \n",
- cdev->dev.bus_id);
- break;
- default:
- printk(KERN_EMERG "%s: Unknown error in "
- "Do_IO %d\n",cdev->dev.bus_id, return_code);
- }
- }
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > exit on line %d\n",
- cdev->dev.bus_id,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"ccwret");
+ case -EBUSY: /* BUSY is a transient state no action needed */
+ break;
+ case -ENODEV:
+ printk(KERN_EMERG "%s: Missing device called "
+ "for IO ENODEV\n", dev_name(&cdev->dev));
+ break;
+ case -EIO:
+ printk(KERN_EMERG "%s: Status pending... EIO \n",
+ dev_name(&cdev->dev));
+ break;
+ case -EINVAL:
+ printk(KERN_EMERG "%s: Invalid Dev State EINVAL \n",
+ dev_name(&cdev->dev));
+ break;
+ default:
+ printk(KERN_EMERG "%s: Unknown error in "
+ "Do_IO %d\n", dev_name(&cdev->dev),
+ return_code);
+ }
+ }
+ CLAW_DBF_TEXT(4, trace, "ccwret");
} /* end of ccw_check_return_code */
/*-------------------------------------------------------------------*
@@ -1528,173 +1162,46 @@ ccw_check_return_code(struct ccw_device *cdev, int return_code)
static void
ccw_check_unit_check(struct chbk * p_ch, unsigned char sense )
{
- struct net_device *dev = p_ch->ndev;
-
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > enter\n",dev->name,__func__);
-#endif
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *)dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable sense =\n",dev->name);
- dumpit((char *)&sense, 2);
-#endif
- CLAW_DBF_TEXT(4,trace,"unitchek");
+ struct net_device *ndev = p_ch->ndev;
+ CLAW_DBF_TEXT(4, trace, "unitchek");
printk(KERN_INFO "%s: Unit Check with sense byte:0x%04x\n",
- dev->name, sense);
+ ndev->name, sense);
if (sense & 0x40) {
if (sense & 0x01) {
printk(KERN_WARNING "%s: Interface disconnect or "
"Selective reset "
- "occurred (remote side)\n", dev->name);
+ "occurred (remote side)\n", ndev->name);
}
else {
printk(KERN_WARNING "%s: System reset occured"
- " (remote side)\n", dev->name);
+ " (remote side)\n", ndev->name);
}
}
else if (sense & 0x20) {
if (sense & 0x04) {
printk(KERN_WARNING "%s: Data-streaming "
- "timeout)\n", dev->name);
+ "timeout)\n", ndev->name);
}
else {
printk(KERN_WARNING "%s: Data-transfer parity"
- " error\n", dev->name);
+ " error\n", ndev->name);
}
}
else if (sense & 0x10) {
if (sense & 0x20) {
printk(KERN_WARNING "%s: Hardware malfunction "
- "(remote side)\n", dev->name);
+ "(remote side)\n", ndev->name);
}
else {
printk(KERN_WARNING "%s: read-data parity error "
- "(remote side)\n", dev->name);
+ "(remote side)\n", ndev->name);
}
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
} /* end of ccw_check_unit_check */
-
-
-/*-------------------------------------------------------------------*
-* Dump buffer format *
-* *
-*--------------------------------------------------------------------*/
-#ifdef DEBUG
-static void
-dumpit(char* buf, int len)
-{
-
- __u32 ct, sw, rm, dup;
- char *ptr, *rptr;
- char tbuf[82], tdup[82];
-#if (CONFIG_64BIT)
- char addr[22];
-#else
- char addr[12];
-#endif
- char boff[12];
- char bhex[82], duphex[82];
- char basc[40];
-
- sw = 0;
- rptr =ptr=buf;
- rm = 16;
- duphex[0] = 0x00;
- dup = 0;
- for ( ct=0; ct < len; ct++, ptr++, rptr++ ) {
- if (sw == 0) {
-#if (CONFIG_64BIT)
- sprintf(addr, "%16.16lX",(unsigned long)rptr);
-#else
- sprintf(addr, "%8.8X",(__u32)rptr);
-#endif
- sprintf(boff, "%4.4X", (__u32)ct);
- bhex[0] = '\0';
- basc[0] = '\0';
- }
- if ((sw == 4) || (sw == 12)) {
- strcat(bhex, " ");
- }
- if (sw == 8) {
- strcat(bhex, " ");
- }
-#if (CONFIG_64BIT)
- sprintf(tbuf,"%2.2lX", (unsigned long)*ptr);
-#else
- sprintf(tbuf,"%2.2X", (__u32)*ptr);
-#endif
- tbuf[2] = '\0';
- strcat(bhex, tbuf);
- if ((0!=isprint(*ptr)) && (*ptr >= 0x20)) {
- basc[sw] = *ptr;
- }
- else {
- basc[sw] = '.';
- }
- basc[sw+1] = '\0';
- sw++;
- rm--;
- if (sw==16) {
- if ((strcmp(duphex, bhex)) !=0) {
- if (dup !=0) {
- sprintf(tdup,"Duplicate as above to"
- " %s", addr);
- printk( KERN_INFO " "
- " --- %s ---\n",tdup);
- }
- printk( KERN_INFO " %s (+%s) : %s [%s]\n",
- addr, boff, bhex, basc);
- dup = 0;
- strcpy(duphex, bhex);
- }
- else {
- dup++;
- }
- sw = 0;
- rm = 16;
- }
- } /* endfor */
-
- if (sw != 0) {
- for ( ; rm > 0; rm--, sw++ ) {
- if ((sw==4) || (sw==12)) strcat(bhex, " ");
- if (sw==8) strcat(bhex, " ");
- strcat(bhex, " ");
- strcat(basc, " ");
- }
- if (dup !=0) {
- sprintf(tdup,"Duplicate as above to %s", addr);
- printk( KERN_INFO " --- %s ---\n",
- tdup);
- }
- printk( KERN_INFO " %s (+%s) : %s [%s]\n",
- addr, boff, bhex, basc);
- }
- else {
- if (dup >=1) {
- sprintf(tdup,"Duplicate as above to %s", addr);
- printk( KERN_INFO " --- %s ---\n",
- tdup);
- }
- if (dup !=0) {
- printk( KERN_INFO " %s (+%s) : %s [%s]\n",
- addr, boff, bhex, basc);
- }
- }
- return;
-
-} /* end of dumpit */
-#endif
-
/*-------------------------------------------------------------------*
* find_link *
*--------------------------------------------------------------------*/
@@ -1705,17 +1212,8 @@ find_link(struct net_device *dev, char *host_name, char *ws_name )
struct claw_env *p_env;
int rc=0;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s > enter \n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(2,setup,"findlink");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev = \n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable host_name = %s\n",dev->name, host_name);
- printk(KERN_INFO "%s: variable ws_name = %s\n",dev->name, ws_name);
-#endif
- privptr=dev->priv;
+ CLAW_DBF_TEXT(2, setup, "findlink");
+ privptr = dev->ml_priv;
p_env=privptr->p_env;
switch (p_env->packing)
{
@@ -1737,10 +1235,6 @@ find_link(struct net_device *dev, char *host_name, char *ws_name )
break;
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
return 0;
} /* end of find_link */
@@ -1769,27 +1263,11 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
int lock;
struct clawph *pk_head;
struct chbk *ch;
-#ifdef IOTRACE
- struct ccwbk *p_buf;
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > enter\n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(4,trace,"hw_tx");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev skb =\n",dev->name);
- dumpit((char *) skb, sizeof(struct sk_buff));
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable linkid = %ld\n",dev->name,linkid);
-#endif
- privptr = (struct claw_privbk *) (dev->priv);
+
+ CLAW_DBF_TEXT(4, trace, "hw_tx");
+ privptr = (struct claw_privbk *)(dev->ml_priv);
p_ch=(struct chbk *)&privptr->channel[WRITE];
p_env =privptr->p_env;
-#ifdef IOTRACE
- printk(KERN_INFO "%s: %s() dump sk_buff \n",dev->name,__func__);
- dumpit((char *)skb ,sizeof(struct sk_buff));
-#endif
claw_free_wrt_buf(dev); /* Clean up free chain if posible */
/* scan the write queue to free any completed write packets */
p_first_ccw=NULL;
@@ -1821,11 +1299,6 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
claw_strt_out_IO(dev );
claw_free_wrt_buf( dev );
if (privptr->write_free_count==0) {
-#ifdef IOTRACE
- printk(KERN_INFO "%s: "
- "(claw_check_busy) no free write "
- "buffers\n", dev->name);
-#endif
ch = &privptr->channel[WRITE];
atomic_inc(&skb->users);
skb_queue_tail(&ch->collect_queue, skb);
@@ -1838,10 +1311,6 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
}
/* tx lock */
if (claw_test_and_setbit_busy(TB_TX,dev)) { /* set to busy */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: busy (claw_test_and_setbit_"
- "busy)\n", dev->name);
-#endif
ch = &privptr->channel[WRITE];
atomic_inc(&skb->users);
skb_queue_tail(&ch->collect_queue, skb);
@@ -1858,28 +1327,16 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
privptr->p_write_free_chain == NULL ) {
claw_setbit_busy(TB_NOBUFFER,dev);
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: busy (claw_setbit_busy"
- "(TB_NOBUFFER))\n", dev->name);
- printk(KERN_INFO " free_count: %d, numBuffers : %d\n",
- (int)privptr->write_free_count,(int) numBuffers );
-#endif
ch = &privptr->channel[WRITE];
atomic_inc(&skb->users);
skb_queue_tail(&ch->collect_queue, skb);
- CLAW_DBF_TEXT(2,trace,"clawbusy");
+ CLAW_DBF_TEXT(2, trace, "clawbusy");
goto Done2;
}
pDataAddress=skb->data;
len_of_data=skb->len;
while (len_of_data > 0) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() length-of-data is %ld \n",
- dev->name ,__func__,len_of_data);
- dumpit((char *)pDataAddress ,64);
-#endif
p_this_ccw=privptr->p_write_free_chain; /* get a block */
if (p_this_ccw == NULL) { /* lost the race */
ch = &privptr->channel[WRITE];
@@ -1911,12 +1368,6 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
(__u32)__pa(&p_this_ccw->write);
}
p_last_ccw=p_this_ccw; /* save new last block */
-#ifdef IOTRACE
- printk(KERN_INFO "%s: %s() > CCW and Buffer %ld bytes long \n",
- dev->name,__func__,bytesInThisBuffer);
- dumpit((char *)p_this_ccw, sizeof(struct ccwbk));
- dumpit((char *)p_this_ccw->p_buffer, 64);
-#endif
}
/* FirstCCW and LastCCW now contain a new set of write channel
@@ -1949,13 +1400,11 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
pEnd->write1_nop2.count=1;
} /* end if if (pEnd->write1) */
-
if (privptr->p_write_active_first==NULL ) {
privptr->p_write_active_first=p_first_ccw;
privptr->p_write_active_last=p_last_ccw;
}
else {
-
/* set up Tic CCWs */
tempCCW.cda=(__u32)__pa(&p_first_ccw->write);
@@ -1994,19 +1443,6 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
}
} /* endif (p_first_ccw!=NULL) */
-
-
-#ifdef IOTRACE
- printk(KERN_INFO "%s: %s() > Dump Active CCW chain \n",
- dev->name,__func__);
- p_buf=privptr->p_write_active_first;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
- p_buf=(struct ccwbk*)privptr->p_end_ccw;
- dumpit((char *)p_buf, sizeof(struct endccw));
-#endif
dev_kfree_skb_any(skb);
if (linkid==0) {
lock=LOCK_NO;
@@ -2016,21 +1452,12 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
}
claw_strt_out_IO(dev );
/* if write free count is zero , set NOBUFFER */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() > free_count is %d\n",
- dev->name,__func__,
- (int) privptr->write_free_count );
-#endif
if (privptr->write_free_count==0) {
claw_setbit_busy(TB_NOBUFFER,dev);
}
Done2:
claw_clearbit_busy(TB_TX,dev);
Done:
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > exit on line %d, rc = %d \n",
- dev->name,__func__,__LINE__, rc);
-#endif
return(rc);
} /* end of claw_hw_tx */
@@ -2057,19 +1484,12 @@ init_ccw_bk(struct net_device *dev)
struct ccwbk*p_last_CCWB;
struct ccwbk*p_first_CCWB;
struct endccw *p_endccw=NULL;
- addr_t real_address;
- struct claw_privbk *privptr=dev->priv;
+ addr_t real_address;
+ struct claw_privbk *privptr = dev->ml_priv;
struct clawh *pClawH=NULL;
addr_t real_TIC_address;
int i,j;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() enter \n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(4,trace,"init_ccw");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
-#endif
+ CLAW_DBF_TEXT(4, trace, "init_ccw");
/* initialize statistics field */
privptr->active_link_ID=0;
@@ -2094,20 +1514,6 @@ init_ccw_bk(struct net_device *dev)
*/
ccw_blocks_required =
privptr->p_env->read_buffers+privptr->p_env->write_buffers+1;
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() "
- "ccw_blocks_required=%d\n",
- dev->name,__func__,
- ccw_blocks_required);
- printk(KERN_INFO "%s: %s() "
- "PAGE_SIZE=0x%x\n",
- dev->name,__func__,
- (unsigned int)PAGE_SIZE);
- printk(KERN_INFO "%s: %s() > "
- "PAGE_MASK=0x%x\n",
- dev->name,__func__,
- (unsigned int)PAGE_MASK);
-#endif
/*
* compute number of CCW blocks that will fit in a page
*/
@@ -2115,14 +1521,6 @@ init_ccw_bk(struct net_device *dev)
ccw_pages_required=
DIV_ROUND_UP(ccw_blocks_required, ccw_blocks_perpage);
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() > ccw_blocks_perpage=%d\n",
- dev->name,__func__,
- ccw_blocks_perpage);
- printk(KERN_INFO "%s: %s() > ccw_pages_required=%d\n",
- dev->name,__func__,
- ccw_pages_required);
-#endif
/*
* read and write sizes are set by 2 constants in claw.h
* 4k and 32k. Unpacked values other than 4k are not going to
@@ -2153,36 +1551,6 @@ init_ccw_bk(struct net_device *dev)
claw_write_pages = privptr->p_env->write_buffers *
privptr->p_buff_pages_perwrite;
}
-#ifdef DEBUGMSG
- if (privptr->p_env->read_size < PAGE_SIZE) {
- printk(KERN_INFO "%s: %s() reads_perpage=%d\n",
- dev->name,__func__,
- claw_reads_perpage);
- }
- else {
- printk(KERN_INFO "%s: %s() pages_perread=%d\n",
- dev->name,__func__,
- privptr->p_buff_pages_perread);
- }
- printk(KERN_INFO "%s: %s() read_pages=%d\n",
- dev->name,__func__,
- claw_read_pages);
- if (privptr->p_env->write_size < PAGE_SIZE) {
- printk(KERN_INFO "%s: %s() writes_perpage=%d\n",
- dev->name,__func__,
- claw_writes_perpage);
- }
- else {
- printk(KERN_INFO "%s: %s() pages_perwrite=%d\n",
- dev->name,__func__,
- privptr->p_buff_pages_perwrite);
- }
- printk(KERN_INFO "%s: %s() write_pages=%d\n",
- dev->name,__func__,
- claw_write_pages);
-#endif
-
-
/*
* allocate ccw_pages_required
*/
@@ -2191,17 +1559,6 @@ init_ccw_bk(struct net_device *dev)
(void *)__get_free_pages(__GFP_DMA,
(int)pages_to_order_of_mag(ccw_pages_required ));
if (privptr->p_buff_ccw==NULL) {
- printk(KERN_INFO "%s: %s() "
- "__get_free_pages for CCWs failed : "
- "pages is %d\n",
- dev->name,__func__,
- ccw_pages_required );
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > "
- "exit on line %d, rc = ENOMEM\n",
- dev->name,__func__,
- __LINE__);
-#endif
return -ENOMEM;
}
privptr->p_buff_ccw_num=ccw_pages_required;
@@ -2216,11 +1573,6 @@ init_ccw_bk(struct net_device *dev)
privptr->p_end_ccw = (struct endccw *)&privptr->end_ccw;
real_address = (__u32)__pa(privptr->p_end_ccw);
/* Initialize ending CCW block */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() begin initialize ending CCW blocks\n",
- dev->name,__func__);
-#endif
-
p_endccw=privptr->p_end_ccw;
p_endccw->real=real_address;
p_endccw->write1=0x00;
@@ -2274,21 +1626,10 @@ init_ccw_bk(struct net_device *dev)
p_endccw->read2_nop2.count = 1;
p_endccw->read2_nop2.cda = 0;
-#ifdef IOTRACE
- printk(KERN_INFO "%s: %s() dump claw ending CCW BK \n",
- dev->name,__func__);
- dumpit((char *)p_endccw, sizeof(struct endccw));
-#endif
-
/*
* Build a chain of CCWs
*
*/
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() Begin build a chain of CCW buffer \n",
- dev->name,__func__);
-#endif
p_buff=privptr->p_buff_ccw;
p_free_chain=NULL;
@@ -2303,26 +1644,10 @@ init_ccw_bk(struct net_device *dev)
}
p_buff+=PAGE_SIZE;
}
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() "
- "End build a chain of CCW buffer \n",
- dev->name,__func__);
- p_buf=p_free_chain;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
-#endif
-
/*
* Initialize ClawSignalBlock
*
*/
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() "
- "Begin initialize ClawSignalBlock \n",
- dev->name,__func__);
-#endif
if (privptr->p_claw_signal_blk==NULL) {
privptr->p_claw_signal_blk=p_free_chain;
p_free_chain=p_free_chain->next;
@@ -2331,12 +1656,6 @@ init_ccw_bk(struct net_device *dev)
pClawH->opcode=0xff;
pClawH->flag=CLAW_BUSY;
}
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() > End initialize "
- "ClawSignalBlock\n",
- dev->name,__func__);
- dumpit((char *)privptr->p_claw_signal_blk, sizeof(struct ccwbk));
-#endif
/*
* allocate write_pages_required and add to free chain
@@ -2347,17 +1666,7 @@ init_ccw_bk(struct net_device *dev)
(void *)__get_free_pages(__GFP_DMA,
(int)pages_to_order_of_mag(claw_write_pages ));
if (privptr->p_buff_write==NULL) {
- printk(KERN_INFO "%s: %s() __get_free_pages for write"
- " bufs failed : get is for %d pages\n",
- dev->name,__func__,claw_write_pages );
- free_pages((unsigned long)privptr->p_buff_ccw,
- (int)pages_to_order_of_mag(privptr->p_buff_ccw_num));
privptr->p_buff_ccw=NULL;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > exit on line %d,"
- "rc = ENOMEM\n",
- dev->name,__func__,__LINE__);
-#endif
return -ENOMEM;
}
/*
@@ -2367,10 +1676,6 @@ init_ccw_bk(struct net_device *dev)
memset(privptr->p_buff_write, 0x00,
ccw_pages_required * PAGE_SIZE);
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() Begin build claw write free "
- "chain \n",dev->name,__func__);
-#endif
privptr->p_write_free_chain=NULL;
p_buff=privptr->p_buff_write;
@@ -2406,18 +1711,7 @@ init_ccw_bk(struct net_device *dev)
p_buff=(void *)__get_free_pages(__GFP_DMA,
(int)pages_to_order_of_mag(
privptr->p_buff_pages_perwrite) );
-#ifdef IOTRACE
- printk(KERN_INFO "%s:%s __get_free_pages "
- "for writes buf: get for %d pages\n",
- dev->name,__func__,
- privptr->p_buff_pages_perwrite);
-#endif
if (p_buff==NULL) {
- printk(KERN_INFO "%s:%s __get_free_pages "
- "for writes buf failed : get is for %d pages\n",
- dev->name,
- __func__,
- privptr->p_buff_pages_perwrite );
free_pages((unsigned long)privptr->p_buff_ccw,
(int)pages_to_order_of_mag(
privptr->p_buff_ccw_num));
@@ -2430,12 +1724,6 @@ init_ccw_bk(struct net_device *dev)
privptr->p_buff_pages_perwrite));
p_buf=p_buf->next;
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s exit on line %d, rc = ENOMEM\n",
- dev->name,
- __func__,
- __LINE__);
-#endif
return -ENOMEM;
} /* Error on get_pages */
memset(p_buff, 0x00, privptr->p_env->write_size );
@@ -2464,15 +1752,6 @@ init_ccw_bk(struct net_device *dev)
privptr->write_free_count=privptr->p_env->write_buffers;
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s End build claw write free chain \n",
- dev->name,__func__);
- p_buf=privptr->p_write_free_chain;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
-#endif
/*
* allocate read_pages_required and chain to free chain
*/
@@ -2482,10 +1761,6 @@ init_ccw_bk(struct net_device *dev)
(void *)__get_free_pages(__GFP_DMA,
(int)pages_to_order_of_mag(claw_read_pages) );
if (privptr->p_buff_read==NULL) {
- printk(KERN_INFO "%s: %s() "
- "__get_free_pages for read buf failed : "
- "get is for %d pages\n",
- dev->name,__func__,claw_read_pages );
free_pages((unsigned long)privptr->p_buff_ccw,
(int)pages_to_order_of_mag(
privptr->p_buff_ccw_num));
@@ -2495,10 +1770,6 @@ init_ccw_bk(struct net_device *dev)
privptr->p_buff_write_num));
privptr->p_buff_ccw=NULL;
privptr->p_buff_write=NULL;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > exit on line %d, rc ="
- " ENOMEM\n",dev->name,__func__,__LINE__);
-#endif
return -ENOMEM;
}
memset(privptr->p_buff_read, 0x00, claw_read_pages * PAGE_SIZE);
@@ -2507,10 +1778,6 @@ init_ccw_bk(struct net_device *dev)
* Build CLAW read free chain
*
*/
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() Begin build claw read free chain \n",
- dev->name,__func__);
-#endif
p_buff=privptr->p_buff_read;
for (i=0 ; i< privptr->p_env->read_buffers ; i++) {
p_buf = p_free_chain;
@@ -2587,19 +1854,10 @@ init_ccw_bk(struct net_device *dev)
} /* for read_buffers */
} /* read_size < PAGE_SIZE */
else { /* read Size >= PAGE_SIZE */
-
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() Begin build claw read free chain \n",
- dev->name,__func__);
-#endif
for (i=0 ; i< privptr->p_env->read_buffers ; i++) {
p_buff = (void *)__get_free_pages(__GFP_DMA,
(int)pages_to_order_of_mag(privptr->p_buff_pages_perread) );
if (p_buff==NULL) {
- printk(KERN_INFO "%s: %s() __get_free_pages for read "
- "buf failed : get is for %d pages\n",
- dev->name,__func__,
- privptr->p_buff_pages_perread );
free_pages((unsigned long)privptr->p_buff_ccw,
(int)pages_to_order_of_mag(privptr->p_buff_ccw_num));
/* free the write pages */
@@ -2620,11 +1878,6 @@ init_ccw_bk(struct net_device *dev)
}
privptr->p_buff_ccw=NULL;
privptr->p_buff_write=NULL;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() exit on line %d, rc = ENOMEM\n",
- dev->name,__func__,
- __LINE__);
-#endif
return -ENOMEM;
}
memset(p_buff, 0x00, privptr->p_env->read_size);
@@ -2693,22 +1946,9 @@ init_ccw_bk(struct net_device *dev)
} /* For read_buffers */
} /* read_size >= PAGE_SIZE */
} /* pBuffread = NULL */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() > End build claw read free chain \n",
- dev->name,__func__);
- p_buf=p_first_CCWB;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
-
-#endif
add_claw_reads( dev ,p_first_CCWB , p_last_CCWB);
privptr->buffs_alloc = 1;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
+
return 0;
} /* end of init_ccw_bk */
@@ -2721,34 +1961,18 @@ init_ccw_bk(struct net_device *dev)
static void
probe_error( struct ccwgroup_device *cgdev)
{
- struct claw_privbk *privptr;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s enter \n",__func__);
-#endif
- CLAW_DBF_TEXT(4,trace,"proberr");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s variable cgdev =\n",__func__);
- dumpit((char *) cgdev, sizeof(struct ccwgroup_device));
-#endif
- privptr=(struct claw_privbk *)cgdev->dev.driver_data;
- if (privptr!=NULL) {
- kfree(privptr->p_env);
- privptr->p_env=NULL;
- kfree(privptr->p_mtc_envelope);
- privptr->p_mtc_envelope=NULL;
- kfree(privptr);
- privptr=NULL;
- }
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s > exit on line %d\n",
- __func__,__LINE__);
-#endif
+ struct claw_privbk *privptr;
- return;
+ CLAW_DBF_TEXT(4, trace, "proberr");
+ privptr = (struct claw_privbk *) cgdev->dev.driver_data;
+ if (privptr != NULL) {
+ cgdev->dev.driver_data = NULL;
+ kfree(privptr->p_env);
+ kfree(privptr->p_mtc_envelope);
+ kfree(privptr);
+ }
} /* probe_error */
-
-
/*-------------------------------------------------------------------*
* claw_process_control *
* *
@@ -2770,32 +1994,19 @@ claw_process_control( struct net_device *dev, struct ccwbk * p_ccw)
struct conncmd *p_connect=NULL;
int rc;
struct chbk *p_ch = NULL;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > enter \n",
- dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(2,setup,"clw_cntl");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable p_ccw =\n",dev->name);
- dumpit((char *) p_ccw, sizeof(struct ccwbk *));
-#endif
+ struct device *tdev;
+ CLAW_DBF_TEXT(2, setup, "clw_cntl");
udelay(1000); /* Wait a ms for the control packets to
*catch up to each other */
- privptr=dev->priv;
+ privptr = dev->ml_priv;
p_env=privptr->p_env;
+ tdev = &privptr->channel[READ].cdev->dev;
memcpy( &temp_host_name, p_env->host_name, 8);
memcpy( &temp_ws_name, p_env->adapter_name , 8);
printk(KERN_INFO "%s: CLAW device %.8s: "
"Received Control Packet\n",
dev->name, temp_ws_name);
if (privptr->release_pend==1) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() > "
- "exit on line %d, rc=0\n",
- dev->name,__func__,__LINE__);
-#endif
return 0;
}
p_buf=p_ccw->p_buffer;
@@ -2805,261 +2016,246 @@ claw_process_control( struct net_device *dev, struct ccwbk * p_ccw)
} else {
memcpy(p_ctlbk, p_buf, sizeof(struct clawctl));
}
-#ifdef IOTRACE
- printk(KERN_INFO "%s: dump claw control data inbound\n",dev->name);
- dumpit((char *)p_ctlbk, sizeof(struct clawctl));
-#endif
switch (p_ctlbk->command)
{
- case SYSTEM_VALIDATE_REQUEST:
- if (p_ctlbk->version!=CLAW_VERSION_ID) {
- claw_snd_sys_validate_rsp(dev, p_ctlbk,
- CLAW_RC_WRONG_VERSION );
- printk("%s: %d is wrong version id. "
- "Expected %d\n",
- dev->name, p_ctlbk->version,
- CLAW_VERSION_ID);
- }
- p_sysval=(struct sysval *)&(p_ctlbk->data);
- printk( "%s: Recv Sys Validate Request: "
- "Vers=%d,link_id=%d,Corr=%d,WS name=%."
- "8s,Host name=%.8s\n",
- dev->name, p_ctlbk->version,
- p_ctlbk->linkid,
- p_ctlbk->correlator,
- p_sysval->WS_name,
- p_sysval->host_name);
- if (0!=memcmp(temp_host_name,p_sysval->host_name,8)) {
- claw_snd_sys_validate_rsp(dev, p_ctlbk,
- CLAW_RC_NAME_MISMATCH );
- CLAW_DBF_TEXT(2,setup,"HSTBAD");
- CLAW_DBF_TEXT_(2,setup,"%s",p_sysval->host_name);
- CLAW_DBF_TEXT_(2,setup,"%s",temp_host_name);
- printk(KERN_INFO "%s: Host name mismatch\n",
- dev->name);
- printk(KERN_INFO "%s: Received :%s: "
- "expected :%s: \n",
- dev->name,
- p_sysval->host_name,
- temp_host_name);
- }
- if (0!=memcmp(temp_ws_name,p_sysval->WS_name,8)) {
- claw_snd_sys_validate_rsp(dev, p_ctlbk,
- CLAW_RC_NAME_MISMATCH );
- CLAW_DBF_TEXT(2,setup,"WSNBAD");
- CLAW_DBF_TEXT_(2,setup,"%s",p_sysval->WS_name);
- CLAW_DBF_TEXT_(2,setup,"%s",temp_ws_name);
- printk(KERN_INFO "%s: WS name mismatch\n",
- dev->name);
- printk(KERN_INFO "%s: Received :%s: "
- "expected :%s: \n",
- dev->name,
- p_sysval->WS_name,
- temp_ws_name);
- }
- if (( p_sysval->write_frame_size < p_env->write_size) &&
- ( p_env->packing == 0)) {
- claw_snd_sys_validate_rsp(dev, p_ctlbk,
- CLAW_RC_HOST_RCV_TOO_SMALL );
- printk(KERN_INFO "%s: host write size is too "
- "small\n", dev->name);
- CLAW_DBF_TEXT(2,setup,"wrtszbad");
- }
- if (( p_sysval->read_frame_size < p_env->read_size) &&
- ( p_env->packing == 0)) {
- claw_snd_sys_validate_rsp(dev, p_ctlbk,
- CLAW_RC_HOST_RCV_TOO_SMALL );
- printk(KERN_INFO "%s: host read size is too "
- "small\n", dev->name);
- CLAW_DBF_TEXT(2,setup,"rdsizbad");
- }
- claw_snd_sys_validate_rsp(dev, p_ctlbk, 0 );
- printk("%s: CLAW device %.8s: System validate"
- " completed.\n",dev->name, temp_ws_name);
- printk("%s: sys Validate Rsize:%d Wsize:%d\n",dev->name,
- p_sysval->read_frame_size,p_sysval->write_frame_size);
- privptr->system_validate_comp=1;
- if(strncmp(p_env->api_type,WS_APPL_NAME_PACKED,6) == 0) {
- p_env->packing = PACKING_ASK;
- }
- claw_strt_conn_req(dev);
- break;
-
- case SYSTEM_VALIDATE_RESPONSE:
- p_sysval=(struct sysval *)&(p_ctlbk->data);
- printk("%s: Recv Sys Validate Resp: Vers=%d,Corr=%d,RC=%d,"
- "WS name=%.8s,Host name=%.8s\n",
- dev->name,
- p_ctlbk->version,
- p_ctlbk->correlator,
- p_ctlbk->rc,
- p_sysval->WS_name,
- p_sysval->host_name);
- switch (p_ctlbk->rc)
- {
- case 0:
- printk(KERN_INFO "%s: CLAW device "
- "%.8s: System validate "
- "completed.\n",
- dev->name, temp_ws_name);
- if (privptr->system_validate_comp == 0)
- claw_strt_conn_req(dev);
- privptr->system_validate_comp=1;
- break;
- case CLAW_RC_NAME_MISMATCH:
- printk(KERN_INFO "%s: Sys Validate "
- "Resp : Host, WS name is "
- "mismatch\n",
- dev->name);
- break;
- case CLAW_RC_WRONG_VERSION:
- printk(KERN_INFO "%s: Sys Validate "
- "Resp : Wrong version\n",
- dev->name);
- break;
- case CLAW_RC_HOST_RCV_TOO_SMALL:
- printk(KERN_INFO "%s: Sys Validate "
- "Resp : bad frame size\n",
- dev->name);
- break;
- default:
- printk(KERN_INFO "%s: Sys Validate "
- "error code=%d \n",
- dev->name, p_ctlbk->rc );
- break;
- }
- break;
+ case SYSTEM_VALIDATE_REQUEST:
+ if (p_ctlbk->version != CLAW_VERSION_ID) {
+ claw_snd_sys_validate_rsp(dev, p_ctlbk,
+ CLAW_RC_WRONG_VERSION);
+ printk("%s: %d is wrong version id. "
+ "Expected %d\n",
+ dev->name, p_ctlbk->version,
+ CLAW_VERSION_ID);
+ }
+ p_sysval = (struct sysval *)&(p_ctlbk->data);
+ printk("%s: Recv Sys Validate Request: "
+ "Vers=%d,link_id=%d,Corr=%d,WS name=%."
+ "8s,Host name=%.8s\n",
+ dev->name, p_ctlbk->version,
+ p_ctlbk->linkid,
+ p_ctlbk->correlator,
+ p_sysval->WS_name,
+ p_sysval->host_name);
+ if (memcmp(temp_host_name, p_sysval->host_name, 8)) {
+ claw_snd_sys_validate_rsp(dev, p_ctlbk,
+ CLAW_RC_NAME_MISMATCH);
+ CLAW_DBF_TEXT(2, setup, "HSTBAD");
+ CLAW_DBF_TEXT_(2, setup, "%s", p_sysval->host_name);
+ CLAW_DBF_TEXT_(2, setup, "%s", temp_host_name);
+ printk(KERN_INFO "%s: Host name mismatch\n",
+ dev->name);
+ printk(KERN_INFO "%s: Received :%s: "
+ "expected :%s: \n",
+ dev->name,
+ p_sysval->host_name,
+ temp_host_name);
+ }
+ if (memcmp(temp_ws_name, p_sysval->WS_name, 8)) {
+ claw_snd_sys_validate_rsp(dev, p_ctlbk,
+ CLAW_RC_NAME_MISMATCH);
+ CLAW_DBF_TEXT(2, setup, "WSNBAD");
+ CLAW_DBF_TEXT_(2, setup, "%s", p_sysval->WS_name);
+ CLAW_DBF_TEXT_(2, setup, "%s", temp_ws_name);
+ printk(KERN_INFO "%s: WS name mismatch\n",
+ dev->name);
+ printk(KERN_INFO "%s: Received :%s: "
+ "expected :%s: \n",
+ dev->name,
+ p_sysval->WS_name,
+ temp_ws_name);
+ }
+ if ((p_sysval->write_frame_size < p_env->write_size) &&
+ (p_env->packing == 0)) {
+ claw_snd_sys_validate_rsp(dev, p_ctlbk,
+ CLAW_RC_HOST_RCV_TOO_SMALL);
+ printk(KERN_INFO "%s: host write size is too "
+ "small\n", dev->name);
+ CLAW_DBF_TEXT(2, setup, "wrtszbad");
+ }
+ if ((p_sysval->read_frame_size < p_env->read_size) &&
+ (p_env->packing == 0)) {
+ claw_snd_sys_validate_rsp(dev, p_ctlbk,
+ CLAW_RC_HOST_RCV_TOO_SMALL);
+ printk(KERN_INFO "%s: host read size is too "
+ "small\n", dev->name);
+ CLAW_DBF_TEXT(2, setup, "rdsizbad");
+ }
+ claw_snd_sys_validate_rsp(dev, p_ctlbk, 0);
+ printk(KERN_INFO "%s: CLAW device %.8s: System validate "
+ "completed.\n", dev->name, temp_ws_name);
+ printk("%s: sys Validate Rsize:%d Wsize:%d\n", dev->name,
+ p_sysval->read_frame_size, p_sysval->write_frame_size);
+ privptr->system_validate_comp = 1;
+ if (strncmp(p_env->api_type, WS_APPL_NAME_PACKED, 6) == 0)
+ p_env->packing = PACKING_ASK;
+ claw_strt_conn_req(dev);
+ break;
+ case SYSTEM_VALIDATE_RESPONSE:
+ p_sysval = (struct sysval *)&(p_ctlbk->data);
+ printk("%s: Recv Sys Validate Resp: Vers=%d,Corr=%d,RC=%d,"
+ "WS name=%.8s,Host name=%.8s\n",
+ dev->name,
+ p_ctlbk->version,
+ p_ctlbk->correlator,
+ p_ctlbk->rc,
+ p_sysval->WS_name,
+ p_sysval->host_name);
+ switch (p_ctlbk->rc) {
+ case 0:
+ printk(KERN_INFO "%s: CLAW device "
+ "%.8s: System validate "
+ "completed.\n",
+ dev->name, temp_ws_name);
+ if (privptr->system_validate_comp == 0)
+ claw_strt_conn_req(dev);
+ privptr->system_validate_comp = 1;
+ break;
+ case CLAW_RC_NAME_MISMATCH:
+ printk(KERN_INFO "%s: Sys Validate "
+ "Resp : Host, WS name is "
+ "mismatch\n",
+ dev->name);
+ break;
+ case CLAW_RC_WRONG_VERSION:
+ printk(KERN_INFO "%s: Sys Validate "
+ "Resp : Wrong version\n",
+ dev->name);
+ break;
+ case CLAW_RC_HOST_RCV_TOO_SMALL:
+ printk(KERN_INFO "%s: Sys Validate "
+ "Resp : bad frame size\n",
+ dev->name);
+ break;
+ default:
+ printk(KERN_INFO "%s: Sys Validate "
+ "error code=%d \n",
+ dev->name, p_ctlbk->rc);
+ break;
+ }
+ break;
- case CONNECTION_REQUEST:
- p_connect=(struct conncmd *)&(p_ctlbk->data);
- printk(KERN_INFO "%s: Recv Conn Req: Vers=%d,link_id=%d,"
- "Corr=%d,HOST appl=%.8s,WS appl=%.8s\n",
- dev->name,
- p_ctlbk->version,
- p_ctlbk->linkid,
- p_ctlbk->correlator,
- p_connect->host_name,
- p_connect->WS_name);
- if (privptr->active_link_ID!=0 ) {
- claw_snd_disc(dev, p_ctlbk);
- printk(KERN_INFO "%s: Conn Req error : "
- "already logical link is active \n",
- dev->name);
- }
- if (p_ctlbk->linkid!=1 ) {
- claw_snd_disc(dev, p_ctlbk);
- printk(KERN_INFO "%s: Conn Req error : "
- "req logical link id is not 1\n",
+ case CONNECTION_REQUEST:
+ p_connect = (struct conncmd *)&(p_ctlbk->data);
+ printk(KERN_INFO "%s: Recv Conn Req: Vers=%d,link_id=%d,"
+ "Corr=%d,HOST appl=%.8s,WS appl=%.8s\n",
+ dev->name,
+ p_ctlbk->version,
+ p_ctlbk->linkid,
+ p_ctlbk->correlator,
+ p_connect->host_name,
+ p_connect->WS_name);
+ if (privptr->active_link_ID != 0) {
+ claw_snd_disc(dev, p_ctlbk);
+ printk(KERN_INFO "%s: Conn Req error : "
+ "already logical link is active \n",
+ dev->name);
+ }
+ if (p_ctlbk->linkid != 1) {
+ claw_snd_disc(dev, p_ctlbk);
+ printk(KERN_INFO "%s: Conn Req error : "
+ "req logical link id is not 1\n",
+ dev->name);
+ }
+ rc = find_link(dev, p_connect->host_name, p_connect->WS_name);
+ if (rc != 0) {
+ claw_snd_disc(dev, p_ctlbk);
+ printk(KERN_INFO "%s: Conn Resp error: "
+ "req appl name does not match\n",
+ dev->name);
+ }
+ claw_send_control(dev,
+ CONNECTION_CONFIRM, p_ctlbk->linkid,
+ p_ctlbk->correlator,
+ 0, p_connect->host_name,
+ p_connect->WS_name);
+ if (p_env->packing == PACKING_ASK) {
+ p_env->packing = PACK_SEND;
+ claw_snd_conn_req(dev, 0);
+ }
+ printk(KERN_INFO "%s: CLAW device %.8s: Connection "
+ "completed link_id=%d.\n",
+ dev->name, temp_ws_name,
+ p_ctlbk->linkid);
+ privptr->active_link_ID = p_ctlbk->linkid;
+ p_ch = &privptr->channel[WRITE];
+ wake_up(&p_ch->wait); /* wake up claw_open ( WRITE) */
+ break;
+ case CONNECTION_RESPONSE:
+ p_connect = (struct conncmd *)&(p_ctlbk->data);
+ printk(KERN_INFO "%s: Revc Conn Resp: Vers=%d,link_id=%d,"
+ "Corr=%d,RC=%d,Host appl=%.8s, WS appl=%.8s\n",
+ dev->name,
+ p_ctlbk->version,
+ p_ctlbk->linkid,
+ p_ctlbk->correlator,
+ p_ctlbk->rc,
+ p_connect->host_name,
+ p_connect->WS_name);
+
+ if (p_ctlbk->rc != 0) {
+ printk(KERN_INFO "%s: Conn Resp error: rc=%d \n",
+ dev->name, p_ctlbk->rc);
+ return 1;
+ }
+ rc = find_link(dev,
+ p_connect->host_name, p_connect->WS_name);
+ if (rc != 0) {
+ claw_snd_disc(dev, p_ctlbk);
+ printk(KERN_INFO "%s: Conn Resp error: "
+ "req appl name does not match\n",
+ dev->name);
+ }
+ /* should be until CONNECTION_CONFIRM */
+ privptr->active_link_ID = -(p_ctlbk->linkid);
+ break;
+ case CONNECTION_CONFIRM:
+ p_connect = (struct conncmd *)&(p_ctlbk->data);
+ printk(KERN_INFO "%s: Recv Conn Confirm:Vers=%d,link_id=%d,"
+ "Corr=%d,Host appl=%.8s,WS appl=%.8s\n",
+ dev->name,
+ p_ctlbk->version,
+ p_ctlbk->linkid,
+ p_ctlbk->correlator,
+ p_connect->host_name,
+ p_connect->WS_name);
+ if (p_ctlbk->linkid == -(privptr->active_link_ID)) {
+ privptr->active_link_ID = p_ctlbk->linkid;
+ if (p_env->packing > PACKING_ASK) {
+ printk(KERN_INFO "%s: Confirmed Now packing\n",
dev->name);
- }
- rc=find_link(dev,
- p_connect->host_name, p_connect->WS_name);
- if (rc!=0) {
- claw_snd_disc(dev, p_ctlbk);
- printk(KERN_INFO "%s: Conn Req error : "
- "req appl name does not match\n",
- dev->name);
- }
- claw_send_control(dev,
- CONNECTION_CONFIRM, p_ctlbk->linkid,
- p_ctlbk->correlator,
- 0, p_connect->host_name,
- p_connect->WS_name);
- if (p_env->packing == PACKING_ASK) {
- printk("%s: Now Pack ask\n",dev->name);
- p_env->packing = PACK_SEND;
- claw_snd_conn_req(dev,0);
- }
- printk(KERN_INFO "%s: CLAW device %.8s: Connection "
- "completed link_id=%d.\n",
- dev->name, temp_ws_name,
- p_ctlbk->linkid);
- privptr->active_link_ID=p_ctlbk->linkid;
- p_ch=&privptr->channel[WRITE];
- wake_up(&p_ch->wait); /* wake up claw_open ( WRITE) */
- break;
- case CONNECTION_RESPONSE:
- p_connect=(struct conncmd *)&(p_ctlbk->data);
- printk(KERN_INFO "%s: Revc Conn Resp: Vers=%d,link_id=%d,"
- "Corr=%d,RC=%d,Host appl=%.8s, WS appl=%.8s\n",
- dev->name,
- p_ctlbk->version,
- p_ctlbk->linkid,
- p_ctlbk->correlator,
- p_ctlbk->rc,
- p_connect->host_name,
- p_connect->WS_name);
-
- if (p_ctlbk->rc !=0 ) {
- printk(KERN_INFO "%s: Conn Resp error: rc=%d \n",
- dev->name, p_ctlbk->rc);
- return 1;
- }
- rc=find_link(dev,
- p_connect->host_name, p_connect->WS_name);
- if (rc!=0) {
- claw_snd_disc(dev, p_ctlbk);
- printk(KERN_INFO "%s: Conn Resp error: "
- "req appl name does not match\n",
- dev->name);
- }
- /* should be until CONNECTION_CONFIRM */
- privptr->active_link_ID = - (p_ctlbk->linkid);
- break;
- case CONNECTION_CONFIRM:
- p_connect=(struct conncmd *)&(p_ctlbk->data);
- printk(KERN_INFO "%s: Recv Conn Confirm:Vers=%d,link_id=%d,"
- "Corr=%d,Host appl=%.8s,WS appl=%.8s\n",
- dev->name,
- p_ctlbk->version,
- p_ctlbk->linkid,
- p_ctlbk->correlator,
- p_connect->host_name,
- p_connect->WS_name);
- if (p_ctlbk->linkid== -(privptr->active_link_ID)) {
- privptr->active_link_ID=p_ctlbk->linkid;
- if (p_env->packing > PACKING_ASK) {
- printk(KERN_INFO "%s: Confirmed Now packing\n",dev->name);
- p_env->packing = DO_PACKED;
- }
- p_ch=&privptr->channel[WRITE];
- wake_up(&p_ch->wait);
- }
- else {
- printk(KERN_INFO "%s: Conn confirm: "
- "unexpected linkid=%d \n",
- dev->name, p_ctlbk->linkid);
- claw_snd_disc(dev, p_ctlbk);
- }
- break;
- case DISCONNECT:
- printk(KERN_INFO "%s: Disconnect: "
- "Vers=%d,link_id=%d,Corr=%d\n",
- dev->name, p_ctlbk->version,
- p_ctlbk->linkid, p_ctlbk->correlator);
- if ((p_ctlbk->linkid == 2) &&
- (p_env->packing == PACK_SEND)) {
- privptr->active_link_ID = 1;
p_env->packing = DO_PACKED;
}
- else
- privptr->active_link_ID=0;
- break;
- case CLAW_ERROR:
- printk(KERN_INFO "%s: CLAW ERROR detected\n",
- dev->name);
- break;
- default:
- printk(KERN_INFO "%s: Unexpected command code=%d \n",
- dev->name, p_ctlbk->command);
- break;
+ p_ch = &privptr->channel[WRITE];
+ wake_up(&p_ch->wait);
+ } else {
+ printk(KERN_INFO "%s: Conn confirm: "
+ "unexpected linkid=%d \n",
+ dev->name, p_ctlbk->linkid);
+ claw_snd_disc(dev, p_ctlbk);
+ }
+ break;
+ case DISCONNECT:
+ printk(KERN_INFO "%s: Disconnect: "
+ "Vers=%d,link_id=%d,Corr=%d\n",
+ dev->name, p_ctlbk->version,
+ p_ctlbk->linkid, p_ctlbk->correlator);
+ if ((p_ctlbk->linkid == 2) &&
+ (p_env->packing == PACK_SEND)) {
+ privptr->active_link_ID = 1;
+ p_env->packing = DO_PACKED;
+ } else
+ privptr->active_link_ID = 0;
+ break;
+ case CLAW_ERROR:
+ printk(KERN_INFO "%s: CLAW ERROR detected\n",
+ dev->name);
+ break;
+ default:
+ printk(KERN_INFO "%s: Unexpected command code=%d \n",
+ dev->name, p_ctlbk->command);
+ break;
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s() exit on line %d, rc = 0\n",
- dev->name,__func__,__LINE__);
-#endif
-
return 0;
} /* end of claw_process_control */
@@ -3079,19 +2275,8 @@ claw_send_control(struct net_device *dev, __u8 type, __u8 link,
struct conncmd *p_connect;
struct sk_buff *skb;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s > enter \n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(2,setup,"sndcntl");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: Sending Control Packet \n",dev->name);
- printk(KERN_INFO "%s: variable type = 0x%X, link = "
- "%d, correlator = %d, rc = %d\n",
- dev->name,type, link, correlator, rc);
- printk(KERN_INFO "%s: variable local_name = %s, "
- "remote_name = %s\n",dev->name, local_name, remote_name);
-#endif
- privptr=dev->priv;
+ CLAW_DBF_TEXT(2, setup, "sndcntl");
+ privptr = dev->ml_priv;
p_ctl=(struct clawctl *)&privptr->ctl_bk;
p_ctl->command=type;
@@ -3112,7 +2297,7 @@ claw_send_control(struct net_device *dev, __u8 type, __u8 link,
p_sysval->read_frame_size=DEF_PACK_BUFSIZE;
p_sysval->write_frame_size=DEF_PACK_BUFSIZE;
} else {
- /* how big is the piggest group of packets */
+ /* how big is the biggest group of packets */
p_sysval->read_frame_size=privptr->p_env->read_size;
p_sysval->write_frame_size=privptr->p_env->write_size;
}
@@ -3142,29 +2327,14 @@ claw_send_control(struct net_device *dev, __u8 type, __u8 link,
skb = dev_alloc_skb(sizeof(struct clawctl));
if (!skb) {
- printk( "%s:%s low on mem, returning...\n",
- dev->name,__func__);
-#ifdef DEBUG
- printk(KERN_INFO "%s:%s Exit, rc = ENOMEM\n",
- dev->name,__func__);
-#endif
return -ENOMEM;
}
memcpy(skb_put(skb, sizeof(struct clawctl)),
p_ctl, sizeof(struct clawctl));
-#ifdef IOTRACE
- printk(KERN_INFO "%s: outbnd claw cntl data \n",dev->name);
- dumpit((char *)p_ctl,sizeof(struct clawctl));
-#endif
if (privptr->p_env->packing >= PACK_SEND)
claw_hw_tx(skb, dev, 1);
else
claw_hw_tx(skb, dev, 0);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
-
return 0;
} /* end of claw_send_control */
@@ -3176,25 +2346,14 @@ static int
claw_snd_conn_req(struct net_device *dev, __u8 link)
{
int rc;
- struct claw_privbk *privptr=dev->priv;
+ struct claw_privbk *privptr = dev->ml_priv;
struct clawctl *p_ctl;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(2,setup,"snd_conn");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable link = %X, dev =\n",dev->name, link);
- dumpit((char *) dev, sizeof(struct net_device));
-#endif
+ CLAW_DBF_TEXT(2, setup, "snd_conn");
rc = 1;
p_ctl=(struct clawctl *)&privptr->ctl_bk;
p_ctl->linkid = link;
if ( privptr->system_validate_comp==0x00 ) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc = 1\n",
- dev->name,__func__,__LINE__);
-#endif
return rc;
}
if (privptr->p_env->packing == PACKING_ASK )
@@ -3207,10 +2366,6 @@ claw_snd_conn_req(struct net_device *dev, __u8 link)
if (privptr->p_env->packing == 0)
rc=claw_send_control(dev, CONNECTION_REQUEST,0,0,0,
HOST_APPL_NAME, privptr->p_env->api_type);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
- dev->name,__func__,__LINE__, rc);
-#endif
return rc;
} /* end of claw_snd_conn_req */
@@ -3227,25 +2382,12 @@ claw_snd_disc(struct net_device *dev, struct clawctl * p_ctl)
int rc;
struct conncmd * p_connect;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(2,setup,"snd_dsc");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable p_ctl",dev->name);
- dumpit((char *) p_ctl, sizeof(struct clawctl));
-#endif
+ CLAW_DBF_TEXT(2, setup, "snd_dsc");
p_connect=(struct conncmd *)&p_ctl->data;
rc=claw_send_control(dev, DISCONNECT, p_ctl->linkid,
p_ctl->correlator, 0,
p_connect->host_name, p_connect->WS_name);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
- dev->name,__func__, __LINE__, rc);
-#endif
return rc;
} /* end of claw_snd_disc */
@@ -3263,19 +2405,8 @@ claw_snd_sys_validate_rsp(struct net_device *dev,
struct claw_privbk *privptr;
int rc;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",
- dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(2,setup,"chkresp");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable return_code = %d, dev =\n",
- dev->name, return_code);
- dumpit((char *) dev, sizeof(struct net_device));
- printk(KERN_INFO "%s: variable p_ctl =\n",dev->name);
- dumpit((char *) p_ctl, sizeof(struct clawctl));
-#endif
- privptr = dev->priv;
+ CLAW_DBF_TEXT(2, setup, "chkresp");
+ privptr = dev->ml_priv;
p_env=privptr->p_env;
rc=claw_send_control(dev, SYSTEM_VALIDATE_RESPONSE,
p_ctl->linkid,
@@ -3283,10 +2414,6 @@ claw_snd_sys_validate_rsp(struct net_device *dev,
return_code,
p_env->host_name,
p_env->adapter_name );
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
- dev->name,__func__,__LINE__, rc);
-#endif
return rc;
} /* end of claw_snd_sys_validate_rsp */
@@ -3300,19 +2427,8 @@ claw_strt_conn_req(struct net_device *dev )
{
int rc;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(2,setup,"conn_req");
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: variable dev =\n",dev->name);
- dumpit((char *) dev, sizeof(struct net_device));
-#endif
+ CLAW_DBF_TEXT(2, setup, "conn_req");
rc=claw_snd_conn_req(dev, 1);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
- dev->name,__func__,__LINE__, rc);
-#endif
return rc;
} /* end of claw_strt_conn_req */
@@ -3326,15 +2442,9 @@ static struct
net_device_stats *claw_stats(struct net_device *dev)
{
struct claw_privbk *privptr;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(4,trace,"stats");
- privptr = dev->priv;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
+
+ CLAW_DBF_TEXT(4, trace, "stats");
+ privptr = dev->ml_priv;
return &privptr->stats;
} /* end of claw_stats */
@@ -3355,36 +2465,28 @@ unpack_read(struct net_device *dev )
struct clawph *p_packh;
void *p_packd;
struct clawctl *p_ctlrec=NULL;
+ struct device *p_dev;
__u32 len_of_data;
__u32 pack_off;
__u8 link_num;
__u8 mtc_this_frm=0;
__u32 bytes_to_mov;
- struct chbk *p_ch = NULL;
int i=0;
int p=0;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s enter \n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(4,trace,"unpkread");
+ CLAW_DBF_TEXT(4, trace, "unpkread");
p_first_ccw=NULL;
p_last_ccw=NULL;
p_packh=NULL;
p_packd=NULL;
- privptr=dev->priv;
+ privptr = dev->ml_priv;
+
+ p_dev = &privptr->channel[READ].cdev->dev;
p_env = privptr->p_env;
p_this_ccw=privptr->p_read_active_first;
i=0;
while (p_this_ccw!=NULL && p_this_ccw->header.flag!=CLAW_PENDING) {
-#ifdef IOTRACE
- printk(KERN_INFO "%s p_this_ccw \n",dev->name);
- dumpit((char*)p_this_ccw, sizeof(struct ccwbk));
- printk(KERN_INFO "%s Inbound p_this_ccw->p_buffer(64)"
- " pk=%d \n",dev->name,p_env->packing);
- dumpit((char *)p_this_ccw->p_buffer, 64 );
-#endif
pack_off = 0;
p = 0;
p_this_ccw->header.flag=CLAW_PENDING;
@@ -3406,10 +2508,6 @@ unpack_read(struct net_device *dev )
else
link_num=p_this_ccw->header.opcode / 8;
if ((p_this_ccw->header.opcode & MORE_to_COME_FLAG)!=0) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s > More_to_come is ON\n",
- dev->name,__func__);
-#endif
mtc_this_frm=1;
if (p_this_ccw->header.length!=
privptr->p_env->read_size ) {
@@ -3432,22 +2530,12 @@ unpack_read(struct net_device *dev )
privptr->mtc_skipping=0; /* Ok, the end */
privptr->mtc_logical_link=-1;
}
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s goto next "
- "frame from MoretoComeSkip \n",
- dev->name,__func__);
-#endif
goto NextFrame;
}
if (link_num==0) {
claw_process_control(dev, p_this_ccw);
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s goto next "
- "frame from claw_process_control \n",
- dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(4,trace,"UnpkCntl");
+ CLAW_DBF_TEXT(4, trace, "UnpkCntl");
goto NextFrame;
}
unpack_next:
@@ -3466,10 +2554,6 @@ unpack_next:
bytes_to_mov=p_this_ccw->header.length;
}
if (privptr->mtc_logical_link<0) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s mtc_logical_link < 0 \n",
- dev->name,__func__);
-#endif
/*
* if More-To-Come is set in this frame then we don't know
@@ -3483,15 +2567,6 @@ unpack_next:
if (bytes_to_mov > (MAX_ENVELOPE_SIZE- privptr->mtc_offset) ) {
/* error */
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s > goto next "
- "frame from MoretoComeSkip \n",
- dev->name,
- __func__);
- printk(KERN_INFO " bytes_to_mov %d > (MAX_ENVELOPE_"
- "SIZE-privptr->mtc_offset %d)\n",
- bytes_to_mov,(MAX_ENVELOPE_SIZE- privptr->mtc_offset));
-#endif
privptr->stats.rx_frame_errors++;
goto NextFrame;
}
@@ -3503,16 +2578,6 @@ unpack_next:
memcpy( privptr->p_mtc_envelope+ privptr->mtc_offset,
p_this_ccw->p_buffer, bytes_to_mov);
}
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() received data \n",
- dev->name,__func__);
- if (p_env->packing == DO_PACKED)
- dumpit((char *)p_packd+sizeof(struct clawph),32);
- else
- dumpit((char *)p_this_ccw->p_buffer, 32);
- printk(KERN_INFO "%s: %s() bytelength %d \n",
- dev->name,__func__,bytes_to_mov);
-#endif
if (mtc_this_frm==0) {
len_of_data=privptr->mtc_offset+bytes_to_mov;
skb=dev_alloc_skb(len_of_data);
@@ -3527,11 +2592,6 @@ unpack_next:
privptr->stats.rx_packets++;
privptr->stats.rx_bytes+=len_of_data;
netif_rx(skb);
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: %s() netif_"
- "rx(skb) completed \n",
- dev->name,__func__);
-#endif
}
else {
privptr->stats.rx_dropped++;
@@ -3568,28 +2628,14 @@ NextFrame:
* chain to next block on active read queue
*/
p_this_ccw = privptr->p_read_active_first;
- CLAW_DBF_TEXT_(4,trace,"rxpkt %d",p);
+ CLAW_DBF_TEXT_(4, trace, "rxpkt %d", p);
} /* end of while */
/* check validity */
-#ifdef IOTRACE
- printk(KERN_INFO "%s:%s processed frame is %d \n",
- dev->name,__func__,i);
- printk(KERN_INFO "%s:%s F:%lx L:%lx\n",
- dev->name,
- __func__,
- (unsigned long)p_first_ccw,
- (unsigned long)p_last_ccw);
-#endif
- CLAW_DBF_TEXT_(4,trace,"rxfrm %d",i);
+ CLAW_DBF_TEXT_(4, trace, "rxfrm %d", i);
add_claw_reads(dev, p_first_ccw, p_last_ccw);
- p_ch=&privptr->channel[READ];
claw_strt_read(dev, LOCK_YES);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s: %s exit on line %d\n",
- dev->name, __func__, __LINE__);
-#endif
return;
} /* end of unpack_read */
@@ -3603,18 +2649,13 @@ claw_strt_read (struct net_device *dev, int lock )
int rc = 0;
__u32 parm;
unsigned long saveflags = 0;
- struct claw_privbk *privptr=dev->priv;
+ struct claw_privbk *privptr = dev->ml_priv;
struct ccwbk*p_ccwbk;
struct chbk *p_ch;
struct clawh *p_clawh;
p_ch=&privptr->channel[READ];
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__);
- printk(KERN_INFO "%s: variable lock = %d, dev =\n",dev->name, lock);
- dumpit((char *) dev, sizeof(struct net_device));
-#endif
- CLAW_DBF_TEXT(4,trace,"StRdNter");
+ CLAW_DBF_TEXT(4, trace, "StRdNter");
p_clawh=(struct clawh *)privptr->p_claw_signal_blk;
p_clawh->flag=CLAW_IDLE; /* 0x00 */
@@ -3624,21 +2665,11 @@ claw_strt_read (struct net_device *dev, int lock )
privptr->p_read_active_first->header.flag!=CLAW_PENDING )) {
p_clawh->flag=CLAW_BUSY; /* 0xff */
}
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s state-%02x\n" ,
- dev->name,__func__, p_ch->claw_state);
-#endif
if (lock==LOCK_YES) {
spin_lock_irqsave(get_ccwdev_lock(p_ch->cdev), saveflags);
}
if (test_and_set_bit(0, (void *)&p_ch->IO_active) == 0) {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: HOT READ started in %s\n" ,
- dev->name,__func__);
- p_clawh=(struct clawh *)privptr->p_claw_signal_blk;
- dumpit((char *)&p_clawh->flag , 1);
-#endif
- CLAW_DBF_TEXT(4,trace,"HotRead");
+ CLAW_DBF_TEXT(4, trace, "HotRead");
p_ccwbk=privptr->p_read_active_first;
parm = (unsigned long) p_ch;
rc = ccw_device_start (p_ch->cdev, &p_ccwbk->read, parm,
@@ -3648,21 +2679,13 @@ claw_strt_read (struct net_device *dev, int lock )
}
}
else {
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s: No READ started by %s() In progress\n" ,
- dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(2,trace,"ReadAct");
+ CLAW_DBF_TEXT(2, trace, "ReadAct");
}
if (lock==LOCK_YES) {
spin_unlock_irqrestore(get_ccwdev_lock(p_ch->cdev), saveflags);
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
- CLAW_DBF_TEXT(4,trace,"StRdExit");
+ CLAW_DBF_TEXT(4, trace, "StRdExit");
return;
} /* end of claw_strt_read */
@@ -3680,38 +2703,23 @@ claw_strt_out_IO( struct net_device *dev )
struct chbk *p_ch;
struct ccwbk *p_first_ccw;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__);
-#endif
if (!dev) {
return;
}
- privptr=(struct claw_privbk *)dev->priv;
+ privptr = (struct claw_privbk *)dev->ml_priv;
p_ch=&privptr->channel[WRITE];
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s state-%02x\n" ,
- dev->name,__func__,p_ch->claw_state);
-#endif
- CLAW_DBF_TEXT(4,trace,"strt_io");
+ CLAW_DBF_TEXT(4, trace, "strt_io");
p_first_ccw=privptr->p_write_active_first;
if (p_ch->claw_state == CLAW_STOP)
return;
if (p_first_ccw == NULL) {
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
return;
}
if (test_and_set_bit(0, (void *)&p_ch->IO_active) == 0) {
parm = (unsigned long) p_ch;
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s do_io \n" ,dev->name,__func__);
- dumpit((char *)p_first_ccw, sizeof(struct ccwbk));
-#endif
- CLAW_DBF_TEXT(2,trace,"StWrtIO");
+ CLAW_DBF_TEXT(2, trace, "StWrtIO");
rc = ccw_device_start (p_ch->cdev,&p_first_ccw->write, parm,
0xff, 0);
if (rc != 0) {
@@ -3719,11 +2727,6 @@ claw_strt_out_IO( struct net_device *dev )
}
}
dev->trans_start = jiffies;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- dev->name,__func__,__LINE__);
-#endif
-
return;
} /* end of claw_strt_out_IO */
@@ -3736,37 +2739,16 @@ static void
claw_free_wrt_buf( struct net_device *dev )
{
- struct claw_privbk *privptr=(struct claw_privbk *)dev->priv;
+ struct claw_privbk *privptr = (struct claw_privbk *)dev->ml_priv;
struct ccwbk*p_first_ccw;
struct ccwbk*p_last_ccw;
struct ccwbk*p_this_ccw;
struct ccwbk*p_next_ccw;
-#ifdef IOTRACE
- struct ccwbk*p_buf;
-#endif
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__);
- printk(KERN_INFO "%s: free count = %d variable dev =\n",
- dev->name,privptr->write_free_count);
-#endif
- CLAW_DBF_TEXT(4,trace,"freewrtb");
+
+ CLAW_DBF_TEXT(4, trace, "freewrtb");
/* scan the write queue to free any completed write packets */
p_first_ccw=NULL;
p_last_ccw=NULL;
-#ifdef IOTRACE
- printk(KERN_INFO "%s: Dump current CCW chain \n",dev->name );
- p_buf=privptr->p_write_active_first;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
- if (p_buf==NULL) {
- printk(KERN_INFO "%s: privptr->p_write_"
- "active_first==NULL\n",dev->name );
- }
- p_buf=(struct ccwbk*)privptr->p_end_ccw;
- dumpit((char *)p_buf, sizeof(struct endccw));
-#endif
p_this_ccw=privptr->p_write_active_first;
while ( (p_this_ccw!=NULL) && (p_this_ccw->header.flag!=CLAW_PENDING))
{
@@ -3796,31 +2778,8 @@ claw_free_wrt_buf( struct net_device *dev )
/* whole chain removed? */
if (privptr->p_write_active_first==NULL) {
privptr->p_write_active_last=NULL;
-#ifdef DEBUGMSG
- printk(KERN_INFO "%s:%s p_write_"
- "active_first==NULL\n",dev->name,__func__);
-#endif
}
-#ifdef IOTRACE
- printk(KERN_INFO "%s: Dump arranged CCW chain \n",dev->name );
- p_buf=privptr->p_write_active_first;
- while (p_buf!=NULL) {
- dumpit((char *)p_buf, sizeof(struct ccwbk));
- p_buf=p_buf->next;
- }
- if (p_buf==NULL) {
- printk(KERN_INFO "%s: privptr->p_write_active_"
- "first==NULL\n",dev->name );
- }
- p_buf=(struct ccwbk*)privptr->p_end_ccw;
- dumpit((char *)p_buf, sizeof(struct endccw));
-#endif
-
- CLAW_DBF_TEXT_(4,trace,"FWC=%d",privptr->write_free_count);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d free_count =%d\n",
- dev->name,__func__, __LINE__,privptr->write_free_count);
-#endif
+ CLAW_DBF_TEXT_(4, trace, "FWC=%d", privptr->write_free_count);
return;
}
@@ -3832,30 +2791,24 @@ static void
claw_free_netdevice(struct net_device * dev, int free_dev)
{
struct claw_privbk *privptr;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(2,setup,"free_dev");
+ CLAW_DBF_TEXT(2, setup, "free_dev");
if (!dev)
return;
- CLAW_DBF_TEXT_(2,setup,"%s",dev->name);
- privptr = dev->priv;
+ CLAW_DBF_TEXT_(2, setup, "%s", dev->name);
+ privptr = dev->ml_priv;
if (dev->flags & IFF_RUNNING)
claw_release(dev);
if (privptr) {
privptr->channel[READ].ndev = NULL; /* say it's free */
}
- dev->priv=NULL;
+ dev->ml_priv = NULL;
#ifdef MODULE
if (free_dev) {
free_netdev(dev);
}
#endif
- CLAW_DBF_TEXT(2,setup,"feee_ok");
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit\n",dev->name,__func__);
-#endif
+ CLAW_DBF_TEXT(2, setup, "free_ok");
}
/**
@@ -3866,17 +2819,8 @@ claw_free_netdevice(struct net_device * dev, int free_dev)
static void
claw_init_netdevice(struct net_device * dev)
{
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(2,setup,"init_dev");
- CLAW_DBF_TEXT_(2,setup,"%s",dev->name);
- if (!dev) {
- printk(KERN_WARNING "claw:%s BAD Device exit line %d\n",
- __func__,__LINE__);
- CLAW_DBF_TEXT(2,setup,"baddev");
- return;
- }
+ CLAW_DBF_TEXT(2, setup, "init_dev");
+ CLAW_DBF_TEXT_(2, setup, "%s", dev->name);
dev->mtu = CLAW_DEFAULT_MTU_SIZE;
dev->hard_start_xmit = claw_tx;
dev->open = claw_open;
@@ -3888,10 +2832,7 @@ claw_init_netdevice(struct net_device * dev)
dev->type = ARPHRD_SLIP;
dev->tx_queue_len = 1300;
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit\n",dev->name,__func__);
-#endif
- CLAW_DBF_TEXT(2,setup,"initok");
+ CLAW_DBF_TEXT(2, setup, "initok");
return;
}
@@ -3908,29 +2849,16 @@ add_channel(struct ccw_device *cdev,int i,struct claw_privbk *privptr)
struct chbk *p_ch;
struct ccw_dev_id dev_id;
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",cdev->dev.bus_id,__func__);
-#endif
- CLAW_DBF_TEXT_(2,setup,"%s",cdev->dev.bus_id);
+ CLAW_DBF_TEXT_(2, setup, "%s", dev_name(&cdev->dev));
privptr->channel[i].flag = i+1; /* Read is 1 Write is 2 */
p_ch = &privptr->channel[i];
p_ch->cdev = cdev;
- snprintf(p_ch->id, CLAW_ID_SIZE, "cl-%s", cdev->dev.bus_id);
+ snprintf(p_ch->id, CLAW_ID_SIZE, "cl-%s", dev_name(&cdev->dev));
ccw_device_get_id(cdev, &dev_id);
p_ch->devno = dev_id.devno;
if ((p_ch->irb = kzalloc(sizeof (struct irb),GFP_KERNEL)) == NULL) {
- printk(KERN_WARNING "%s Out of memory in %s for irb\n",
- p_ch->id,__func__);
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- p_ch->id,__func__,__LINE__);
-#endif
return -ENOMEM;
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Exit on line %d\n",
- cdev->dev.bus_id,__func__,__LINE__);
-#endif
return 0;
}
@@ -3952,9 +2880,9 @@ claw_new_device(struct ccwgroup_device *cgdev)
int ret;
struct ccw_dev_id dev_id;
- pr_debug("%s() called\n", __func__);
- printk(KERN_INFO "claw: add for %s\n",cgdev->cdev[READ]->dev.bus_id);
- CLAW_DBF_TEXT(2,setup,"new_dev");
+ printk(KERN_INFO "claw: add for %s\n",
+ dev_name(&cgdev->cdev[READ]->dev));
+ CLAW_DBF_TEXT(2, setup, "new_dev");
privptr = cgdev->dev.driver_data;
cgdev->cdev[READ]->dev.driver_data = privptr;
cgdev->cdev[WRITE]->dev.driver_data = privptr;
@@ -3969,23 +2897,24 @@ claw_new_device(struct ccwgroup_device *cgdev)
if (ret == 0)
ret = add_channel(cgdev->cdev[1],1,privptr);
if (ret != 0) {
- printk(KERN_WARNING
- "add channel failed "
- "with ret = %d\n", ret);
- goto out;
+ printk(KERN_WARNING
+ "add channel failed with ret = %d\n", ret);
+ goto out;
}
ret = ccw_device_set_online(cgdev->cdev[READ]);
if (ret != 0) {
printk(KERN_WARNING
- "claw: ccw_device_set_online %s READ failed "
- "with ret = %d\n",cgdev->cdev[READ]->dev.bus_id,ret);
+ "claw: ccw_device_set_online %s READ failed "
+ "with ret = %d\n", dev_name(&cgdev->cdev[READ]->dev),
+ ret);
goto out;
}
ret = ccw_device_set_online(cgdev->cdev[WRITE]);
if (ret != 0) {
printk(KERN_WARNING
- "claw: ccw_device_set_online %s WRITE failed "
- "with ret = %d\n",cgdev->cdev[WRITE]->dev.bus_id, ret);
+ "claw: ccw_device_set_online %s WRITE failed "
+ "with ret = %d\n", dev_name(&cgdev->cdev[WRITE]->dev),
+ ret);
goto out;
}
dev = alloc_netdev(0,"claw%d",claw_init_netdevice);
@@ -3993,7 +2922,7 @@ claw_new_device(struct ccwgroup_device *cgdev)
printk(KERN_WARNING "%s:alloc_netdev failed\n",__func__);
goto out;
}
- dev->priv = privptr;
+ dev->ml_priv = privptr;
cgdev->dev.driver_data = privptr;
cgdev->cdev[READ]->dev.driver_data = privptr;
cgdev->cdev[WRITE]->dev.driver_data = privptr;
@@ -4001,18 +2930,16 @@ claw_new_device(struct ccwgroup_device *cgdev)
SET_NETDEV_DEV(dev, &cgdev->dev);
if (register_netdev(dev) != 0) {
claw_free_netdevice(dev, 1);
- CLAW_DBF_TEXT(2,trace,"regfail");
+ CLAW_DBF_TEXT(2, trace, "regfail");
goto out;
}
dev->flags &=~IFF_RUNNING;
if (privptr->buffs_alloc == 0) {
ret=init_ccw_bk(dev);
if (ret !=0) {
- printk(KERN_WARNING
- "claw: init_ccw_bk failed with ret=%d\n", ret);
unregister_netdev(dev);
claw_free_netdevice(dev,1);
- CLAW_DBF_TEXT(2,trace,"ccwmem");
+ CLAW_DBF_TEXT(2, trace, "ccwmem");
goto out;
}
}
@@ -4034,7 +2961,6 @@ claw_new_device(struct ccwgroup_device *cgdev)
out:
ccw_device_set_offline(cgdev->cdev[1]);
ccw_device_set_offline(cgdev->cdev[0]);
-
return -ENODEV;
}
@@ -4043,8 +2969,7 @@ claw_purge_skb_queue(struct sk_buff_head *q)
{
struct sk_buff *skb;
- CLAW_DBF_TEXT(4,trace,"purgque");
-
+ CLAW_DBF_TEXT(4, trace, "purgque");
while ((skb = skb_dequeue(q))) {
atomic_dec(&skb->users);
dev_kfree_skb_any(skb);
@@ -4065,8 +2990,7 @@ claw_shutdown_device(struct ccwgroup_device *cgdev)
struct net_device *ndev;
int ret;
- pr_debug("%s() called\n", __func__);
- CLAW_DBF_TEXT_(2,setup,"%s",cgdev->dev.bus_id);
+ CLAW_DBF_TEXT_(2, setup, "%s", dev_name(&cgdev->dev));
priv = cgdev->dev.driver_data;
if (!priv)
return -ENODEV;
@@ -4079,7 +3003,7 @@ claw_shutdown_device(struct ccwgroup_device *cgdev)
ret = claw_release(ndev);
ndev->flags &=~IFF_RUNNING;
unregister_netdev(ndev);
- ndev->priv = NULL; /* cgdev data, not ndev's to free */
+ ndev->ml_priv = NULL; /* cgdev data, not ndev's to free */
claw_free_netdevice(ndev, 1);
priv->channel[READ].ndev = NULL;
priv->channel[WRITE].ndev = NULL;
@@ -4095,15 +3019,12 @@ claw_remove_device(struct ccwgroup_device *cgdev)
{
struct claw_privbk *priv;
- pr_debug("%s() called\n", __func__);
- CLAW_DBF_TEXT_(2,setup,"%s",cgdev->dev.bus_id);
+ BUG_ON(!cgdev);
+ CLAW_DBF_TEXT_(2, setup, "%s", dev_name(&cgdev->dev));
priv = cgdev->dev.driver_data;
- if (!priv) {
- printk(KERN_WARNING "claw: %s() no Priv exiting\n",__func__);
- return;
- }
+ BUG_ON(!priv);
printk(KERN_INFO "claw: %s() called %s will be removed.\n",
- __func__,cgdev->cdev[0]->dev.bus_id);
+ __func__, dev_name(&cgdev->cdev[0]->dev));
if (cgdev->state == CCWGROUP_ONLINE)
claw_shutdown_device(cgdev);
claw_remove_files(&cgdev->dev);
@@ -4120,6 +3041,8 @@ claw_remove_device(struct ccwgroup_device *cgdev)
cgdev->cdev[READ]->dev.driver_data = NULL;
cgdev->cdev[WRITE]->dev.driver_data = NULL;
put_device(&cgdev->dev);
+
+ return;
}
@@ -4155,8 +3078,8 @@ claw_hname_write(struct device *dev, struct device_attribute *attr, const char *
strncpy(p_env->host_name,buf, count);
p_env->host_name[count-1] = 0x20; /* clear extra 0x0a */
p_env->host_name[MAX_NAME_LEN] = 0x00;
- CLAW_DBF_TEXT(2,setup,"HstnSet");
- CLAW_DBF_TEXT_(2,setup,"%s",p_env->host_name);
+ CLAW_DBF_TEXT(2, setup, "HstnSet");
+ CLAW_DBF_TEXT_(2, setup, "%s", p_env->host_name);
return count;
}
@@ -4173,7 +3096,7 @@ claw_adname_show(struct device *dev, struct device_attribute *attr, char *buf)
if (!priv)
return -ENODEV;
p_env = priv->p_env;
- return sprintf(buf, "%s\n",p_env->adapter_name);
+ return sprintf(buf, "%s\n", p_env->adapter_name);
}
static ssize_t
@@ -4192,8 +3115,8 @@ claw_adname_write(struct device *dev, struct device_attribute *attr, const char
strncpy(p_env->adapter_name,buf, count);
p_env->adapter_name[count-1] = 0x20; /* clear extra 0x0a */
p_env->adapter_name[MAX_NAME_LEN] = 0x00;
- CLAW_DBF_TEXT(2,setup,"AdnSet");
- CLAW_DBF_TEXT_(2,setup,"%s",p_env->adapter_name);
+ CLAW_DBF_TEXT(2, setup, "AdnSet");
+ CLAW_DBF_TEXT_(2, setup, "%s", p_env->adapter_name);
return count;
}
@@ -4234,15 +3157,15 @@ claw_apname_write(struct device *dev, struct device_attribute *attr, const char
p_env->read_size=DEF_PACK_BUFSIZE;
p_env->write_size=DEF_PACK_BUFSIZE;
p_env->packing=PACKING_ASK;
- CLAW_DBF_TEXT(2,setup,"PACKING");
+ CLAW_DBF_TEXT(2, setup, "PACKING");
}
else {
p_env->packing=0;
p_env->read_size=CLAW_FRAME_SIZE;
p_env->write_size=CLAW_FRAME_SIZE;
- CLAW_DBF_TEXT(2,setup,"ApiSet");
+ CLAW_DBF_TEXT(2, setup, "ApiSet");
}
- CLAW_DBF_TEXT_(2,setup,"%s",p_env->api_type);
+ CLAW_DBF_TEXT_(2, setup, "%s", p_env->api_type);
return count;
}
@@ -4282,8 +3205,8 @@ claw_wbuff_write(struct device *dev, struct device_attribute *attr, const char *
if ((nnn > max ) || (nnn < 2))
return -EINVAL;
p_env->write_buffers = nnn;
- CLAW_DBF_TEXT(2,setup,"Wbufset");
- CLAW_DBF_TEXT_(2,setup,"WB=%d",p_env->write_buffers);
+ CLAW_DBF_TEXT(2, setup, "Wbufset");
+ CLAW_DBF_TEXT_(2, setup, "WB=%d", p_env->write_buffers);
return count;
}
@@ -4323,8 +3246,8 @@ claw_rbuff_write(struct device *dev, struct device_attribute *attr, const char *
if ((nnn > max ) || (nnn < 2))
return -EINVAL;
p_env->read_buffers = nnn;
- CLAW_DBF_TEXT(2,setup,"Rbufset");
- CLAW_DBF_TEXT_(2,setup,"RB=%d",p_env->read_buffers);
+ CLAW_DBF_TEXT(2, setup, "Rbufset");
+ CLAW_DBF_TEXT_(2, setup, "RB=%d", p_env->read_buffers);
return count;
}
@@ -4346,16 +3269,14 @@ static struct attribute_group claw_attr_group = {
static int
claw_add_files(struct device *dev)
{
- pr_debug("%s() called\n", __func__);
- CLAW_DBF_TEXT(2,setup,"add_file");
+ CLAW_DBF_TEXT(2, setup, "add_file");
return sysfs_create_group(&dev->kobj, &claw_attr_group);
}
static void
claw_remove_files(struct device *dev)
{
- pr_debug("%s() called\n", __func__);
- CLAW_DBF_TEXT(2,setup,"rem_file");
+ CLAW_DBF_TEXT(2, setup, "rem_file");
sysfs_remove_group(&dev->kobj, &claw_attr_group);
}
@@ -4384,35 +3305,27 @@ claw_init(void)
int ret = 0;
printk(KERN_INFO "claw: starting driver\n");
-#ifdef FUNCTRACE
- printk(KERN_INFO "claw: %s() enter \n",__func__);
-#endif
ret = claw_register_debug_facility();
if (ret) {
printk(KERN_WARNING "claw: %s() debug_register failed %d\n",
__func__,ret);
return ret;
}
- CLAW_DBF_TEXT(2,setup,"init_mod");
+ CLAW_DBF_TEXT(2, setup, "init_mod");
ret = register_cu3088_discipline(&claw_group_driver);
if (ret) {
+ CLAW_DBF_TEXT(2, setup, "init_bad");
claw_unregister_debug_facility();
printk(KERN_WARNING "claw; %s() cu3088 register failed %d\n",
__func__,ret);
}
-#ifdef FUNCTRACE
- printk(KERN_INFO "claw: %s() exit \n",__func__);
-#endif
return ret;
}
module_init(claw_init);
module_exit(claw_cleanup);
-
-
-/*--------------------------------------------------------------------*
-* End of File *
-*---------------------------------------------------------------------*/
-
-
+MODULE_AUTHOR("Andy Richter <richtera@us.ibm.com>");
+MODULE_DESCRIPTION("Linux for System z CLAW Driver\n" \
+ "Copyright 2000,2008 IBM Corporation\n");
+MODULE_LICENSE("GPL");
diff --git a/drivers/s390/net/claw.h b/drivers/s390/net/claw.h
index 1a89d989f34..005072c420d 100644
--- a/drivers/s390/net/claw.h
+++ b/drivers/s390/net/claw.h
@@ -85,7 +85,7 @@
#define CLAW_MAX_DEV 256 /* max claw devices */
#define MAX_NAME_LEN 8 /* host name, adapter name length */
#define CLAW_FRAME_SIZE 4096
-#define CLAW_ID_SIZE BUS_ID_SIZE+3
+#define CLAW_ID_SIZE 20+3
/* state machine codes used in claw_irq_handler */
diff --git a/drivers/s390/net/ctcm_dbug.c b/drivers/s390/net/ctcm_dbug.c
index 8eb25d00b2e..1ca58f15347 100644
--- a/drivers/s390/net/ctcm_dbug.c
+++ b/drivers/s390/net/ctcm_dbug.c
@@ -7,6 +7,7 @@
*/
#include <linux/stddef.h>
+#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
@@ -22,15 +23,13 @@
* Debug Facility Stuff
*/
-DEFINE_PER_CPU(char[256], ctcm_dbf_txt_buf);
-
struct ctcm_dbf_info ctcm_dbf[CTCM_DBF_INFOS] = {
- [CTCM_DBF_SETUP] = {"ctc_setup", 8, 1, 64, 5, NULL},
- [CTCM_DBF_ERROR] = {"ctc_error", 8, 1, 64, 3, NULL},
- [CTCM_DBF_TRACE] = {"ctc_trace", 8, 1, 64, 3, NULL},
- [CTCM_DBF_MPC_SETUP] = {"mpc_setup", 8, 1, 64, 5, NULL},
- [CTCM_DBF_MPC_ERROR] = {"mpc_error", 8, 1, 64, 3, NULL},
- [CTCM_DBF_MPC_TRACE] = {"mpc_trace", 8, 1, 64, 3, NULL},
+ [CTCM_DBF_SETUP] = {"ctc_setup", 8, 1, 64, CTC_DBF_INFO, NULL},
+ [CTCM_DBF_ERROR] = {"ctc_error", 8, 1, 64, CTC_DBF_ERROR, NULL},
+ [CTCM_DBF_TRACE] = {"ctc_trace", 8, 1, 64, CTC_DBF_ERROR, NULL},
+ [CTCM_DBF_MPC_SETUP] = {"mpc_setup", 8, 1, 80, CTC_DBF_INFO, NULL},
+ [CTCM_DBF_MPC_ERROR] = {"mpc_error", 8, 1, 80, CTC_DBF_ERROR, NULL},
+ [CTCM_DBF_MPC_TRACE] = {"mpc_trace", 8, 1, 80, CTC_DBF_ERROR, NULL},
};
void ctcm_unregister_dbf_views(void)
@@ -65,3 +64,17 @@ int ctcm_register_dbf_views(void)
return 0;
}
+void ctcm_dbf_longtext(enum ctcm_dbf_names dbf_nix, int level, char *fmt, ...)
+{
+ char dbf_txt_buf[64];
+ va_list args;
+
+ if (level > (ctcm_dbf[dbf_nix].id)->level)
+ return;
+ va_start(args, fmt);
+ vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args);
+ va_end(args);
+
+ debug_text_event(ctcm_dbf[dbf_nix].id, level, dbf_txt_buf);
+}
+
diff --git a/drivers/s390/net/ctcm_dbug.h b/drivers/s390/net/ctcm_dbug.h
index fdff34fe59a..26966d0b9ab 100644
--- a/drivers/s390/net/ctcm_dbug.h
+++ b/drivers/s390/net/ctcm_dbug.h
@@ -20,16 +20,17 @@
#else
#define do_debug 0
#endif
-#ifdef DEBUGDATA
- #define do_debug_data 1
-#else
- #define do_debug_data 0
-#endif
#ifdef DEBUGCCW
#define do_debug_ccw 1
+ #define DEBUGDATA 1
#else
#define do_debug_ccw 0
#endif
+#ifdef DEBUGDATA
+ #define do_debug_data 1
+#else
+ #define do_debug_data 0
+#endif
/* define dbf debug levels similar to kernel msg levels */
#define CTC_DBF_ALWAYS 0 /* always print this */
@@ -42,8 +43,6 @@
#define CTC_DBF_INFO 5 /* informational */
#define CTC_DBF_DEBUG 6 /* debug-level messages */
-DECLARE_PER_CPU(char[256], ctcm_dbf_txt_buf);
-
enum ctcm_dbf_names {
CTCM_DBF_SETUP,
CTCM_DBF_ERROR,
@@ -67,6 +66,7 @@ extern struct ctcm_dbf_info ctcm_dbf[CTCM_DBF_INFOS];
int ctcm_register_dbf_views(void);
void ctcm_unregister_dbf_views(void);
+void ctcm_dbf_longtext(enum ctcm_dbf_names dbf_nix, int level, char *text, ...);
static inline const char *strtail(const char *s, int n)
{
@@ -74,12 +74,6 @@ static inline const char *strtail(const char *s, int n)
return (l > n) ? s + (l - n) : s;
}
-/* sort out levels early to avoid unnecessary sprintfs */
-static inline int ctcm_dbf_passes(debug_info_t *dbf_grp, int level)
-{
- return (dbf_grp->level >= level);
-}
-
#define CTCM_FUNTAIL strtail((char *)__func__, 16)
#define CTCM_DBF_TEXT(name, level, text) \
@@ -94,16 +88,7 @@ static inline int ctcm_dbf_passes(debug_info_t *dbf_grp, int level)
} while (0)
#define CTCM_DBF_TEXT_(name, level, text...) \
- do { \
- if (ctcm_dbf_passes(ctcm_dbf[CTCM_DBF_##name].id, level)) { \
- char *ctcm_dbf_txt_buf = \
- get_cpu_var(ctcm_dbf_txt_buf); \
- sprintf(ctcm_dbf_txt_buf, text); \
- debug_text_event(ctcm_dbf[CTCM_DBF_##name].id, \
- level, ctcm_dbf_txt_buf); \
- put_cpu_var(ctcm_dbf_txt_buf); \
- } \
- } while (0)
+ ctcm_dbf_longtext(CTCM_DBF_##name, level, text)
/*
* cat : one of {setup, mpc_setup, trace, mpc_trace, error, mpc_error}.
@@ -112,13 +97,13 @@ static inline int ctcm_dbf_passes(debug_info_t *dbf_grp, int level)
*/
#define CTCM_DBF_DEV_NAME(cat, dev, text) \
do { \
- CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%s) : %s", \
+ CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%s) :- %s", \
CTCM_FUNTAIL, dev->name, text); \
} while (0)
#define MPC_DBF_DEV_NAME(cat, dev, text) \
do { \
- CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%s) : %s", \
+ CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%s) := %s", \
CTCM_FUNTAIL, dev->name, text); \
} while (0)
@@ -137,13 +122,13 @@ static inline int ctcm_dbf_passes(debug_info_t *dbf_grp, int level)
*/
#define CTCM_DBF_DEV(cat, dev, text) \
do { \
- CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%p) : %s", \
+ CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%p) :-: %s", \
CTCM_FUNTAIL, dev, text); \
} while (0)
#define MPC_DBF_DEV(cat, dev, text) \
do { \
- CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%p) : %s", \
+ CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%p) :=: %s", \
CTCM_FUNTAIL, dev, text); \
} while (0)
diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c
index 2a106f3a076..42776550acf 100644
--- a/drivers/s390/net/ctcm_fsms.c
+++ b/drivers/s390/net/ctcm_fsms.c
@@ -190,7 +190,8 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg);
void ctcm_ccw_check_rc(struct channel *ch, int rc, char *msg)
{
CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
- "ccw error %s (%s): %04x\n", ch->id, msg, rc);
+ "%s(%s): %s: %04x\n",
+ CTCM_FUNTAIL, ch->id, msg, rc);
switch (rc) {
case -EBUSY:
ctcm_pr_warn("%s (%s): Busy !\n", ch->id, msg);
@@ -212,7 +213,7 @@ void ctcm_purge_skb_queue(struct sk_buff_head *q)
{
struct sk_buff *skb;
- CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__);
+ CTCM_DBF_TEXT(TRACE, CTC_DBF_DEBUG, __func__);
while ((skb = skb_dequeue(q))) {
atomic_dec(&skb->users);
@@ -244,22 +245,25 @@ static void chx_txdone(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct sk_buff *skb;
int first = 1;
int i;
unsigned long duration;
struct timespec done_stamp = current_kernel_time(); /* xtime */
+ CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name);
+
duration =
(done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 +
(done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000;
if (duration > ch->prof.tx_time)
ch->prof.tx_time = duration;
- if (ch->irb->scsw.count != 0)
- ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n",
- dev->name, ch->irb->scsw.count);
+ if (ch->irb->scsw.cmd.count != 0)
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "%s(%s): TX not complete, remaining %d bytes",
+ CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.count);
fsm_deltimer(&ch->timer);
while ((skb = skb_dequeue(&ch->io_queue))) {
priv->stats.tx_packets++;
@@ -332,9 +336,10 @@ void ctcm_chx_txidle(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
+
+ CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name);
- CTCM_DBF_TEXT(TRACE, 6, __FUNCTION__);
fsm_deltimer(&ch->timer);
fsm_newstate(fi, CTC_STATE_TXIDLE);
fsm_event(priv->fsm, DEV_EVENT_TXUP, ch->netdev);
@@ -352,8 +357,8 @@ static void chx_rx(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
- int len = ch->max_bufsize - ch->irb->scsw.count;
+ struct ctcm_priv *priv = dev->ml_priv;
+ int len = ch->max_bufsize - ch->irb->scsw.cmd.count;
struct sk_buff *skb = ch->trans_skb;
__u16 block_len = *((__u16 *)skb->data);
int check_len;
@@ -361,15 +366,17 @@ static void chx_rx(fsm_instance *fi, int event, void *arg)
fsm_deltimer(&ch->timer);
if (len < 8) {
- ctcm_pr_debug("%s: got packet with length %d < 8\n",
- dev->name, len);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s): got packet with length %d < 8\n",
+ CTCM_FUNTAIL, dev->name, len);
priv->stats.rx_dropped++;
priv->stats.rx_length_errors++;
goto again;
}
if (len > ch->max_bufsize) {
- ctcm_pr_debug("%s: got packet with length %d > %d\n",
- dev->name, len, ch->max_bufsize);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s): got packet with length %d > %d\n",
+ CTCM_FUNTAIL, dev->name, len, ch->max_bufsize);
priv->stats.rx_dropped++;
priv->stats.rx_length_errors++;
goto again;
@@ -388,8 +395,9 @@ static void chx_rx(fsm_instance *fi, int event, void *arg)
break;
}
if ((len < block_len) || (len > check_len)) {
- ctcm_pr_debug("%s: got block length %d != rx length %d\n",
- dev->name, block_len, len);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s): got block length %d != rx length %d\n",
+ CTCM_FUNTAIL, dev->name, block_len, len);
if (do_debug)
ctcmpc_dump_skb(skb, 0);
@@ -425,17 +433,23 @@ static void chx_rx(fsm_instance *fi, int event, void *arg)
*/
static void chx_firstio(fsm_instance *fi, int event, void *arg)
{
- struct channel *ch = arg;
int rc;
+ struct channel *ch = arg;
+ int fsmstate = fsm_getstate(fi);
- CTCM_DBF_TEXT(TRACE, 6, __FUNCTION__);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s) : %02x",
+ CTCM_FUNTAIL, ch->id, fsmstate);
- if (fsm_getstate(fi) == CTC_STATE_TXIDLE)
- ctcm_pr_debug("%s: remote side issued READ?, init.\n", ch->id);
+ ch->sense_rc = 0; /* reset unit check report control */
+ if (fsmstate == CTC_STATE_TXIDLE)
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "%s(%s): remote side issued READ?, init.\n",
+ CTCM_FUNTAIL, ch->id);
fsm_deltimer(&ch->timer);
if (ctcm_checkalloc_buffer(ch))
return;
- if ((fsm_getstate(fi) == CTC_STATE_SETUPWAIT) &&
+ if ((fsmstate == CTC_STATE_SETUPWAIT) &&
(ch->protocol == CTCM_PROTO_OS390)) {
/* OS/390 resp. z/OS */
if (CHANNEL_DIRECTION(ch->flags) == READ) {
@@ -445,13 +459,12 @@ static void chx_firstio(fsm_instance *fi, int event, void *arg)
chx_rxidle(fi, event, arg);
} else {
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
fsm_newstate(fi, CTC_STATE_TXIDLE);
fsm_event(priv->fsm, DEV_EVENT_TXUP, dev);
}
return;
}
-
/*
* Don't setup a timer for receiving the initial RX frame
* if in compatibility mode, since VM TCP delays the initial
@@ -483,7 +496,7 @@ static void chx_firstio(fsm_instance *fi, int event, void *arg)
if ((CHANNEL_DIRECTION(ch->flags) == READ) &&
(ch->protocol == CTCM_PROTO_S390)) {
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
}
}
@@ -501,15 +514,14 @@ static void chx_rxidle(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
__u16 buflen;
int rc;
- CTCM_DBF_TEXT(TRACE, 6, __FUNCTION__);
fsm_deltimer(&ch->timer);
buflen = *((__u16 *)ch->trans_skb->data);
- if (do_debug)
- ctcm_pr_debug("%s: Initial RX count %d\n", dev->name, buflen);
+ CTCM_PR_DEBUG("%s: %s: Initial RX count = %d\n",
+ __func__, dev->name, buflen);
if (buflen >= CTCM_INITIAL_BLOCKLEN) {
if (ctcm_checkalloc_buffer(ch))
@@ -524,9 +536,9 @@ static void chx_rxidle(fsm_instance *fi, int event, void *arg)
} else
fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
} else {
- if (do_debug)
- ctcm_pr_debug("%s: Initial RX count %d not %d\n",
- dev->name, buflen, CTCM_INITIAL_BLOCKLEN);
+ CTCM_PR_DEBUG("%s: %s: Initial RX count %d not %d\n",
+ __func__, dev->name,
+ buflen, CTCM_INITIAL_BLOCKLEN);
chx_firstio(fi, event, arg);
}
}
@@ -548,14 +560,12 @@ static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg)
fsm_deltimer(&ch->timer);
if (IS_MPC(ch)) {
timeout = 1500;
- if (do_debug)
- ctcm_pr_debug("ctcm enter: %s(): cp=%i ch=0x%p id=%s\n",
- __FUNCTION__, smp_processor_id(), ch, ch->id);
+ CTCM_PR_DEBUG("enter %s: cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ch, ch->id);
}
fsm_addtimer(&ch->timer, timeout, CTC_EVENT_TIMER, ch);
fsm_newstate(fi, CTC_STATE_SETUPWAIT);
- if (do_debug_ccw && IS_MPC(ch))
- ctcmpc_dumpit((char *)&ch->ccw[6], sizeof(struct ccw1) * 2);
+ CTCM_CCW_DUMP((char *)&ch->ccw[6], sizeof(struct ccw1) * 2);
if (event == CTC_EVENT_TIMER) /* only for timer not yet locked */
spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
@@ -583,24 +593,12 @@ static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg)
*/
static void ctcm_chx_start(fsm_instance *fi, int event, void *arg)
{
- struct channel *ch = arg;
- int rc;
- struct net_device *dev;
+ struct channel *ch = arg;
unsigned long saveflags;
+ int rc;
- CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__);
- if (ch == NULL) {
- ctcm_pr_warn("chx_start ch=NULL\n");
- return;
- }
- if (ch->netdev == NULL) {
- ctcm_pr_warn("chx_start dev=NULL, id=%s\n", ch->id);
- return;
- }
- dev = ch->netdev;
-
- if (do_debug)
- ctcm_pr_debug("%s: %s channel start\n", dev->name,
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s): %s",
+ CTCM_FUNTAIL, ch->id,
(CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
if (ch->trans_skb != NULL) {
@@ -618,11 +616,12 @@ static void ctcm_chx_start(fsm_instance *fi, int event, void *arg)
ch->ccw[1].count = 0;
}
if (ctcm_checkalloc_buffer(ch)) {
- ctcm_pr_notice("%s: %s trans_skb allocation delayed "
- "until first transfer\n", dev->name,
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "%s(%s): %s trans_skb alloc delayed "
+ "until first transfer",
+ CTCM_FUNTAIL, ch->id,
(CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
}
-
ch->ccw[0].cmd_code = CCW_CMD_PREPARE;
ch->ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
ch->ccw[0].count = 0;
@@ -661,7 +660,6 @@ static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg)
int rc;
int oldstate;
- CTCM_DBF_TEXT(TRACE, 2, __FUNCTION__);
fsm_deltimer(&ch->timer);
if (IS_MPC(ch))
fsm_deltimer(&ch->sweep_timer);
@@ -684,7 +682,7 @@ static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg)
fsm_deltimer(&ch->timer);
if (event != CTC_EVENT_STOP) {
fsm_newstate(fi, oldstate);
- ctcm_ccw_check_rc(ch, rc, (char *)__FUNCTION__);
+ ctcm_ccw_check_rc(ch, rc, (char *)__func__);
}
}
}
@@ -701,9 +699,11 @@ static void ctcm_chx_cleanup(fsm_instance *fi, int state,
struct channel *ch)
{
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
- CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__);
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE,
+ "%s(%s): %s[%d]\n",
+ CTCM_FUNTAIL, dev->name, ch->id, state);
fsm_deltimer(&ch->timer);
if (IS_MPC(ch))
@@ -743,7 +743,6 @@ static void ctcm_chx_cleanup(fsm_instance *fi, int state,
*/
static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg)
{
- CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__);
ctcm_chx_cleanup(fi, CTC_STATE_STOPPED, arg);
}
@@ -771,7 +770,6 @@ static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg)
*/
static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg)
{
- CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__);
ctcm_chx_cleanup(fi, CTC_STATE_NOTOP, arg);
}
@@ -786,7 +784,7 @@ static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
/*
* Special case: Got UC_RCRESET on setmode.
@@ -809,8 +807,8 @@ static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg)
}
CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT,
- "%s : %s error during %s channel setup state=%s\n",
- dev->name, ctc_ch_event_names[event],
+ "%s(%s) : %s error during %s channel setup state=%s\n",
+ CTCM_FUNTAIL, dev->name, ctc_ch_event_names[event],
(CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX",
fsm_getstate_str(fi));
@@ -838,10 +836,12 @@ static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg)
int oldstate;
int rc;
- CTCM_DBF_TEXT(TRACE, CTC_DBF_NOTICE, __FUNCTION__);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s: %s[%d] of %s\n",
+ CTCM_FUNTAIL, ch->id, event, dev->name);
+
fsm_deltimer(&ch->timer);
- ctcm_pr_debug("%s: %s channel restart\n", dev->name,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
+
fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
oldstate = fsm_getstate(fi);
fsm_newstate(fi, CTC_STATE_STARTWAIT);
@@ -874,15 +874,12 @@ static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
- CTCM_DBF_TEXT(SETUP, 3, __FUNCTION__);
if (event == CTC_EVENT_TIMER) {
if (!IS_MPCDEV(dev))
/* TODO : check if MPC deletes timer somewhere */
fsm_deltimer(&ch->timer);
- ctcm_pr_debug("%s: Timeout during RX init handshake\n",
- dev->name);
if (ch->retry++ < 3)
ctcm_chx_restart(fi, event, arg);
else {
@@ -905,11 +902,12 @@ static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
- CTCM_DBF_TEXT(SETUP, 3, __FUNCTION__);
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): RX %s busy, init. fail",
+ CTCM_FUNTAIL, dev->name, ch->id);
fsm_newstate(fi, CTC_STATE_RXERR);
- ctcm_pr_warn("%s: RX busy. Initialization failed\n", dev->name);
fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
}
@@ -925,13 +923,12 @@ static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg)
struct channel *ch = arg;
struct channel *ch2;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
- CTCM_DBF_DEV_NAME(TRACE, dev, "Got remote disconnect, re-initializing");
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s: %s: remote disconnect - re-init ...",
+ CTCM_FUNTAIL, dev->name);
fsm_deltimer(&ch->timer);
- if (do_debug)
- ctcm_pr_debug("%s: Got remote disconnect, "
- "re-initializing ...\n", dev->name);
/*
* Notify device statemachine
*/
@@ -957,12 +954,10 @@ static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
if (event == CTC_EVENT_TIMER) {
fsm_deltimer(&ch->timer);
- CTCM_DBF_DEV_NAME(ERROR, dev,
- "Timeout during TX init handshake");
if (ch->retry++ < 3)
ctcm_chx_restart(fi, event, arg);
else {
@@ -971,9 +966,8 @@ static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg)
}
} else {
CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
- "%s : %s error during channel setup state=%s",
- dev->name, ctc_ch_event_names[event],
- fsm_getstate_str(fi));
+ "%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
+ ctc_ch_event_names[event], fsm_getstate_str(fi));
ctcm_pr_warn("%s: Error during TX init handshake\n", dev->name);
}
@@ -990,18 +984,18 @@ static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct sk_buff *skb;
- if (do_debug)
- ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
- __FUNCTION__, smp_processor_id(), ch, ch->id);
+ CTCM_PR_DEBUG("Enter: %s: cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ch, ch->id);
fsm_deltimer(&ch->timer);
if (ch->retry++ > 3) {
struct mpc_group *gptr = priv->mpcg;
- ctcm_pr_debug("%s: TX retry failed, restarting channel\n",
- dev->name);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
+ "%s: %s: retries exceeded",
+ CTCM_FUNTAIL, ch->id);
fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
/* call restart if not MPC or if MPC and mpcg fsm is ready.
use gptr as mpc indicator */
@@ -1010,7 +1004,9 @@ static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg)
goto done;
}
- ctcm_pr_debug("%s: TX retry %d\n", dev->name, ch->retry);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "%s : %s: retry %d",
+ CTCM_FUNTAIL, ch->id, ch->retry);
skb = skb_peek(&ch->io_queue);
if (skb) {
int rc = 0;
@@ -1018,8 +1014,9 @@ static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg)
clear_normalized_cda(&ch->ccw[4]);
ch->ccw[4].count = skb->len;
if (set_normalized_cda(&ch->ccw[4], skb->data)) {
- ctcm_pr_debug("%s: IDAL alloc failed, chan restart\n",
- dev->name);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
+ "%s: %s: IDAL alloc failed",
+ CTCM_FUNTAIL, ch->id);
fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
ctcm_chx_restart(fi, event, arg);
goto done;
@@ -1060,23 +1057,22 @@ static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
+ int rd = CHANNEL_DIRECTION(ch->flags);
- CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__);
fsm_deltimer(&ch->timer);
- ctcm_pr_warn("%s %s : unrecoverable channel error\n",
- CTC_DRIVER_NAME, dev->name);
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s: %s: %s unrecoverable channel error",
+ CTCM_FUNTAIL, ch->id, rd == READ ? "RX" : "TX");
+
if (IS_MPC(ch)) {
priv->stats.tx_dropped++;
priv->stats.tx_errors++;
}
-
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- ctcm_pr_debug("%s: RX I/O error\n", dev->name);
+ if (rd == READ) {
fsm_newstate(fi, CTC_STATE_RXERR);
fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
} else {
- ctcm_pr_debug("%s: TX I/O error\n", dev->name);
fsm_newstate(fi, CTC_STATE_TXERR);
fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
}
@@ -1211,32 +1207,32 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct sk_buff *skb;
int first = 1;
int i;
- struct timespec done_stamp;
__u32 data_space;
unsigned long duration;
struct sk_buff *peekskb;
int rc;
struct th_header *header;
struct pdu *p_header;
+ struct timespec done_stamp = current_kernel_time(); /* xtime */
- if (do_debug)
- ctcm_pr_debug("%s cp:%i enter: %s()\n",
- dev->name, smp_processor_id(), __FUNCTION__);
+ CTCM_PR_DEBUG("Enter %s: %s cp:%i\n",
+ __func__, dev->name, smp_processor_id());
- done_stamp = current_kernel_time(); /* xtime */
- duration = (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000
- + (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000;
+ duration =
+ (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 +
+ (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000;
if (duration > ch->prof.tx_time)
ch->prof.tx_time = duration;
- if (ch->irb->scsw.count != 0)
- ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n",
- dev->name, ch->irb->scsw.count);
+ if (ch->irb->scsw.cmd.count != 0)
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ "%s(%s): TX not complete, remaining %d bytes",
+ CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.count);
fsm_deltimer(&ch->timer);
while ((skb = skb_dequeue(&ch->io_queue))) {
priv->stats.tx_packets++;
@@ -1250,7 +1246,6 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
}
spin_lock(&ch->collect_lock);
clear_normalized_cda(&ch->ccw[4]);
-
if ((ch->collect_len <= 0) || (grp->in_sweep != 0)) {
spin_unlock(&ch->collect_lock);
fsm_newstate(fi, CTC_STATE_TXIDLE);
@@ -1269,17 +1264,13 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
i = 0;
-
- if (do_debug_data)
- ctcm_pr_debug("ctcmpc: %s() building "
- "trans_skb from collect_q \n", __FUNCTION__);
-
+ p_header = NULL;
data_space = grp->group_max_buflen - TH_HEADER_LENGTH;
- if (do_debug_data)
- ctcm_pr_debug("ctcmpc: %s() building trans_skb from collect_q"
- " data_space:%04x\n", __FUNCTION__, data_space);
- p_header = NULL;
+ CTCM_PR_DBGDATA("%s: building trans_skb from collect_q"
+ " data_space:%04x\n",
+ __func__, data_space);
+
while ((skb = skb_dequeue(&ch->collect_queue))) {
memcpy(skb_put(ch->trans_skb, skb->len), skb->data, skb->len);
p_header = (struct pdu *)
@@ -1290,15 +1281,12 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
else
p_header->pdu_flag |= 0x20;
- if (do_debug_data) {
- ctcm_pr_debug("ctcmpc: %s()trans_skb len:%04x \n",
- __FUNCTION__, ch->trans_skb->len);
- ctcm_pr_debug("ctcmpc: %s() pdu header and data"
- " for up to 32 bytes sent to vtam\n",
- __FUNCTION__);
- ctcmpc_dumpit((char *)p_header,
- min_t(int, skb->len, 32));
- }
+ CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n",
+ __func__, ch->trans_skb->len);
+ CTCM_PR_DBGDATA("%s: pdu header and data for up"
+ " to 32 bytes sent to vtam\n", __func__);
+ CTCM_D3_DUMP((char *)p_header, min_t(int, skb->len, 32));
+
ch->collect_len -= skb->len;
data_space -= skb->len;
priv->stats.tx_packets++;
@@ -1314,46 +1302,38 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
if (p_header)
p_header->pdu_flag |= PDU_LAST; /*Say it's the last one*/
header = kzalloc(TH_HEADER_LENGTH, gfp_type());
-
if (!header) {
- printk(KERN_WARNING "ctcmpc: OUT OF MEMORY IN %s()"
- ": Data Lost \n", __FUNCTION__);
spin_unlock(&ch->collect_lock);
fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
- goto done;
+ goto done;
}
-
header->th_ch_flag = TH_HAS_PDU; /* Normal data */
ch->th_seq_num++;
header->th_seq_num = ch->th_seq_num;
- if (do_debug_data)
- ctcm_pr_debug("%s: ToVTAM_th_seq= %08x\n" ,
- __FUNCTION__, ch->th_seq_num);
+ CTCM_PR_DBGDATA("%s: ToVTAM_th_seq= %08x\n" ,
+ __func__, ch->th_seq_num);
memcpy(skb_push(ch->trans_skb, TH_HEADER_LENGTH), header,
TH_HEADER_LENGTH); /* put the TH on the packet */
kfree(header);
- if (do_debug_data) {
- ctcm_pr_debug("ctcmpc: %s()trans_skb len:%04x \n",
- __FUNCTION__, ch->trans_skb->len);
-
- ctcm_pr_debug("ctcmpc: %s() up-to-50 bytes of trans_skb "
- "data to vtam from collect_q\n", __FUNCTION__);
- ctcmpc_dumpit((char *)ch->trans_skb->data,
+ CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n",
+ __func__, ch->trans_skb->len);
+ CTCM_PR_DBGDATA("%s: up-to-50 bytes of trans_skb "
+ "data to vtam from collect_q\n", __func__);
+ CTCM_D3_DUMP((char *)ch->trans_skb->data,
min_t(int, ch->trans_skb->len, 50));
- }
spin_unlock(&ch->collect_lock);
clear_normalized_cda(&ch->ccw[1]);
if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
dev_kfree_skb_any(ch->trans_skb);
ch->trans_skb = NULL;
- printk(KERN_WARNING
- "ctcmpc: %s()CCW failure - data lost\n",
- __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR,
+ "%s: %s: IDAL alloc failed",
+ CTCM_FUNTAIL, ch->id);
fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
return;
}
@@ -1373,7 +1353,6 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
}
done:
ctcm_clear_busy(dev);
- ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__);
return;
}
@@ -1389,30 +1368,29 @@ static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct sk_buff *skb = ch->trans_skb;
struct sk_buff *new_skb;
- unsigned long saveflags = 0; /* avoids compiler warning */
- int len = ch->max_bufsize - ch->irb->scsw.count;
-
- if (do_debug_data) {
- CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "mpc_ch_rx %s cp:%i %s\n",
- dev->name, smp_processor_id(), ch->id);
- CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "mpc_ch_rx: maxbuf: %04x "
- "len: %04x\n", ch->max_bufsize, len);
- }
+ unsigned long saveflags = 0; /* avoids compiler warning */
+ int len = ch->max_bufsize - ch->irb->scsw.cmd.count;
+
+ CTCM_PR_DEBUG("%s: %s: cp:%i %s maxbuf : %04x, len: %04x\n",
+ CTCM_FUNTAIL, dev->name, smp_processor_id(),
+ ch->id, ch->max_bufsize, len);
fsm_deltimer(&ch->timer);
if (skb == NULL) {
- ctcm_pr_debug("ctcmpc exit: %s() TRANS_SKB = NULL \n",
- __FUNCTION__);
- goto again;
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): TRANS_SKB = NULL",
+ CTCM_FUNTAIL, dev->name);
+ goto again;
}
if (len < TH_HEADER_LENGTH) {
- ctcm_pr_info("%s: got packet with invalid length %d\n",
- dev->name, len);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): packet length %d to short",
+ CTCM_FUNTAIL, dev->name, len);
priv->stats.rx_dropped++;
priv->stats.rx_length_errors++;
} else {
@@ -1422,11 +1400,9 @@ static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg)
new_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC);
if (new_skb == NULL) {
- printk(KERN_INFO "ctcmpc:%s() NEW_SKB = NULL\n",
- __FUNCTION__);
- printk(KERN_WARNING "ctcmpc: %s() MEMORY ALLOC FAILED"
- " - DATA LOST - MPC FAILED\n",
- __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%d): skb allocation failed",
+ CTCM_FUNTAIL, dev->name);
fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
goto again;
}
@@ -1479,9 +1455,8 @@ again:
break;
}
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit : %s %s(): ch=0x%p id=%s\n",
- dev->name, __FUNCTION__, ch, ch->id);
+ CTCM_PR_DEBUG("Exit %s: %s, ch=0x%p, id=%s\n",
+ __func__, dev->name, ch, ch->id);
}
@@ -1496,16 +1471,17 @@ static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *gptr = priv->mpcg;
+
+ CTCM_PR_DEBUG("Enter %s: id=%s, ch=0x%p\n",
+ __func__, ch->id, ch);
+
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_INFO,
+ "%s: %s: chstate:%i, grpstate:%i, prot:%i\n",
+ CTCM_FUNTAIL, ch->id, fsm_getstate(fi),
+ fsm_getstate(gptr->fsm), ch->protocol);
- if (do_debug) {
- struct mpc_group *gptr = priv->mpcg;
- ctcm_pr_debug("ctcmpc enter: %s(): ch=0x%p id=%s\n",
- __FUNCTION__, ch, ch->id);
- ctcm_pr_debug("%s() %s chstate:%i grpstate:%i chprotocol:%i\n",
- __FUNCTION__, ch->id, fsm_getstate(fi),
- fsm_getstate(gptr->fsm), ch->protocol);
- }
if (fsm_getstate(fi) == CTC_STATE_TXIDLE)
MPC_DBF_DEV_NAME(TRACE, dev, "remote side issued READ? ");
@@ -1531,9 +1507,8 @@ static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg)
? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
done:
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
- __FUNCTION__, ch, ch->id);
+ CTCM_PR_DEBUG("Exit %s: id=%s, ch=0x%p\n",
+ __func__, ch->id, ch);
return;
}
@@ -1550,18 +1525,15 @@ void ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
int rc;
unsigned long saveflags = 0; /* avoids compiler warning */
fsm_deltimer(&ch->timer);
- ctcm_pr_debug("%s cp:%i enter: %s()\n",
- dev->name, smp_processor_id(), __FUNCTION__);
- if (do_debug)
- ctcm_pr_debug("%s() %s chstate:%i grpstate:%i\n",
- __FUNCTION__, ch->id,
- fsm_getstate(fi), fsm_getstate(grp->fsm));
+ CTCM_PR_DEBUG("%s: %s: %s: cp:%i, chstate:%i grpstate:%i\n",
+ __func__, ch->id, dev->name, smp_processor_id(),
+ fsm_getstate(fi), fsm_getstate(grp->fsm));
fsm_newstate(fi, CTC_STATE_RXIDLE);
/* XID processing complete */
@@ -1575,9 +1547,7 @@ void ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg)
skb_reset_tail_pointer(ch->trans_skb);
ch->trans_skb->len = 0;
ch->ccw[1].count = ch->max_bufsize;
- if (do_debug_ccw)
- ctcmpc_dumpit((char *)&ch->ccw[0],
- sizeof(struct ccw1) * 3);
+ CTCM_CCW_DUMP((char *)&ch->ccw[0], sizeof(struct ccw1) * 3);
if (event == CTC_EVENT_START)
/* see remark about conditional locking */
spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
@@ -1598,9 +1568,6 @@ void ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg)
fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
done:
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit: %s %s()\n",
- dev->name, __FUNCTION__);
return;
}
@@ -1613,16 +1580,12 @@ static void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
- if (do_debug) {
- ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s"
- "GrpState:%s ChState:%s\n",
- __FUNCTION__, smp_processor_id(), ch, ch->id,
- fsm_getstate_str(grp->fsm),
- fsm_getstate_str(ch->fsm));
- }
+ CTCM_PR_DEBUG("%s(%s): %s(ch=0x%p), cp=%i, ChStat:%s, GrpStat:%s\n",
+ __func__, dev->name, ch->id, ch, smp_processor_id(),
+ fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->fsm));
switch (fsm_getstate(grp->fsm)) {
case MPCG_STATE_XID2INITW:
@@ -1664,11 +1627,7 @@ static void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg)
break;
}
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit : %s(): cp=%i ch=0x%p id=%s\n",
- __FUNCTION__, smp_processor_id(), ch, ch->id);
return;
-
}
/*
@@ -1680,14 +1639,12 @@ static void ctcmpc_chx_attnbusy(fsm_instance *fsm, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
- ctcm_pr_debug("ctcmpc enter: %s %s() %s \nGrpState:%s ChState:%s\n",
- dev->name,
- __FUNCTION__, ch->id,
- fsm_getstate_str(grp->fsm),
- fsm_getstate_str(ch->fsm));
+ CTCM_PR_DEBUG("%s(%s): %s\n ChState:%s GrpState:%s\n",
+ __func__, dev->name, ch->id,
+ fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->fsm));
fsm_deltimer(&ch->timer);
@@ -1750,16 +1707,12 @@ static void ctcmpc_chx_attnbusy(fsm_instance *fsm, int event, void *arg)
if (ch->in_mpcgroup)
fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
else
- printk(KERN_WARNING "ctcmpc: %s() Not all channels have"
- " been added to group\n", __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): channel %s not added to group",
+ CTCM_FUNTAIL, dev->name, ch->id);
done:
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit : %s()%s ch=0x%p id=%s\n",
- __FUNCTION__, dev->name, ch, ch->id);
-
return;
-
}
/*
@@ -1771,16 +1724,10 @@ static void ctcmpc_chx_resend(fsm_instance *fsm, int event, void *arg)
{
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
- ctcm_pr_debug("ctcmpc enter: %s %s() %s \nGrpState:%s ChState:%s\n",
- dev->name, __FUNCTION__, ch->id,
- fsm_getstate_str(grp->fsm),
- fsm_getstate_str(ch->fsm));
-
fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
-
return;
}
@@ -1793,7 +1740,7 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg)
{
struct channel *ach = arg;
struct net_device *dev = ach->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct channel *wch = priv->channel[WRITE];
struct channel *rch = priv->channel[READ];
@@ -1802,19 +1749,16 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg)
int rc = 0;
unsigned long saveflags = 0;
- if (do_debug)
- ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
- __FUNCTION__, smp_processor_id(), ach, ach->id);
+ CTCM_PR_DEBUG("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ach, ach->id);
if (grp->in_sweep == 0)
goto done;
- if (do_debug_data) {
- ctcm_pr_debug("ctcmpc: %s() 1: ToVTAM_th_seq= %08x\n" ,
- __FUNCTION__, wch->th_seq_num);
- ctcm_pr_debug("ctcmpc: %s() 1: FromVTAM_th_seq= %08x\n" ,
- __FUNCTION__, rch->th_seq_num);
- }
+ CTCM_PR_DBGDATA("%s: 1: ToVTAM_th_seq= %08x\n" ,
+ __func__, wch->th_seq_num);
+ CTCM_PR_DBGDATA("%s: 1: FromVTAM_th_seq= %08x\n" ,
+ __func__, rch->th_seq_num);
if (fsm_getstate(wch->fsm) != CTC_STATE_TXIDLE) {
/* give the previous IO time to complete */
@@ -1853,11 +1797,9 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg)
header->sw.th_last_seq = wch->th_seq_num;
- if (do_debug_ccw)
- ctcmpc_dumpit((char *)&wch->ccw[3], sizeof(struct ccw1) * 3);
-
- ctcm_pr_debug("ctcmpc: %s() sweep packet\n", __FUNCTION__);
- ctcmpc_dumpit((char *)header, TH_SWEEP_LENGTH);
+ CTCM_CCW_DUMP((char *)&wch->ccw[3], sizeof(struct ccw1) * 3);
+ CTCM_PR_DBGDATA("%s: sweep packet\n", __func__);
+ CTCM_D3_DUMP((char *)header, TH_SWEEP_LENGTH);
fsm_addtimer(&wch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, wch);
fsm_newstate(wch->fsm, CTC_STATE_TX);
@@ -1876,19 +1818,13 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg)
ctcm_clear_busy_do(dev);
}
- if (do_debug_data) {
- ctcm_pr_debug("ctcmpc: %s()2: ToVTAM_th_seq= %08x\n" ,
- __FUNCTION__, wch->th_seq_num);
- ctcm_pr_debug("ctcmpc: %s()2: FromVTAM_th_seq= %08x\n" ,
- __FUNCTION__, rch->th_seq_num);
- }
+ CTCM_PR_DBGDATA("%s: To-/From-VTAM_th_seq = %08x/%08x\n" ,
+ __func__, wch->th_seq_num, rch->th_seq_num);
if (rc != 0)
ctcm_ccw_check_rc(wch, rc, "send sweep");
done:
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit: %s() %s\n", __FUNCTION__, ach->id);
return;
}
@@ -2114,7 +2050,7 @@ int mpc_ch_fsm_len = ARRAY_SIZE(ctcmpc_ch_fsm);
static void dev_action_start(fsm_instance *fi, int event, void *arg)
{
struct net_device *dev = arg;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
int direction;
CTCMY_DBF_DEV_NAME(SETUP, dev, "");
@@ -2140,7 +2076,7 @@ static void dev_action_stop(fsm_instance *fi, int event, void *arg)
{
int direction;
struct net_device *dev = arg;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
CTCMY_DBF_DEV_NAME(SETUP, dev, "");
@@ -2149,9 +2085,8 @@ static void dev_action_stop(fsm_instance *fi, int event, void *arg)
struct channel *ch = priv->channel[direction];
fsm_event(ch->fsm, CTC_EVENT_STOP, ch);
ch->th_seq_num = 0x00;
- if (do_debug)
- ctcm_pr_debug("ctcm: %s() CH_th_seq= %08x\n",
- __FUNCTION__, ch->th_seq_num);
+ CTCM_PR_DEBUG("%s: CH_th_seq= %08x\n",
+ __func__, ch->th_seq_num);
}
if (IS_MPC(priv))
fsm_newstate(priv->mpcg->fsm, MPCG_STATE_RESET);
@@ -2161,7 +2096,7 @@ static void dev_action_restart(fsm_instance *fi, int event, void *arg)
{
int restart_timer;
struct net_device *dev = arg;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
CTCMY_DBF_DEV_NAME(TRACE, dev, "");
@@ -2198,9 +2133,12 @@ static void dev_action_restart(fsm_instance *fi, int event, void *arg)
static void dev_action_chup(fsm_instance *fi, int event, void *arg)
{
struct net_device *dev = arg;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
+ int dev_stat = fsm_getstate(fi);
- CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE,
+ "%s(%s): priv = %p [%d,%d]\n ", CTCM_FUNTAIL,
+ dev->name, dev->ml_priv, dev_stat, event);
switch (fsm_getstate(fi)) {
case DEV_STATE_STARTWAIT_RXTX:
@@ -2257,7 +2195,7 @@ static void dev_action_chdown(fsm_instance *fi, int event, void *arg)
{
struct net_device *dev = arg;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
CTCMY_DBF_DEV_NAME(SETUP, dev, "");
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index d52843da4f5..a4e29836a2a 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -69,7 +69,7 @@ struct channel *channels;
void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
{
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
__u16 len = *((__u16 *) pskb->data);
skb_put(pskb, 2 + LL_HEADER_LENGTH);
@@ -84,20 +84,19 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
skb_pull(pskb, LL_HEADER_LENGTH);
if ((ch->protocol == CTCM_PROTO_S390) &&
(header->type != ETH_P_IP)) {
-
if (!(ch->logflags & LOG_FLAG_ILLEGALPKT)) {
+ ch->logflags |= LOG_FLAG_ILLEGALPKT;
/*
* Check packet type only if we stick strictly
* to S/390's protocol of OS390. This only
* supports IP. Otherwise allow any packet
* type.
*/
- ctcm_pr_warn("%s Illegal packet type 0x%04x "
- "received, dropping\n",
- dev->name, header->type);
- ch->logflags |= LOG_FLAG_ILLEGALPKT;
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): Illegal packet type 0x%04x"
+ " - dropping",
+ CTCM_FUNTAIL, dev->name, header->type);
}
-
priv->stats.rx_dropped++;
priv->stats.rx_frame_errors++;
return;
@@ -105,11 +104,11 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
pskb->protocol = ntohs(header->type);
if (header->length <= LL_HEADER_LENGTH) {
if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) {
- ctcm_pr_warn(
- "%s Illegal packet size %d "
- "received (MTU=%d blocklen=%d), "
- "dropping\n", dev->name, header->length,
- dev->mtu, len);
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): Illegal packet size %d(%d,%d)"
+ "- dropping",
+ CTCM_FUNTAIL, dev->name,
+ header->length, dev->mtu, len);
ch->logflags |= LOG_FLAG_ILLEGALSIZE;
}
@@ -122,10 +121,10 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
if ((header->length > skb_tailroom(pskb)) ||
(header->length > len)) {
if (!(ch->logflags & LOG_FLAG_OVERRUN)) {
- ctcm_pr_warn(
- "%s Illegal packet size %d (beyond the"
- " end of received data), dropping\n",
- dev->name, header->length);
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): Packet size %d (overrun)"
+ " - dropping", CTCM_FUNTAIL,
+ dev->name, header->length);
ch->logflags |= LOG_FLAG_OVERRUN;
}
@@ -139,9 +138,9 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
skb = dev_alloc_skb(pskb->len);
if (!skb) {
if (!(ch->logflags & LOG_FLAG_NOMEM)) {
- ctcm_pr_warn(
- "%s Out of memory in ctcm_unpack_skb\n",
- dev->name);
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): MEMORY allocation error",
+ CTCM_FUNTAIL, dev->name);
ch->logflags |= LOG_FLAG_NOMEM;
}
priv->stats.rx_dropped++;
@@ -184,7 +183,7 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
*/
static void channel_free(struct channel *ch)
{
- CTCM_DBF_TEXT(TRACE, 2, __FUNCTION__);
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s)", CTCM_FUNTAIL, ch->id);
ch->flags &= ~CHANNEL_FLAGS_INUSE;
fsm_newstate(ch->fsm, CTC_STATE_IDLE);
}
@@ -251,19 +250,12 @@ static struct channel *channel_get(enum channel_types type,
{
struct channel *ch = channels;
- if (do_debug) {
- char buf[64];
- sprintf(buf, "%s(%d, %s, %d)\n",
- CTCM_FUNTAIL, type, id, direction);
- CTCM_DBF_TEXT(TRACE, CTC_DBF_INFO, buf);
- }
while (ch && (strncmp(ch->id, id, CTCM_ID_SIZE) || (ch->type != type)))
ch = ch->next;
if (!ch) {
- char buf[64];
- sprintf(buf, "%s(%d, %s, %d) not found in channel list\n",
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%d, %s, %d) not found in channel list\n",
CTCM_FUNTAIL, type, id, direction);
- CTCM_DBF_TEXT(ERROR, CTC_DBF_ERROR, buf);
} else {
if (ch->flags & CHANNEL_FLAGS_INUSE)
ch = NULL;
@@ -283,19 +275,20 @@ static long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb)
if (!IS_ERR(irb))
return 0;
- CTCM_DBF_TEXT_(ERROR, CTC_DBF_WARN, "irb error %ld on device %s\n",
- PTR_ERR(irb), cdev->dev.bus_id);
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_WARN,
+ "irb error %ld on device %s\n",
+ PTR_ERR(irb), dev_name(&cdev->dev));
switch (PTR_ERR(irb)) {
case -EIO:
- ctcm_pr_warn("i/o-error on device %s\n", cdev->dev.bus_id);
+ ctcm_pr_warn("i/o-error on device %s\n", dev_name(&cdev->dev));
break;
case -ETIMEDOUT:
- ctcm_pr_warn("timeout on device %s\n", cdev->dev.bus_id);
+ ctcm_pr_warn("timeout on device %s\n", dev_name(&cdev->dev));
break;
default:
ctcm_pr_warn("unknown error %ld on device %s\n",
- PTR_ERR(irb), cdev->dev.bus_id);
+ PTR_ERR(irb), dev_name(&cdev->dev));
}
return PTR_ERR(irb);
}
@@ -307,58 +300,85 @@ static long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb)
* ch The channel, the sense code belongs to.
* sense The sense code to inspect.
*/
-static inline void ccw_unit_check(struct channel *ch, unsigned char sense)
+static inline void ccw_unit_check(struct channel *ch, __u8 sense)
{
- CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "%s(%s): %02x",
+ CTCM_FUNTAIL, ch->id, sense);
+
if (sense & SNS0_INTERVENTION_REQ) {
if (sense & 0x01) {
- ctcm_pr_debug("%s: Interface disc. or Sel. reset "
- "(remote)\n", ch->id);
+ if (ch->sense_rc != 0x01) {
+ ctcm_pr_debug("%s: Interface disc. or Sel. "
+ "reset (remote)\n", ch->id);
+ ch->sense_rc = 0x01;
+ }
fsm_event(ch->fsm, CTC_EVENT_UC_RCRESET, ch);
} else {
- ctcm_pr_debug("%s: System reset (remote)\n", ch->id);
+ if (ch->sense_rc != SNS0_INTERVENTION_REQ) {
+ ctcm_pr_debug("%s: System reset (remote)\n",
+ ch->id);
+ ch->sense_rc = SNS0_INTERVENTION_REQ;
+ }
fsm_event(ch->fsm, CTC_EVENT_UC_RSRESET, ch);
}
} else if (sense & SNS0_EQUIPMENT_CHECK) {
if (sense & SNS0_BUS_OUT_CHECK) {
- ctcm_pr_warn("%s: Hardware malfunction (remote)\n",
- ch->id);
+ if (ch->sense_rc != SNS0_BUS_OUT_CHECK) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): remote HW error %02x",
+ CTCM_FUNTAIL, ch->id, sense);
+ ch->sense_rc = SNS0_BUS_OUT_CHECK;
+ }
fsm_event(ch->fsm, CTC_EVENT_UC_HWFAIL, ch);
} else {
- ctcm_pr_warn("%s: Read-data parity error (remote)\n",
- ch->id);
+ if (ch->sense_rc != SNS0_EQUIPMENT_CHECK) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): remote read parity error %02x",
+ CTCM_FUNTAIL, ch->id, sense);
+ ch->sense_rc = SNS0_EQUIPMENT_CHECK;
+ }
fsm_event(ch->fsm, CTC_EVENT_UC_RXPARITY, ch);
}
} else if (sense & SNS0_BUS_OUT_CHECK) {
- if (sense & 0x04) {
- ctcm_pr_warn("%s: Data-streaming timeout)\n", ch->id);
+ if (ch->sense_rc != SNS0_BUS_OUT_CHECK) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): BUS OUT error %02x",
+ CTCM_FUNTAIL, ch->id, sense);
+ ch->sense_rc = SNS0_BUS_OUT_CHECK;
+ }
+ if (sense & 0x04) /* data-streaming timeout */
fsm_event(ch->fsm, CTC_EVENT_UC_TXTIMEOUT, ch);
- } else {
- ctcm_pr_warn("%s: Data-transfer parity error\n",
- ch->id);
+ else /* Data-transfer parity error */
fsm_event(ch->fsm, CTC_EVENT_UC_TXPARITY, ch);
- }
} else if (sense & SNS0_CMD_REJECT) {
- ctcm_pr_warn("%s: Command reject\n", ch->id);
+ if (ch->sense_rc != SNS0_CMD_REJECT) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): Command rejected",
+ CTCM_FUNTAIL, ch->id);
+ ch->sense_rc = SNS0_CMD_REJECT;
+ }
} else if (sense == 0) {
- ctcm_pr_debug("%s: Unit check ZERO\n", ch->id);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): Unit check ZERO",
+ CTCM_FUNTAIL, ch->id);
fsm_event(ch->fsm, CTC_EVENT_UC_ZERO, ch);
} else {
- ctcm_pr_warn("%s: Unit Check with sense code: %02x\n",
- ch->id, sense);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): Unit check code %02x unknown",
+ CTCM_FUNTAIL, ch->id, sense);
fsm_event(ch->fsm, CTC_EVENT_UC_UNKNOWN, ch);
}
}
int ctcm_ch_alloc_buffer(struct channel *ch)
{
- CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__);
-
clear_normalized_cda(&ch->ccw[1]);
ch->trans_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC | GFP_DMA);
if (ch->trans_skb == NULL) {
- ctcm_pr_warn("%s: Couldn't alloc %s trans_skb\n",
- ch->id,
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): %s trans_skb allocation error",
+ CTCM_FUNTAIL, ch->id,
(CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
return -ENOMEM;
}
@@ -367,9 +387,9 @@ int ctcm_ch_alloc_buffer(struct channel *ch)
if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
dev_kfree_skb(ch->trans_skb);
ch->trans_skb = NULL;
- ctcm_pr_warn("%s: set_normalized_cda for %s "
- "trans_skb failed, dropping packets\n",
- ch->id,
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): %s set norm_cda failed",
+ CTCM_FUNTAIL, ch->id,
(CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
return -ENOMEM;
}
@@ -394,7 +414,7 @@ int ctcm_ch_alloc_buffer(struct channel *ch)
*/
int ctcm_open(struct net_device *dev)
{
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
CTCMY_DBF_DEV_NAME(SETUP, dev, "");
if (!IS_MPC(priv))
@@ -412,7 +432,7 @@ int ctcm_open(struct net_device *dev)
*/
int ctcm_close(struct net_device *dev)
{
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
CTCMY_DBF_DEV_NAME(SETUP, dev, "");
if (!IS_MPC(priv))
@@ -516,7 +536,7 @@ static int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb)
atomic_dec(&skb->users);
skb_pull(skb, LL_HEADER_LENGTH + 2);
ctcm_clear_busy(ch->netdev);
- return -EBUSY;
+ return -ENOMEM;
}
skb_reset_tail_pointer(ch->trans_skb);
@@ -553,7 +573,7 @@ static int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb)
skb_pull(skb, LL_HEADER_LENGTH + 2);
} else if (ccw_idx == 0) {
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
priv->stats.tx_packets++;
priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
}
@@ -570,15 +590,12 @@ static void ctcmpc_send_sweep_req(struct channel *rch)
struct th_sweep *header;
struct sk_buff *sweep_skb;
struct channel *ch;
- int rc = 0;
+ /* int rc = 0; */
- priv = dev->priv;
+ priv = dev->ml_priv;
grp = priv->mpcg;
ch = priv->channel[WRITE];
- if (do_debug)
- MPC_DBF_DEV_NAME(TRACE, dev, ch->id);
-
/* sweep processing is not complete until response and request */
/* has completed for all read channels in group */
if (grp->in_sweep == 0) {
@@ -590,17 +607,16 @@ static void ctcmpc_send_sweep_req(struct channel *rch)
sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA);
if (sweep_skb == NULL) {
- printk(KERN_INFO "Couldn't alloc sweep_skb\n");
- rc = -ENOMEM;
- goto done;
+ /* rc = -ENOMEM; */
+ goto nomem;
}
header = kmalloc(TH_SWEEP_LENGTH, gfp_type());
if (!header) {
dev_kfree_skb_any(sweep_skb);
- rc = -ENOMEM;
- goto done;
+ /* rc = -ENOMEM; */
+ goto nomem;
}
header->th.th_seg = 0x00 ;
@@ -621,12 +637,10 @@ static void ctcmpc_send_sweep_req(struct channel *rch)
return;
-done:
- if (rc != 0) {
- grp->in_sweep = 0;
- ctcm_clear_busy(dev);
- fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
- }
+nomem:
+ grp->in_sweep = 0;
+ ctcm_clear_busy(dev);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
return;
}
@@ -638,7 +652,7 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
{
struct pdu *p_header;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct th_header *header;
struct sk_buff *nskb;
@@ -648,11 +662,9 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
unsigned long saveflags = 0; /* avoids compiler warning */
__u16 block_len;
- if (do_debug)
- ctcm_pr_debug(
- "ctcm enter: %s(): %s cp=%i ch=0x%p id=%s state=%s\n",
- __FUNCTION__, dev->name, smp_processor_id(), ch,
- ch->id, fsm_getstate_str(ch->fsm));
+ CTCM_PR_DEBUG("Enter %s: %s, cp=%i ch=0x%p id=%s state=%s\n",
+ __func__, dev->name, smp_processor_id(), ch,
+ ch->id, fsm_getstate_str(ch->fsm));
if ((fsm_getstate(ch->fsm) != CTC_STATE_TXIDLE) || grp->in_sweep) {
spin_lock_irqsave(&ch->collect_lock, saveflags);
@@ -660,14 +672,8 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
p_header = kmalloc(PDU_HEADER_LENGTH, gfp_type());
if (!p_header) {
- printk(KERN_WARNING "ctcm: OUT OF MEMORY IN %s():"
- " Data Lost \n", __FUNCTION__);
-
- atomic_dec(&skb->users);
- dev_kfree_skb_any(skb);
spin_unlock_irqrestore(&ch->collect_lock, saveflags);
- fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
- goto done;
+ goto nomem_exit;
}
p_header->pdu_offset = skb->len;
@@ -682,13 +688,10 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header,
PDU_HEADER_LENGTH);
- if (do_debug_data) {
- ctcm_pr_debug("ctcm: %s() Putting on collect_q"
- " - skb len: %04x \n", __FUNCTION__, skb->len);
- ctcm_pr_debug("ctcm: %s() pdu header and data"
- " for up to 32 bytes\n", __FUNCTION__);
- ctcmpc_dump32((char *)skb->data, skb->len);
- }
+ CTCM_PR_DEBUG("%s(%s): Put on collect_q - skb len: %04x \n"
+ "pdu header and data for up to 32 bytes:\n",
+ __func__, dev->name, skb->len);
+ CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len));
skb_queue_tail(&ch->collect_queue, skb);
ch->collect_len += skb->len;
@@ -713,12 +716,7 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
if (hi) {
nskb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
if (!nskb) {
- printk(KERN_WARNING "ctcm: %s() OUT OF MEMORY"
- "- Data Lost \n", __FUNCTION__);
- atomic_dec(&skb->users);
- dev_kfree_skb_any(skb);
- fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
- goto done;
+ goto nomem_exit;
} else {
memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
atomic_inc(&nskb->users);
@@ -730,15 +728,8 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
p_header = kmalloc(PDU_HEADER_LENGTH, gfp_type());
- if (!p_header) {
- printk(KERN_WARNING "ctcm: %s() OUT OF MEMORY"
- ": Data Lost \n", __FUNCTION__);
-
- atomic_dec(&skb->users);
- dev_kfree_skb_any(skb);
- fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
- goto done;
- }
+ if (!p_header)
+ goto nomem_exit;
p_header->pdu_offset = skb->len;
p_header->pdu_proto = 0x01;
@@ -768,15 +759,8 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
ch->prof.txlen += skb->len - PDU_HEADER_LENGTH;
header = kmalloc(TH_HEADER_LENGTH, gfp_type());
-
- if (!header) {
- printk(KERN_WARNING "ctcm: %s() OUT OF MEMORY: Data Lost \n",
- __FUNCTION__);
- atomic_dec(&skb->users);
- dev_kfree_skb_any(skb);
- fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
- goto done;
- }
+ if (!header)
+ goto nomem_exit;
header->th_seg = 0x00;
header->th_ch_flag = TH_HAS_PDU; /* Normal data */
@@ -785,41 +769,31 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
ch->th_seq_num++;
header->th_seq_num = ch->th_seq_num;
- if (do_debug_data)
- ctcm_pr_debug("ctcm: %s() ToVTAM_th_seq= %08x\n" ,
- __FUNCTION__, ch->th_seq_num);
+ CTCM_PR_DBGDATA("%s(%s) ToVTAM_th_seq= %08x\n" ,
+ __func__, dev->name, ch->th_seq_num);
/* put the TH on the packet */
memcpy(skb_push(skb, TH_HEADER_LENGTH), header, TH_HEADER_LENGTH);
kfree(header);
- if (do_debug_data) {
- ctcm_pr_debug("ctcm: %s(): skb len: %04x \n",
- __FUNCTION__, skb->len);
- ctcm_pr_debug("ctcm: %s(): pdu header and data for up to 32 "
- "bytes sent to vtam\n", __FUNCTION__);
- ctcmpc_dump32((char *)skb->data, skb->len);
- }
+ CTCM_PR_DBGDATA("%s(%s): skb len: %04x\n - pdu header and data for "
+ "up to 32 bytes sent to vtam:\n",
+ __func__, dev->name, skb->len);
+ CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len));
ch->ccw[4].count = skb->len;
if (set_normalized_cda(&ch->ccw[4], skb->data)) {
/*
- * idal allocation failed, try via copying to
- * trans_skb. trans_skb usually has a pre-allocated
- * idal.
+ * idal allocation failed, try via copying to trans_skb.
+ * trans_skb usually has a pre-allocated idal.
*/
if (ctcm_checkalloc_buffer(ch)) {
/*
- * Remove our header. It gets added
- * again on retransmit.
+ * Remove our header.
+ * It gets added again on retransmit.
*/
- atomic_dec(&skb->users);
- dev_kfree_skb_any(skb);
- printk(KERN_WARNING "ctcm: %s()OUT OF MEMORY:"
- " Data Lost \n", __FUNCTION__);
- fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
- goto done;
+ goto nomem_exit;
}
skb_reset_tail_pointer(ch->trans_skb);
@@ -829,14 +803,11 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
atomic_dec(&skb->users);
dev_kfree_skb_irq(skb);
ccw_idx = 0;
- if (do_debug_data) {
- ctcm_pr_debug("ctcm: %s() TRANS skb len: %d \n",
- __FUNCTION__, ch->trans_skb->len);
- ctcm_pr_debug("ctcm: %s up to 32 bytes of data"
- " sent to vtam\n", __FUNCTION__);
- ctcmpc_dump32((char *)ch->trans_skb->data,
- ch->trans_skb->len);
- }
+ CTCM_PR_DBGDATA("%s(%s): trans_skb len: %04x\n"
+ "up to 32 bytes sent to vtam:\n",
+ __func__, dev->name, ch->trans_skb->len);
+ CTCM_D3_DUMP((char *)ch->trans_skb->data,
+ min_t(int, 32, ch->trans_skb->len));
} else {
skb_queue_tail(&ch->io_queue, skb);
ccw_idx = 3;
@@ -865,13 +836,21 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
priv->stats.tx_packets++;
priv->stats.tx_bytes += skb->len - TH_HEADER_LENGTH;
}
- if (ch->th_seq_num > 0xf0000000) /* Chose 4Billion at random. */
+ if (ch->th_seq_num > 0xf0000000) /* Chose at random. */
ctcmpc_send_sweep_req(ch);
+ goto done;
+nomem_exit:
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_CRIT,
+ "%s(%s): MEMORY allocation ERROR\n",
+ CTCM_FUNTAIL, ch->id);
+ rc = -ENOMEM;
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
done:
- if (do_debug)
- ctcm_pr_debug("ctcm exit: %s %s()\n", dev->name, __FUNCTION__);
- return 0;
+ CTCM_PR_DEBUG("Exit %s(%s)\n", __func__, dev->name);
+ return rc;
}
/**
@@ -888,20 +867,19 @@ done:
/* first merge version - leaving both functions separated */
static int ctcm_tx(struct sk_buff *skb, struct net_device *dev)
{
- int rc = 0;
- struct ctcm_priv *priv;
-
- CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__);
- priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
if (skb == NULL) {
- ctcm_pr_warn("%s: NULL sk_buff passed\n", dev->name);
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): NULL sk_buff passed",
+ CTCM_FUNTAIL, dev->name);
priv->stats.tx_dropped++;
return 0;
}
if (skb_headroom(skb) < (LL_HEADER_LENGTH + 2)) {
- ctcm_pr_warn("%s: Got sk_buff with head room < %ld bytes\n",
- dev->name, LL_HEADER_LENGTH + 2);
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): Got sk_buff with head room < %ld bytes",
+ CTCM_FUNTAIL, dev->name, LL_HEADER_LENGTH + 2);
dev_kfree_skb(skb);
priv->stats.tx_dropped++;
return 0;
@@ -925,51 +903,43 @@ static int ctcm_tx(struct sk_buff *skb, struct net_device *dev)
dev->trans_start = jiffies;
if (ctcm_transmit_skb(priv->channel[WRITE], skb) != 0)
- rc = 1;
- return rc;
+ return 1;
+ return 0;
}
/* unmerged MPC variant of ctcm_tx */
static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev)
{
int len = 0;
- struct ctcm_priv *priv = NULL;
- struct mpc_group *grp = NULL;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
struct sk_buff *newskb = NULL;
- if (do_debug)
- ctcm_pr_debug("ctcmpc enter: %s(): skb:%0lx\n",
- __FUNCTION__, (unsigned long)skb);
-
- CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
- "ctcmpc enter: %s(): skb:%0lx\n",
- __FUNCTION__, (unsigned long)skb);
-
- priv = dev->priv;
- grp = priv->mpcg;
/*
* Some sanity checks ...
*/
if (skb == NULL) {
- ctcm_pr_warn("ctcmpc: %s: NULL sk_buff passed\n", dev->name);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): NULL sk_buff passed",
+ CTCM_FUNTAIL, dev->name);
priv->stats.tx_dropped++;
goto done;
}
if (skb_headroom(skb) < (TH_HEADER_LENGTH + PDU_HEADER_LENGTH)) {
- CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_WARN,
- "%s: Got sk_buff with head room < %ld bytes\n",
- dev->name, TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR,
+ "%s(%s): Got sk_buff with head room < %ld bytes",
+ CTCM_FUNTAIL, dev->name,
+ TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
- if (do_debug_data)
- ctcmpc_dump32((char *)skb->data, skb->len);
+ CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len));
len = skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
newskb = __dev_alloc_skb(len, gfp_type() | GFP_DMA);
if (!newskb) {
- printk(KERN_WARNING "ctcmpc: %s() OUT OF MEMORY-"
- "Data Lost\n",
- __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR,
+ "%s: %s: __dev_alloc_skb failed",
+ __func__, dev->name);
dev_kfree_skb_any(skb);
priv->stats.tx_dropped++;
@@ -993,9 +963,9 @@ static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev)
if ((fsm_getstate(priv->fsm) != DEV_STATE_RUNNING) ||
(fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) {
dev_kfree_skb_any(skb);
- printk(KERN_INFO "ctcmpc: %s() DATA RCVD - MPC GROUP "
- "NOT ACTIVE - DROPPED\n",
- __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): inactive MPCGROUP - dropped",
+ CTCM_FUNTAIL, dev->name);
priv->stats.tx_dropped++;
priv->stats.tx_errors++;
priv->stats.tx_carrier_errors++;
@@ -1003,8 +973,9 @@ static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev)
}
if (ctcm_test_and_set_busy(dev)) {
- printk(KERN_WARNING "%s:DEVICE ERR - UNRECOVERABLE DATA LOSS\n",
- __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): device busy - dropped",
+ CTCM_FUNTAIL, dev->name);
dev_kfree_skb_any(skb);
priv->stats.tx_dropped++;
priv->stats.tx_errors++;
@@ -1015,12 +986,9 @@ static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev)
dev->trans_start = jiffies;
if (ctcmpc_transmit_skb(priv->channel[WRITE], skb) != 0) {
- printk(KERN_WARNING "ctcmpc: %s() DEVICE ERROR"
- ": Data Lost \n",
- __FUNCTION__);
- printk(KERN_WARNING "ctcmpc: %s() DEVICE ERROR"
- " - UNRECOVERABLE DATA LOSS\n",
- __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): device error - dropped",
+ CTCM_FUNTAIL, dev->name);
dev_kfree_skb_any(skb);
priv->stats.tx_dropped++;
priv->stats.tx_errors++;
@@ -1054,12 +1022,10 @@ static int ctcm_change_mtu(struct net_device *dev, int new_mtu)
struct ctcm_priv *priv;
int max_bufsize;
- CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__);
-
if (new_mtu < 576 || new_mtu > 65527)
return -EINVAL;
- priv = dev->priv;
+ priv = dev->ml_priv;
max_bufsize = priv->channel[READ]->max_bufsize;
if (IS_MPC(priv)) {
@@ -1084,22 +1050,7 @@ static int ctcm_change_mtu(struct net_device *dev, int new_mtu)
*/
static struct net_device_stats *ctcm_stats(struct net_device *dev)
{
- return &((struct ctcm_priv *)dev->priv)->stats;
-}
-
-
-static void ctcm_netdev_unregister(struct net_device *dev)
-{
- CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__);
- if (!dev)
- return;
- unregister_netdev(dev);
-}
-
-static int ctcm_netdev_register(struct net_device *dev)
-{
- CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__);
- return register_netdev(dev);
+ return &((struct ctcm_priv *)dev->ml_priv)->stats;
}
static void ctcm_free_netdevice(struct net_device *dev)
@@ -1107,11 +1058,9 @@ static void ctcm_free_netdevice(struct net_device *dev)
struct ctcm_priv *priv;
struct mpc_group *grp;
- CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__);
-
- if (!dev)
- return;
- priv = dev->priv;
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "%s(%s)", CTCM_FUNTAIL, dev->name);
+ priv = dev->ml_priv;
if (priv) {
grp = priv->mpcg;
if (grp) {
@@ -1171,10 +1120,12 @@ static struct net_device *ctcm_init_netdevice(struct ctcm_priv *priv)
dev = alloc_netdev(0, CTC_DEVICE_GENE, ctcm_dev_setup);
if (!dev) {
- ctcm_pr_err("%s: Out of memory\n", __FUNCTION__);
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT,
+ "%s: MEMORY allocation ERROR",
+ CTCM_FUNTAIL);
return NULL;
}
- dev->priv = priv;
+ dev->ml_priv = priv;
priv->fsm = init_fsm("ctcmdev", dev_state_names, dev_event_names,
CTCM_NR_DEV_STATES, CTCM_NR_DEV_EVENTS,
dev_fsm, dev_fsm_len, GFP_KERNEL);
@@ -1209,6 +1160,7 @@ static struct net_device *ctcm_init_netdevice(struct ctcm_priv *priv)
}
CTCMY_DBF_DEV(SETUP, dev, "finished");
+
return dev;
}
@@ -1226,18 +1178,24 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
struct net_device *dev;
struct ctcm_priv *priv;
struct ccwgroup_device *cgdev;
+ int cstat;
+ int dstat;
+
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "Enter %s(%s)", CTCM_FUNTAIL, dev_name(&cdev->dev));
- CTCM_DBF_TEXT(TRACE, CTC_DBF_DEBUG, __FUNCTION__);
if (ctcm_check_irb_error(cdev, irb))
return;
cgdev = dev_get_drvdata(&cdev->dev);
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
+
/* Check for unsolicited interrupts. */
if (cgdev == NULL) {
- ctcm_pr_warn("ctcm: Got unsolicited irq: %s c-%02x d-%02x\n",
- cdev->dev.bus_id, irb->scsw.cstat,
- irb->scsw.dstat);
+ ctcm_pr_warn("ctcm: Got unsolicited irq: c-%02x d-%02x\n",
+ cstat, dstat);
return;
}
@@ -1250,56 +1208,57 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
ch = priv->channel[WRITE];
else {
ctcm_pr_err("ctcm: Can't determine channel for interrupt, "
- "device %s\n", cdev->dev.bus_id);
+ "device %s\n", dev_name(&cdev->dev));
return;
}
- dev = (struct net_device *)(ch->netdev);
+ dev = ch->netdev;
if (dev == NULL) {
ctcm_pr_crit("ctcm: %s dev=NULL bus_id=%s, ch=0x%p\n",
- __FUNCTION__, cdev->dev.bus_id, ch);
+ __func__, dev_name(&cdev->dev), ch);
return;
}
- if (do_debug)
- ctcm_pr_debug("%s: interrupt for device: %s "
- "received c-%02x d-%02x\n",
- dev->name,
- ch->id,
- irb->scsw.cstat,
- irb->scsw.dstat);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
+ "%s(%s): int. for %s: cstat=%02x dstat=%02x",
+ CTCM_FUNTAIL, dev->name, ch->id, cstat, dstat);
/* Copy interruption response block. */
memcpy(ch->irb, irb, sizeof(struct irb));
+ if (irb->scsw.cmd.cstat) {
/* Check for good subchannel return code, otherwise error message */
- if (irb->scsw.cstat) {
fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch);
ctcm_pr_warn("%s: subchannel check for dev: %s - %02x %02x\n",
- dev->name, ch->id, irb->scsw.cstat,
- irb->scsw.dstat);
+ dev->name, ch->id, irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
return;
}
/* Check the reason-code of a unit check */
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
+ if ((irb->ecw[0] & ch->sense_rc) == 0)
+ /* print it only once */
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
+ "%s(%s): sense=%02x, ds=%02x",
+ CTCM_FUNTAIL, ch->id, irb->ecw[0], dstat);
ccw_unit_check(ch, irb->ecw[0]);
return;
}
- if (irb->scsw.dstat & DEV_STAT_BUSY) {
- if (irb->scsw.dstat & DEV_STAT_ATTENTION)
+ if (irb->scsw.cmd.dstat & DEV_STAT_BUSY) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
fsm_event(ch->fsm, CTC_EVENT_ATTNBUSY, ch);
else
fsm_event(ch->fsm, CTC_EVENT_BUSY, ch);
return;
}
- if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
fsm_event(ch->fsm, CTC_EVENT_ATTN, ch);
return;
}
- if ((irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (irb->scsw.stctl ==
+ if ((irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (irb->scsw.cmd.stctl ==
(SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))
fsm_event(ch->fsm, CTC_EVENT_FINSTAT, ch);
else
@@ -1320,14 +1279,18 @@ static int ctcm_probe_device(struct ccwgroup_device *cgdev)
struct ctcm_priv *priv;
int rc;
- CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s %p", __FUNCTION__, cgdev);
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "%s %p",
+ __func__, cgdev);
if (!get_device(&cgdev->dev))
return -ENODEV;
priv = kzalloc(sizeof(struct ctcm_priv), GFP_KERNEL);
if (!priv) {
- ctcm_pr_err("%s: Out of memory\n", __FUNCTION__);
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s: memory allocation failure",
+ CTCM_FUNTAIL);
put_device(&cgdev->dev);
return -ENOMEM;
}
@@ -1364,10 +1327,13 @@ static int add_channel(struct ccw_device *cdev, enum channel_types type,
int ccw_num;
int rc = 0;
- CTCM_DBF_TEXT(TRACE, 2, __FUNCTION__);
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "%s(%s), type %d, proto %d",
+ __func__, dev_name(&cdev->dev), type, priv->protocol);
+
ch = kzalloc(sizeof(struct channel), GFP_KERNEL);
if (ch == NULL)
- goto nomem_return;
+ return -ENOMEM;
ch->protocol = priv->protocol;
if (IS_MPC(priv)) {
@@ -1392,7 +1358,7 @@ static int add_channel(struct ccw_device *cdev, enum channel_types type,
goto nomem_return;
ch->cdev = cdev;
- snprintf(ch->id, CTCM_ID_SIZE, "ch-%s", cdev->dev.bus_id);
+ snprintf(ch->id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev->dev));
ch->type = type;
/**
@@ -1478,7 +1444,7 @@ static int add_channel(struct ccw_device *cdev, enum channel_types type,
if (*c && (!strncmp((*c)->id, ch->id, CTCM_ID_SIZE))) {
CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
"%s (%s) already in list, using old entry",
- __FUNCTION__, (*c)->id);
+ __func__, (*c)->id);
goto free_return;
}
@@ -1498,11 +1464,10 @@ static int add_channel(struct ccw_device *cdev, enum channel_types type,
return 0;
nomem_return:
- ctcm_pr_warn("ctcm: Out of memory in %s\n", __FUNCTION__);
rc = -ENOMEM;
free_return: /* note that all channel pointers are 0 or valid */
- kfree(ch->ccw); /* TODO: check that again */
+ kfree(ch->ccw);
kfree(ch->discontact_th);
kfree_fsm(ch->fsm);
kfree(ch->irb);
@@ -1540,48 +1505,48 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
enum channel_types type;
struct ctcm_priv *priv;
struct net_device *dev;
+ struct ccw_device *cdev0;
+ struct ccw_device *cdev1;
int ret;
- CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__);
-
priv = dev_get_drvdata(&cgdev->dev);
if (!priv)
return -ENODEV;
- type = get_channel_type(&cgdev->cdev[0]->id);
+ cdev0 = cgdev->cdev[0];
+ cdev1 = cgdev->cdev[1];
- snprintf(read_id, CTCM_ID_SIZE, "ch-%s", cgdev->cdev[0]->dev.bus_id);
- snprintf(write_id, CTCM_ID_SIZE, "ch-%s", cgdev->cdev[1]->dev.bus_id);
+ type = get_channel_type(&cdev0->id);
- ret = add_channel(cgdev->cdev[0], type, priv);
+ snprintf(read_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev0->dev));
+ snprintf(write_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev1->dev));
+
+ ret = add_channel(cdev0, type, priv);
if (ret)
return ret;
- ret = add_channel(cgdev->cdev[1], type, priv);
+ ret = add_channel(cdev1, type, priv);
if (ret)
return ret;
- ret = ccw_device_set_online(cgdev->cdev[0]);
+ ret = ccw_device_set_online(cdev0);
if (ret != 0) {
- CTCM_DBF_TEXT(SETUP, CTC_DBF_WARN,
- "ccw_device_set_online (cdev[0]) failed ");
- ctcm_pr_warn("ccw_device_set_online (cdev[0]) failed "
- "with ret = %d\n", ret);
+ /* may be ok to fail now - can be done later */
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s) set_online rc=%d",
+ CTCM_FUNTAIL, read_id, ret);
}
- ret = ccw_device_set_online(cgdev->cdev[1]);
+ ret = ccw_device_set_online(cdev1);
if (ret != 0) {
- CTCM_DBF_TEXT(SETUP, CTC_DBF_WARN,
- "ccw_device_set_online (cdev[1]) failed ");
- ctcm_pr_warn("ccw_device_set_online (cdev[1]) failed "
- "with ret = %d\n", ret);
+ /* may be ok to fail now - can be done later */
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
+ "%s(%s) set_online rc=%d",
+ CTCM_FUNTAIL, write_id, ret);
}
dev = ctcm_init_netdevice(priv);
-
- if (dev == NULL) {
- ctcm_pr_warn("ctcm_init_netdevice failed\n");
- goto out;
- }
+ if (dev == NULL)
+ goto out;
for (direction = READ; direction <= WRITE; direction++) {
priv->channel[direction] =
@@ -1590,8 +1555,7 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
if (priv->channel[direction] == NULL) {
if (direction == WRITE)
channel_free(priv->channel[READ]);
- ctcm_free_netdevice(dev);
- goto out;
+ goto out_dev;
}
priv->channel[direction]->netdev = dev;
priv->channel[direction]->protocol = priv->protocol;
@@ -1600,26 +1564,24 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
/* sysfs magic */
SET_NETDEV_DEV(dev, &cgdev->dev);
- if (ctcm_netdev_register(dev) != 0) {
- ctcm_free_netdevice(dev);
- goto out;
- }
+ if (register_netdev(dev))
+ goto out_dev;
if (ctcm_add_attributes(&cgdev->dev)) {
- ctcm_netdev_unregister(dev);
-/* dev->priv = NULL; why that ???? */
- ctcm_free_netdevice(dev);
- goto out;
+ unregister_netdev(dev);
+ goto out_dev;
}
strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name));
CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
- "setup(%s) ok : r/w = %s / %s, proto : %d",
- dev->name, priv->channel[READ]->id,
+ "setup(%s) OK : r/w = %s/%s, protocol : %d", dev->name,
+ priv->channel[READ]->id,
priv->channel[WRITE]->id, priv->protocol);
return 0;
+out_dev:
+ ctcm_free_netdevice(dev);
out:
ccw_device_set_offline(cgdev->cdev[1]);
ccw_device_set_offline(cgdev->cdev[0]);
@@ -1658,8 +1620,7 @@ static int ctcm_shutdown_device(struct ccwgroup_device *cgdev)
channel_free(priv->channel[WRITE]);
if (dev) {
- ctcm_netdev_unregister(dev);
-/* dev->priv = NULL; why that ??? */
+ unregister_netdev(dev);
ctcm_free_netdevice(dev);
}
@@ -1682,13 +1643,16 @@ static int ctcm_shutdown_device(struct ccwgroup_device *cgdev)
static void ctcm_remove_device(struct ccwgroup_device *cgdev)
{
- struct ctcm_priv *priv;
+ struct ctcm_priv *priv = dev_get_drvdata(&cgdev->dev);
- CTCM_DBF_TEXT(SETUP, CTC_DBF_ERROR, __FUNCTION__);
+ BUG_ON(priv == NULL);
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "removing device %s, r/w = %s/%s, proto : %d",
+ priv->channel[READ]->netdev->name,
+ priv->channel[READ]->id, priv->channel[WRITE]->id,
+ priv->protocol);
- priv = dev_get_drvdata(&cgdev->dev);
- if (!priv)
- return;
if (cgdev->state == CCWGROUP_ONLINE)
ctcm_shutdown_device(cgdev);
ctcm_remove_files(&cgdev->dev);
@@ -1748,8 +1712,6 @@ static int __init ctcm_init(void)
ret = ctcm_register_dbf_views();
if (ret) {
- ctcm_pr_crit("ctcm_init failed with ctcm_register_dbf_views "
- "rc = %d\n", ret);
return ret;
}
ret = register_cu3088_discipline(&ctcm_group_driver);
diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h
index 95b0c0b6ebc..d77cce3fe4d 100644
--- a/drivers/s390/net/ctcm_main.h
+++ b/drivers/s390/net/ctcm_main.h
@@ -22,9 +22,9 @@
#define CTC_DRIVER_NAME "ctcm"
#define CTC_DEVICE_NAME "ctc"
-#define CTC_DEVICE_GENE "ctc%d"
#define MPC_DEVICE_NAME "mpc"
-#define MPC_DEVICE_GENE "mpc%d"
+#define CTC_DEVICE_GENE CTC_DEVICE_NAME "%d"
+#define MPC_DEVICE_GENE MPC_DEVICE_NAME "%d"
#define CHANNEL_FLAGS_READ 0
#define CHANNEL_FLAGS_WRITE 1
@@ -48,6 +48,30 @@
#define ctcm_pr_err(fmt, arg...) printk(KERN_ERR fmt, ##arg)
#define ctcm_pr_crit(fmt, arg...) printk(KERN_CRIT fmt, ##arg)
+#define CTCM_PR_DEBUG(fmt, arg...) \
+ do { \
+ if (do_debug) \
+ printk(KERN_DEBUG fmt, ##arg); \
+ } while (0)
+
+#define CTCM_PR_DBGDATA(fmt, arg...) \
+ do { \
+ if (do_debug_data) \
+ printk(KERN_DEBUG fmt, ##arg); \
+ } while (0)
+
+#define CTCM_D3_DUMP(buf, len) \
+ do { \
+ if (do_debug_data) \
+ ctcmpc_dumpit(buf, len); \
+ } while (0)
+
+#define CTCM_CCW_DUMP(buf, len) \
+ do { \
+ if (do_debug_ccw) \
+ ctcmpc_dumpit(buf, len); \
+ } while (0)
+
/*
* CCW commands, used in this driver.
*/
@@ -80,7 +104,7 @@
#define READ 0
#define WRITE 1
-#define CTCM_ID_SIZE BUS_ID_SIZE+3
+#define CTCM_ID_SIZE 20+3
struct ctcm_profile {
unsigned long maxmulti;
@@ -161,8 +185,9 @@ struct channel {
fsm_instance *fsm; /* finite state machine of this channel */
struct net_device *netdev; /* corresponding net_device */
struct ctcm_profile prof;
- unsigned char *trans_skb_data;
+ __u8 *trans_skb_data;
__u16 logflags;
+ __u8 sense_rc; /* last unit check sense code report control */
};
struct ctcm_priv {
@@ -204,14 +229,14 @@ void ctcm_remove_files(struct device *dev);
*/
static inline void ctcm_clear_busy_do(struct net_device *dev)
{
- clear_bit(0, &(((struct ctcm_priv *)dev->priv)->tbusy));
+ clear_bit(0, &(((struct ctcm_priv *)dev->ml_priv)->tbusy));
netif_wake_queue(dev);
}
static inline void ctcm_clear_busy(struct net_device *dev)
{
struct mpc_group *grp;
- grp = ((struct ctcm_priv *)dev->priv)->mpcg;
+ grp = ((struct ctcm_priv *)dev->ml_priv)->mpcg;
if (!(grp && grp->in_sweep))
ctcm_clear_busy_do(dev);
@@ -221,7 +246,8 @@ static inline void ctcm_clear_busy(struct net_device *dev)
static inline int ctcm_test_and_set_busy(struct net_device *dev)
{
netif_stop_queue(dev);
- return test_and_set_bit(0, &(((struct ctcm_priv *)dev->priv)->tbusy));
+ return test_and_set_bit(0,
+ &(((struct ctcm_priv *)dev->ml_priv)->tbusy));
}
extern int loglevel;
@@ -267,7 +293,7 @@ struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv);
#define IS_MPC(p) ((p)->protocol == CTCM_PROTO_MPC)
/* test if struct ctcm_priv of struct net_device has MPC protocol setting */
-#define IS_MPCDEV(d) IS_MPC((struct ctcm_priv *)d->priv)
+#define IS_MPCDEV(dev) IS_MPC((struct ctcm_priv *)dev->ml_priv)
static inline gfp_t gfp_type(void)
{
diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c
index 044addee64a..cbe470493bf 100644
--- a/drivers/s390/net/ctcm_mpc.c
+++ b/drivers/s390/net/ctcm_mpc.c
@@ -19,7 +19,6 @@
#undef DEBUGDATA
#undef DEBUGCCW
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -149,7 +148,7 @@ void ctcmpc_dumpit(char *buf, int len)
for (ct = 0; ct < len; ct++, ptr++, rptr++) {
if (sw == 0) {
#if (UTS_MACHINE == s390x)
- sprintf(addr, "%16.16lx", (unsigned long)rptr);
+ sprintf(addr, "%16.16lx", (__u64)rptr);
#else
sprintf(addr, "%8.8X", (__u32)rptr);
#endif
@@ -164,7 +163,7 @@ void ctcmpc_dumpit(char *buf, int len)
strcat(bhex, " ");
#if (UTS_MACHINE == s390x)
- sprintf(tbuf, "%2.2lX", (unsigned long)*ptr);
+ sprintf(tbuf, "%2.2lX", (__u64)*ptr);
#else
sprintf(tbuf, "%2.2X", (__u32)*ptr);
#endif
@@ -179,24 +178,24 @@ void ctcmpc_dumpit(char *buf, int len)
basc[sw+1] = '\0';
sw++;
rm--;
- if (sw == 16) {
- if ((strcmp(duphex, bhex)) != 0) {
- if (dup != 0) {
- sprintf(tdup, "Duplicate as above "
- "to %s", addr);
- printk(KERN_INFO " "
- " --- %s ---\n", tdup);
- }
- printk(KERN_INFO " %s (+%s) : %s [%s]\n",
+ if (sw != 16)
+ continue;
+ if ((strcmp(duphex, bhex)) != 0) {
+ if (dup != 0) {
+ sprintf(tdup,
+ "Duplicate as above to %s", addr);
+ ctcm_pr_debug(" --- %s ---\n",
+ tdup);
+ }
+ ctcm_pr_debug(" %s (+%s) : %s [%s]\n",
addr, boff, bhex, basc);
- dup = 0;
- strcpy(duphex, bhex);
- } else
- dup++;
+ dup = 0;
+ strcpy(duphex, bhex);
+ } else
+ dup++;
- sw = 0;
- rm = 16;
- }
+ sw = 0;
+ rm = 16;
} /* endfor */
if (sw != 0) {
@@ -210,19 +209,17 @@ void ctcmpc_dumpit(char *buf, int len)
}
if (dup != 0) {
sprintf(tdup, "Duplicate as above to %s", addr);
- printk(KERN_INFO " "
- " --- %s ---\n", tdup);
+ ctcm_pr_debug(" --- %s ---\n", tdup);
}
- printk(KERN_INFO " %s (+%s) : %s [%s]\n",
- addr, boff, bhex, basc);
+ ctcm_pr_debug(" %s (+%s) : %s [%s]\n",
+ addr, boff, bhex, basc);
} else {
if (dup >= 1) {
sprintf(tdup, "Duplicate as above to %s", addr);
- printk(KERN_INFO " "
- " --- %s ---\n", tdup);
+ ctcm_pr_debug(" --- %s ---\n", tdup);
}
if (dup != 0) {
- printk(KERN_INFO " %s (+%s) : %s [%s]\n",
+ ctcm_pr_debug(" %s (+%s) : %s [%s]\n",
addr, boff, bhex, basc);
}
}
@@ -241,7 +238,7 @@ void ctcmpc_dumpit(char *buf, int len)
*/
void ctcmpc_dump_skb(struct sk_buff *skb, int offset)
{
- unsigned char *p = skb->data;
+ __u8 *p = skb->data;
struct th_header *header;
struct pdu *pheader;
int bl = skb->len;
@@ -253,8 +250,8 @@ void ctcmpc_dump_skb(struct sk_buff *skb, int offset)
p += offset;
header = (struct th_header *)p;
- printk(KERN_INFO "dump:\n");
- printk(KERN_INFO "skb len=%d \n", skb->len);
+ ctcm_pr_debug("dump:\n");
+ ctcm_pr_debug("skb len=%d \n", skb->len);
if (skb->len > 2) {
switch (header->th_ch_flag) {
case TH_HAS_PDU:
@@ -273,32 +270,64 @@ void ctcmpc_dump_skb(struct sk_buff *skb, int offset)
}
pheader = (struct pdu *)p;
- printk(KERN_INFO "pdu->offset: %d hex: %04x\n",
- pheader->pdu_offset, pheader->pdu_offset);
- printk(KERN_INFO "pdu->flag : %02x\n", pheader->pdu_flag);
- printk(KERN_INFO "pdu->proto : %02x\n", pheader->pdu_proto);
- printk(KERN_INFO "pdu->seq : %02x\n", pheader->pdu_seq);
+ ctcm_pr_debug("pdu->offset: %d hex: %04x\n",
+ pheader->pdu_offset, pheader->pdu_offset);
+ ctcm_pr_debug("pdu->flag : %02x\n", pheader->pdu_flag);
+ ctcm_pr_debug("pdu->proto : %02x\n", pheader->pdu_proto);
+ ctcm_pr_debug("pdu->seq : %02x\n", pheader->pdu_seq);
goto dumpdata;
dumpth:
- printk(KERN_INFO "th->seg : %02x\n", header->th_seg);
- printk(KERN_INFO "th->ch : %02x\n", header->th_ch_flag);
- printk(KERN_INFO "th->blk_flag: %02x\n", header->th_blk_flag);
- printk(KERN_INFO "th->type : %s\n",
- (header->th_is_xid) ? "DATA" : "XID");
- printk(KERN_INFO "th->seqnum : %04x\n", header->th_seq_num);
+ ctcm_pr_debug("th->seg : %02x\n", header->th_seg);
+ ctcm_pr_debug("th->ch : %02x\n", header->th_ch_flag);
+ ctcm_pr_debug("th->blk_flag: %02x\n", header->th_blk_flag);
+ ctcm_pr_debug("th->type : %s\n",
+ (header->th_is_xid) ? "DATA" : "XID");
+ ctcm_pr_debug("th->seqnum : %04x\n", header->th_seq_num);
}
dumpdata:
if (bl > 32)
bl = 32;
- printk(KERN_INFO "data: ");
+ ctcm_pr_debug("data: ");
for (i = 0; i < bl; i++)
- printk(KERN_INFO "%02x%s", *p++, (i % 16) ? " " : "\n<7>");
- printk(KERN_INFO "\n");
+ ctcm_pr_debug("%02x%s", *p++, (i % 16) ? " " : "\n");
+ ctcm_pr_debug("\n");
}
#endif
+static struct net_device *ctcmpc_get_dev(int port_num)
+{
+ char device[20];
+ struct net_device *dev;
+ struct ctcm_priv *priv;
+
+ sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
+
+ dev = __dev_get_by_name(&init_net, device);
+
+ if (dev == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s: Device not found by name: %s",
+ CTCM_FUNTAIL, device);
+ return NULL;
+ }
+ priv = dev->ml_priv;
+ if (priv == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): dev->ml_priv is NULL",
+ CTCM_FUNTAIL, device);
+ return NULL;
+ }
+ if (priv->mpcg == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): priv->mpcg is NULL",
+ CTCM_FUNTAIL, device);
+ return NULL;
+ }
+ return dev;
+}
+
/*
* ctc_mpc_alloc_channel
* (exported interface)
@@ -308,34 +337,23 @@ dumpdata:
*/
int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int))
{
- char device[20];
struct net_device *dev;
struct mpc_group *grp;
struct ctcm_priv *priv;
- ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
-
- sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
- dev = __dev_get_by_name(&init_net, device);
-
- if (dev == NULL) {
- printk(KERN_INFO "ctc_mpc_alloc_channel %s dev=NULL\n", device);
+ dev = ctcmpc_get_dev(port_num);
+ if (dev == NULL)
return 1;
- }
-
- priv = dev->priv;
+ priv = dev->ml_priv;
grp = priv->mpcg;
- if (!grp)
- return 1;
grp->allochanfunc = callback;
grp->port_num = port_num;
grp->port_persist = 1;
- ctcm_pr_debug("ctcmpc: %s called for device %s state=%s\n",
- __FUNCTION__,
- dev->name,
- fsm_getstate_str(grp->fsm));
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
+ "%s(%s): state=%s",
+ CTCM_FUNTAIL, dev->name, fsm_getstate_str(grp->fsm));
switch (fsm_getstate(grp->fsm)) {
case MPCG_STATE_INOP:
@@ -377,12 +395,8 @@ int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int))
grp->allocchan_callback_retries = 0;
}
break;
- default:
- return 0;
-
}
- ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
return 0;
}
EXPORT_SYMBOL(ctc_mpc_alloc_channel);
@@ -394,31 +408,22 @@ EXPORT_SYMBOL(ctc_mpc_alloc_channel);
void ctc_mpc_establish_connectivity(int port_num,
void (*callback)(int, int, int))
{
- char device[20];
struct net_device *dev;
struct mpc_group *grp;
struct ctcm_priv *priv;
struct channel *rch, *wch;
- ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
-
- sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
- dev = __dev_get_by_name(&init_net, device);
-
- if (dev == NULL) {
- printk(KERN_INFO "ctc_mpc_establish_connectivity "
- "%s dev=NULL\n", device);
+ dev = ctcmpc_get_dev(port_num);
+ if (dev == NULL)
return;
- }
- priv = dev->priv;
+ priv = dev->ml_priv;
+ grp = priv->mpcg;
rch = priv->channel[READ];
wch = priv->channel[WRITE];
- grp = priv->mpcg;
-
- ctcm_pr_debug("ctcmpc: %s() called for device %s state=%s\n",
- __FUNCTION__, dev->name,
- fsm_getstate_str(grp->fsm));
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
+ "%s(%s): state=%s",
+ CTCM_FUNTAIL, dev->name, fsm_getstate_str(grp->fsm));
grp->estconnfunc = callback;
grp->port_num = port_num;
@@ -446,8 +451,10 @@ void ctc_mpc_establish_connectivity(int port_num,
case MPCG_STATE_RESET:
/* MPC Group is not ready to start XID - min num of */
/* 1 read and 1 write channel have not been acquired*/
- printk(KERN_WARNING "ctcmpc: %s() REJECTED ACTIVE XID Req"
- "uest - Channel Pair is not Active\n", __FUNCTION__);
+
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): REJECTED - inactive channels",
+ CTCM_FUNTAIL, dev->name);
if (grp->estconnfunc) {
grp->estconnfunc(grp->port_num, -1, 0);
grp->estconnfunc = NULL;
@@ -457,11 +464,12 @@ void ctc_mpc_establish_connectivity(int port_num,
/* alloc channel was called but no XID exchange */
/* has occurred. initiate xside XID exchange */
/* make sure yside XID0 processing has not started */
+
if ((fsm_getstate(rch->fsm) > CH_XID0_PENDING) ||
(fsm_getstate(wch->fsm) > CH_XID0_PENDING)) {
- printk(KERN_WARNING "mpc: %s() ABORT ACTIVE XID"
- " Request- PASSIVE XID in process\n"
- , __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): ABORT - PASSIVE XID",
+ CTCM_FUNTAIL, dev->name);
break;
}
grp->send_qllc_disc = 1;
@@ -476,9 +484,9 @@ void ctc_mpc_establish_connectivity(int port_num,
(fsm_getstate(rch->fsm) == CH_XID0_PENDING))
fsm_event(grp->fsm, MPCG_EVENT_XID0DO, rch);
else {
- printk(KERN_WARNING "mpc: %s() Unable to start"
- " ACTIVE XID0 on read channel\n",
- __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): RX-%s not ready for ACTIVE XID0",
+ CTCM_FUNTAIL, dev->name, rch->id);
if (grp->estconnfunc) {
grp->estconnfunc(grp->port_num, -1, 0);
grp->estconnfunc = NULL;
@@ -490,9 +498,9 @@ void ctc_mpc_establish_connectivity(int port_num,
(fsm_getstate(wch->fsm) == CH_XID0_PENDING))
fsm_event(grp->fsm, MPCG_EVENT_XID0DO, wch);
else {
- printk(KERN_WARNING "mpc: %s() Unable to start"
- " ACTIVE XID0 on write channel\n",
- __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): WX-%s not ready for ACTIVE XID0",
+ CTCM_FUNTAIL, dev->name, wch->id);
if (grp->estconnfunc) {
grp->estconnfunc(grp->port_num, -1, 0);
grp->estconnfunc = NULL;
@@ -508,7 +516,7 @@ void ctc_mpc_establish_connectivity(int port_num,
}
done:
- ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
+ CTCM_PR_DEBUG("Exit %s()\n", __func__);
return;
}
EXPORT_SYMBOL(ctc_mpc_establish_connectivity);
@@ -520,40 +528,22 @@ EXPORT_SYMBOL(ctc_mpc_establish_connectivity);
void ctc_mpc_dealloc_ch(int port_num)
{
struct net_device *dev;
- char device[20];
struct ctcm_priv *priv;
struct mpc_group *grp;
- ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
- sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
- dev = __dev_get_by_name(&init_net, device);
-
- if (dev == NULL) {
- printk(KERN_INFO "%s() %s dev=NULL\n", __FUNCTION__, device);
- goto done;
- }
+ dev = ctcmpc_get_dev(port_num);
+ if (dev == NULL)
+ return;
+ priv = dev->ml_priv;
+ grp = priv->mpcg;
- ctcm_pr_debug("ctcmpc:%s %s() called for device %s refcount=%d\n",
- dev->name, __FUNCTION__,
- dev->name, atomic_read(&dev->refcnt));
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_DEBUG,
+ "%s: %s: refcount = %d\n",
+ CTCM_FUNTAIL, dev->name, atomic_read(&dev->refcnt));
- priv = dev->priv;
- if (priv == NULL) {
- printk(KERN_INFO "%s() %s priv=NULL\n",
- __FUNCTION__, device);
- goto done;
- }
fsm_deltimer(&priv->restart_timer);
-
- grp = priv->mpcg;
- if (grp == NULL) {
- printk(KERN_INFO "%s() %s dev=NULL\n", __FUNCTION__, device);
- goto done;
- }
grp->channels_terminating = 0;
-
fsm_deltimer(&grp->timer);
-
grp->allochanfunc = NULL;
grp->estconnfunc = NULL;
grp->port_persist = 0;
@@ -561,8 +551,6 @@ void ctc_mpc_dealloc_ch(int port_num)
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
ctcm_close(dev);
-done:
- ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
return;
}
EXPORT_SYMBOL(ctc_mpc_dealloc_ch);
@@ -573,32 +561,22 @@ EXPORT_SYMBOL(ctc_mpc_dealloc_ch);
*/
void ctc_mpc_flow_control(int port_num, int flowc)
{
- char device[20];
struct ctcm_priv *priv;
struct mpc_group *grp;
struct net_device *dev;
struct channel *rch;
int mpcg_state;
- ctcm_pr_debug("ctcmpc enter: %s() %i\n", __FUNCTION__, flowc);
-
- sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
- dev = __dev_get_by_name(&init_net, device);
-
- if (dev == NULL) {
- printk(KERN_INFO "ctc_mpc_flow_control %s dev=NULL\n", device);
+ dev = ctcmpc_get_dev(port_num);
+ if (dev == NULL)
return;
- }
+ priv = dev->ml_priv;
+ grp = priv->mpcg;
- ctcm_pr_debug("ctcmpc: %s %s called \n", dev->name, __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ "%s: %s: flowc = %d",
+ CTCM_FUNTAIL, dev->name, flowc);
- priv = dev->priv;
- if (priv == NULL) {
- printk(KERN_INFO "ctcmpc:%s() %s priv=NULL\n",
- __FUNCTION__, device);
- return;
- }
- grp = priv->mpcg;
rch = priv->channel[READ];
mpcg_state = fsm_getstate(grp->fsm);
@@ -629,7 +607,6 @@ void ctc_mpc_flow_control(int port_num, int flowc)
break;
}
- ctcm_pr_debug("ctcmpc exit: %s() %i\n", __FUNCTION__, flowc);
}
EXPORT_SYMBOL(ctc_mpc_flow_control);
@@ -642,16 +619,12 @@ static void mpc_rcvd_sweep_resp(struct mpcg_info *mpcginfo)
{
struct channel *rch = mpcginfo->ch;
struct net_device *dev = rch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct channel *ch = priv->channel[WRITE];
- if (do_debug)
- ctcm_pr_debug("ctcmpc enter: %s(): ch=0x%p id=%s\n",
- __FUNCTION__, ch, ch->id);
-
- if (do_debug_data)
- ctcmpc_dumpit((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
+ CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, ch, ch->id);
+ CTCM_D3_DUMP((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
grp->sweep_rsp_pend_num--;
@@ -677,21 +650,20 @@ static void mpc_rcvd_sweep_resp(struct mpcg_info *mpcginfo)
static void ctcmpc_send_sweep_resp(struct channel *rch)
{
struct net_device *dev = rch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
int rc = 0;
struct th_sweep *header;
struct sk_buff *sweep_skb;
struct channel *ch = priv->channel[WRITE];
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
- __FUNCTION__, rch, rch->id);
+ CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, rch, rch->id);
- sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
- GFP_ATOMIC|GFP_DMA);
+ sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC | GFP_DMA);
if (sweep_skb == NULL) {
- printk(KERN_INFO "Couldn't alloc sweep_skb\n");
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): sweep_skb allocation ERROR\n",
+ CTCM_FUNTAIL, rch->id);
rc = -ENOMEM;
goto done;
}
@@ -740,13 +712,13 @@ static void mpc_rcvd_sweep_req(struct mpcg_info *mpcginfo)
{
struct channel *rch = mpcginfo->ch;
struct net_device *dev = rch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct channel *ch = priv->channel[WRITE];
if (do_debug)
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
- " %s(): ch=0x%p id=%s\n", __FUNCTION__, ch, ch->id);
+ " %s(): ch=0x%p id=%s\n", __func__, ch, ch->id);
if (grp->in_sweep == 0) {
grp->in_sweep = 1;
@@ -755,8 +727,7 @@ static void mpc_rcvd_sweep_req(struct mpcg_info *mpcginfo)
grp->sweep_rsp_pend_num = grp->active_channels[READ];
}
- if (do_debug_data)
- ctcmpc_dumpit((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
+ CTCM_D3_DUMP((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
grp->sweep_req_pend_num--;
ctcmpc_send_sweep_resp(ch);
@@ -875,25 +846,13 @@ static int mpcg_fsm_len = ARRAY_SIZE(mpcg_fsm);
static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg)
{
struct net_device *dev = arg;
- struct ctcm_priv *priv = NULL;
- struct mpc_group *grp = NULL;
-
- if (dev == NULL) {
- printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
- return;
- }
-
- ctcm_pr_debug("ctcmpc enter: %s %s()\n", dev->name, __FUNCTION__);
-
- priv = dev->priv;
- if (priv == NULL) {
- printk(KERN_INFO "%s() priv=NULL\n", __FUNCTION__);
- return;
- }
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
- grp = priv->mpcg;
if (grp == NULL) {
- printk(KERN_INFO "%s() grp=NULL\n", __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): No MPC group",
+ CTCM_FUNTAIL, dev->name);
return;
}
@@ -907,7 +866,12 @@ static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg)
grp->estconnfunc = NULL;
} else if (grp->allochanfunc)
grp->send_qllc_disc = 1;
- goto done;
+
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): fails",
+ CTCM_FUNTAIL, dev->name);
+ return;
}
grp->port_persist = 1;
@@ -916,14 +880,7 @@ static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg)
tasklet_hi_schedule(&grp->mpc_tasklet2);
- ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__);
return;
-
-done:
- fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
-
-
- ctcm_pr_info("ctcmpc: %s()failure occurred\n", __FUNCTION__);
}
/*
@@ -933,42 +890,28 @@ done:
void mpc_group_ready(unsigned long adev)
{
struct net_device *dev = (struct net_device *)adev;
- struct ctcm_priv *priv = NULL;
- struct mpc_group *grp = NULL;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
struct channel *ch = NULL;
-
- ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
-
- if (dev == NULL) {
- printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
- return;
- }
-
- priv = dev->priv;
- if (priv == NULL) {
- printk(KERN_INFO "%s() priv=NULL\n", __FUNCTION__);
- return;
- }
-
- grp = priv->mpcg;
if (grp == NULL) {
- printk(KERN_INFO "ctcmpc:%s() grp=NULL\n", __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): No MPC group",
+ CTCM_FUNTAIL, dev->name);
return;
}
- printk(KERN_NOTICE "ctcmpc: %s GROUP TRANSITIONED TO READY"
- " maxbuf:%d\n",
- dev->name, grp->group_max_buflen);
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_NOTICE,
+ "%s: %s: GROUP TRANSITIONED TO READY, maxbuf = %d\n",
+ CTCM_FUNTAIL, dev->name, grp->group_max_buflen);
fsm_newstate(grp->fsm, MPCG_STATE_READY);
/* Put up a read on the channel */
ch = priv->channel[READ];
ch->pdu_seq = 0;
- if (do_debug_data)
- ctcm_pr_debug("ctcmpc: %s() ToDCM_pdu_seq= %08x\n" ,
- __FUNCTION__, ch->pdu_seq);
+ CTCM_PR_DBGDATA("ctcmpc: %s() ToDCM_pdu_seq= %08x\n" ,
+ __func__, ch->pdu_seq);
ctcmpc_chx_rxidle(ch->fsm, CTC_EVENT_START, ch);
/* Put the write channel in idle state */
@@ -980,22 +923,18 @@ void mpc_group_ready(unsigned long adev)
spin_unlock(&ch->collect_lock);
}
ctcm_chx_txidle(ch->fsm, CTC_EVENT_START, ch);
-
ctcm_clear_busy(dev);
if (grp->estconnfunc) {
grp->estconnfunc(grp->port_num, 0,
grp->group_max_buflen);
grp->estconnfunc = NULL;
- } else
- if (grp->allochanfunc)
- grp->allochanfunc(grp->port_num,
- grp->group_max_buflen);
+ } else if (grp->allochanfunc)
+ grp->allochanfunc(grp->port_num, grp->group_max_buflen);
grp->send_qllc_disc = 1;
grp->changed_side = 0;
- ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
return;
}
@@ -1004,51 +943,26 @@ void mpc_group_ready(unsigned long adev)
* Increment the MPC Group Active Channel Counts
* helper of dev_action (called from channel fsm)
*/
-int mpc_channel_action(struct channel *ch, int direction, int action)
+void mpc_channel_action(struct channel *ch, int direction, int action)
{
- struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv;
- struct mpc_group *grp = NULL;
- int rc = 0;
-
- if (do_debug)
- ctcm_pr_debug("ctcmpc enter: %s(): ch=0x%p id=%s\n",
- __FUNCTION__, ch, ch->id);
-
- if (dev == NULL) {
- printk(KERN_INFO "ctcmpc_channel_action %i dev=NULL\n",
- action);
- rc = 1;
- goto done;
- }
-
- priv = dev->priv;
- if (priv == NULL) {
- printk(KERN_INFO
- "ctcmpc_channel_action%i priv=NULL, dev=%s\n",
- action, dev->name);
- rc = 2;
- goto done;
- }
-
- grp = priv->mpcg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
if (grp == NULL) {
- printk(KERN_INFO "ctcmpc: %s()%i mpcgroup=NULL, dev=%s\n",
- __FUNCTION__, action, dev->name);
- rc = 3;
- goto done;
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): No MPC group",
+ CTCM_FUNTAIL, dev->name);
+ return;
}
- ctcm_pr_info(
- "ctcmpc: %s() %i(): Grp:%s total_channel_paths=%i "
- "active_channels read=%i, write=%i\n",
- __FUNCTION__,
- action,
- fsm_getstate_str(grp->fsm),
- grp->num_channel_paths,
- grp->active_channels[READ],
- grp->active_channels[WRITE]);
+ CTCM_PR_DEBUG("enter %s: ch=0x%p id=%s\n", __func__, ch, ch->id);
+
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
+ "%s: %i / Grp:%s total_channels=%i, active_channels: "
+ "read=%i, write=%i\n", __func__, action,
+ fsm_getstate_str(grp->fsm), grp->num_channel_paths,
+ grp->active_channels[READ], grp->active_channels[WRITE]);
if ((action == MPC_CHANNEL_ADD) && (ch->in_mpcgroup == 0)) {
grp->num_channel_paths++;
@@ -1062,10 +976,11 @@ int mpc_channel_action(struct channel *ch, int direction, int action)
ch->xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
GFP_ATOMIC | GFP_DMA);
if (ch->xid_skb == NULL) {
- printk(KERN_INFO "ctcmpc: %s()"
- "Couldn't alloc ch xid_skb\n", __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): Couldn't alloc ch xid_skb\n",
+ CTCM_FUNTAIL, dev->name);
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
- return 1;
+ return;
}
ch->xid_skb_data = ch->xid_skb->data;
ch->xid_th = (struct th_header *)ch->xid_skb->data;
@@ -1097,8 +1012,9 @@ int mpc_channel_action(struct channel *ch, int direction, int action)
(grp->active_channels[WRITE] > 0) &&
(fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) {
fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);
- printk(KERN_NOTICE "ctcmpc: %s MPC GROUP "
- "CHANNELS ACTIVE\n", dev->name);
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_NOTICE,
+ "%s: %s: MPC GROUP CHANNELS ACTIVE\n",
+ __func__, dev->name);
}
} else if ((action == MPC_CHANNEL_REMOVE) &&
(ch->in_mpcgroup == 1)) {
@@ -1119,25 +1035,14 @@ int mpc_channel_action(struct channel *ch, int direction, int action)
(grp->active_channels[READ] > 0)))
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
}
-
done:
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ "exit %s: %i / Grp:%s total_channels=%i, active_channels: "
+ "read=%i, write=%i\n", __func__, action,
+ fsm_getstate_str(grp->fsm), grp->num_channel_paths,
+ grp->active_channels[READ], grp->active_channels[WRITE]);
- if (do_debug) {
- ctcm_pr_debug(
- "ctcmpc: %s() %i Grp:%s ttl_chan_paths=%i "
- "active_chans read=%i, write=%i\n",
- __FUNCTION__,
- action,
- fsm_getstate_str(grp->fsm),
- grp->num_channel_paths,
- grp->active_channels[READ],
- grp->active_channels[WRITE]);
-
- ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
- __FUNCTION__, ch, ch->id);
- }
- return rc;
-
+ CTCM_PR_DEBUG("exit %s: ch=0x%p id=%s\n", __func__, ch, ch->id);
}
/**
@@ -1151,7 +1056,7 @@ done:
static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
{
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct pdu *curr_pdu;
struct mpcg_info *mpcginfo;
@@ -1163,9 +1068,8 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
int skblen;
int sendrc = 0;
- if (do_debug)
- ctcm_pr_debug("ctcmpc enter: %s() %s cp:%i ch:%s\n",
- __FUNCTION__, dev->name, smp_processor_id(), ch->id);
+ CTCM_PR_DEBUG("ctcmpc enter: %s() %s cp:%i ch:%s\n",
+ __func__, dev->name, smp_processor_id(), ch->id);
header = (struct th_header *)pskb->data;
if ((header->th_seg == 0) &&
@@ -1174,21 +1078,16 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
(header->th_seq_num == 0))
/* nothing for us */ goto done;
- if (do_debug_data) {
- ctcm_pr_debug("ctcmpc: %s() th_header\n", __FUNCTION__);
- ctcmpc_dumpit((char *)header, TH_HEADER_LENGTH);
- ctcm_pr_debug("ctcmpc: %s() pskb len: %04x \n",
- __FUNCTION__, pskb->len);
- }
+ CTCM_PR_DBGDATA("%s: th_header\n", __func__);
+ CTCM_D3_DUMP((char *)header, TH_HEADER_LENGTH);
+ CTCM_PR_DBGDATA("%s: pskb len: %04x \n", __func__, pskb->len);
pskb->dev = dev;
pskb->ip_summed = CHECKSUM_UNNECESSARY;
skb_pull(pskb, TH_HEADER_LENGTH);
if (likely(header->th_ch_flag == TH_HAS_PDU)) {
- if (do_debug_data)
- ctcm_pr_debug("ctcmpc: %s() came into th_has_pdu\n",
- __FUNCTION__);
+ CTCM_PR_DBGDATA("%s: came into th_has_pdu\n", __func__);
if ((fsm_getstate(grp->fsm) == MPCG_STATE_FLOWC) ||
((fsm_getstate(grp->fsm) == MPCG_STATE_READY) &&
(header->th_seq_num != ch->th_seq_num + 1) &&
@@ -1202,33 +1101,29 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
grp->out_of_sequence += 1;
__skb_push(pskb, TH_HEADER_LENGTH);
skb_queue_tail(&ch->io_queue, pskb);
- if (do_debug_data)
- ctcm_pr_debug("ctcmpc: %s() th_seq_num "
- "expect:%08x got:%08x\n", __FUNCTION__,
- ch->th_seq_num + 1, header->th_seq_num);
+ CTCM_PR_DBGDATA("%s: th_seq_num expect:%08x "
+ "got:%08x\n", __func__,
+ ch->th_seq_num + 1, header->th_seq_num);
return;
}
grp->out_of_sequence = 0;
ch->th_seq_num = header->th_seq_num;
- if (do_debug_data)
- ctcm_pr_debug("ctcmpc: %s() FromVTAM_th_seq=%08x\n",
- __FUNCTION__, ch->th_seq_num);
+ CTCM_PR_DBGDATA("ctcmpc: %s() FromVTAM_th_seq=%08x\n",
+ __func__, ch->th_seq_num);
if (unlikely(fsm_getstate(grp->fsm) != MPCG_STATE_READY))
goto done;
pdu_last_seen = 0;
while ((pskb->len > 0) && !pdu_last_seen) {
curr_pdu = (struct pdu *)pskb->data;
- if (do_debug_data) {
- ctcm_pr_debug("ctcm: %s() pdu_header\n",
- __FUNCTION__);
- ctcmpc_dumpit((char *)pskb->data,
- PDU_HEADER_LENGTH);
- ctcm_pr_debug("ctcm: %s() pskb len: %04x \n",
- __FUNCTION__, pskb->len);
- }
+
+ CTCM_PR_DBGDATA("%s: pdu_header\n", __func__);
+ CTCM_D3_DUMP((char *)pskb->data, PDU_HEADER_LENGTH);
+ CTCM_PR_DBGDATA("%s: pskb len: %04x \n",
+ __func__, pskb->len);
+
skb_pull(pskb, PDU_HEADER_LENGTH);
if (curr_pdu->pdu_flag & PDU_LAST)
@@ -1239,46 +1134,39 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
pskb->protocol = htons(ETH_P_SNA_DIX);
if ((pskb->len <= 0) || (pskb->len > ch->max_bufsize)) {
- printk(KERN_INFO
- "%s Illegal packet size %d "
- "received "
- "dropping\n", dev->name,
- pskb->len);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): Dropping packet with "
+ "illegal siize %d",
+ CTCM_FUNTAIL, dev->name, pskb->len);
+
priv->stats.rx_dropped++;
priv->stats.rx_length_errors++;
goto done;
}
skb_reset_mac_header(pskb);
new_len = curr_pdu->pdu_offset;
- if (do_debug_data)
- ctcm_pr_debug("ctcmpc: %s() new_len: %04x \n",
- __FUNCTION__, new_len);
+ CTCM_PR_DBGDATA("%s: new_len: %04x \n",
+ __func__, new_len);
if ((new_len == 0) || (new_len > pskb->len)) {
/* should never happen */
/* pskb len must be hosed...bail out */
- printk(KERN_INFO
- "ctcmpc: %s(): invalid pdu"
- " offset of %04x - data may be"
- "lost\n", __FUNCTION__, new_len);
- goto done;
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): non valid pdu_offset: %04x",
+ /* "data may be lost", */
+ CTCM_FUNTAIL, dev->name, new_len);
+ goto done;
}
skb = __dev_alloc_skb(new_len+4, GFP_ATOMIC);
if (!skb) {
- printk(KERN_INFO
- "ctcm: %s Out of memory in "
- "%s()- request-len:%04x \n",
- dev->name,
- __FUNCTION__,
- new_len+4);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): MEMORY allocation error",
+ CTCM_FUNTAIL, dev->name);
priv->stats.rx_dropped++;
- fsm_event(grp->fsm,
- MPCG_EVENT_INOP, dev);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
goto done;
}
-
- memcpy(skb_put(skb, new_len),
- pskb->data, new_len);
+ memcpy(skb_put(skb, new_len), pskb->data, new_len);
skb_reset_mac_header(skb);
skb->dev = pskb->dev;
@@ -1287,17 +1175,14 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
*((__u32 *) skb_push(skb, 4)) = ch->pdu_seq;
ch->pdu_seq++;
- if (do_debug_data)
- ctcm_pr_debug("%s: ToDCM_pdu_seq= %08x\n",
- __FUNCTION__, ch->pdu_seq);
-
- ctcm_pr_debug("ctcm: %s() skb:%0lx "
- "skb len: %d \n", __FUNCTION__,
- (unsigned long)skb, skb->len);
if (do_debug_data) {
- ctcm_pr_debug("ctcmpc: %s() up to 32 bytes"
- " of pdu_data sent\n",
- __FUNCTION__);
+ ctcm_pr_debug("%s: ToDCM_pdu_seq= %08x\n",
+ __func__, ch->pdu_seq);
+ ctcm_pr_debug("%s: skb:%0lx "
+ "skb len: %d \n", __func__,
+ (unsigned long)skb, skb->len);
+ ctcm_pr_debug("%s: up to 32 bytes "
+ "of pdu_data sent\n", __func__);
ctcmpc_dump32((char *)skb->data, skb->len);
}
@@ -1316,8 +1201,8 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
mpcginfo->ch = ch;
mpcginfo->th = header;
mpcginfo->skb = pskb;
- ctcm_pr_debug("ctcmpc: %s() Not PDU - may be control pkt\n",
- __FUNCTION__);
+ CTCM_PR_DEBUG("%s: Not PDU - may be control pkt\n",
+ __func__);
/* it's a sweep? */
sweep = (struct th_sweep *)pskb->data;
mpcginfo->sweep = sweep;
@@ -1333,8 +1218,9 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
} else if (header->th_blk_flag == TH_DISCONTACT)
fsm_event(grp->fsm, MPCG_EVENT_DISCONC, mpcginfo);
else if (header->th_seq_num != 0) {
- printk(KERN_INFO "%s unexpected packet"
- " expected control pkt\n", dev->name);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): control pkt expected\n",
+ CTCM_FUNTAIL, dev->name);
priv->stats.rx_dropped++;
/* mpcginfo only used for non-data transfers */
kfree(mpcginfo);
@@ -1347,13 +1233,12 @@ done:
dev_kfree_skb_any(pskb);
if (sendrc == NET_RX_DROP) {
printk(KERN_WARNING "%s %s() NETWORK BACKLOG EXCEEDED"
- " - PACKET DROPPED\n", dev->name, __FUNCTION__);
+ " - PACKET DROPPED\n", dev->name, __func__);
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
}
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit : %s %s(): ch=0x%p id=%s\n",
- dev->name, __FUNCTION__, ch, ch->id);
+ CTCM_PR_DEBUG("exit %s: %s: ch=0x%p id=%s\n",
+ __func__, dev->name, ch, ch->id);
}
/**
@@ -1366,15 +1251,14 @@ done:
*/
void ctcmpc_bh(unsigned long thischan)
{
- struct channel *ch = (struct channel *)thischan;
+ struct channel *ch = (struct channel *)thischan;
struct sk_buff *skb;
- struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
- struct mpc_group *grp = priv->mpcg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
- if (do_debug)
- ctcm_pr_debug("%s cp:%i enter: %s() %s\n",
- dev->name, smp_processor_id(), __FUNCTION__, ch->id);
+ CTCM_PR_DEBUG("%s cp:%i enter: %s() %s\n",
+ dev->name, smp_processor_id(), __func__, ch->id);
/* caller has requested driver to throttle back */
while ((fsm_getstate(grp->fsm) != MPCG_STATE_FLOWC) &&
(skb = skb_dequeue(&ch->io_queue))) {
@@ -1390,9 +1274,8 @@ void ctcmpc_bh(unsigned long thischan)
if (skb == skb_peek(&ch->io_queue))
break;
}
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit : %s %s(): ch=0x%p id=%s\n",
- dev->name, __FUNCTION__, ch, ch->id);
+ CTCM_PR_DEBUG("exit %s: %s: ch=0x%p id=%s\n",
+ __func__, dev->name, ch, ch->id);
return;
}
@@ -1403,16 +1286,16 @@ struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv)
{
struct mpc_group *grp;
- CTCM_DBF_TEXT(MPC_SETUP, 3, __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
+ "Enter %s(%p)", CTCM_FUNTAIL, priv);
grp = kzalloc(sizeof(struct mpc_group), GFP_KERNEL);
if (grp == NULL)
return NULL;
- grp->fsm =
- init_fsm("mpcg", mpcg_state_names, mpcg_event_names,
- MPCG_NR_STATES, MPCG_NR_EVENTS, mpcg_fsm,
- mpcg_fsm_len, GFP_KERNEL);
+ grp->fsm = init_fsm("mpcg", mpcg_state_names, mpcg_event_names,
+ MPCG_NR_STATES, MPCG_NR_EVENTS, mpcg_fsm,
+ mpcg_fsm_len, GFP_KERNEL);
if (grp->fsm == NULL) {
kfree(grp);
return NULL;
@@ -1424,7 +1307,6 @@ struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv)
grp->xid_skb =
__dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC | GFP_DMA);
if (grp->xid_skb == NULL) {
- printk(KERN_INFO "Couldn't alloc MPCgroup xid_skb\n");
kfree_fsm(grp->fsm);
kfree(grp);
return NULL;
@@ -1435,7 +1317,7 @@ struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv)
memcpy(skb_put(grp->xid_skb, TH_HEADER_LENGTH),
&thnorm, TH_HEADER_LENGTH);
- grp->xid = (struct xid2 *) skb_tail_pointer(grp->xid_skb);
+ grp->xid = (struct xid2 *)skb_tail_pointer(grp->xid_skb);
memcpy(skb_put(grp->xid_skb, XID2_LENGTH), &init_xid, XID2_LENGTH);
grp->xid->xid2_adj_id = jiffies | 0xfff00000;
grp->xid->xid2_sender_id = jiffies;
@@ -1446,7 +1328,6 @@ struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv)
grp->rcvd_xid_skb =
__dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA);
if (grp->rcvd_xid_skb == NULL) {
- printk(KERN_INFO "Couldn't alloc MPCgroup rcvd_xid_skb\n");
kfree_fsm(grp->fsm);
dev_kfree_skb(grp->xid_skb);
kfree(grp);
@@ -1492,32 +1373,27 @@ static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg)
int rc = 0;
struct channel *wch, *rch;
- if (dev == NULL) {
- printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
- return;
- }
-
- ctcm_pr_debug("ctcmpc enter: %s %s()\n", dev->name, __FUNCTION__);
+ BUG_ON(dev == NULL);
+ CTCM_PR_DEBUG("Enter %s: %s\n", __func__, dev->name);
- priv = dev->priv;
+ priv = dev->ml_priv;
grp = priv->mpcg;
grp->flow_off_called = 0;
-
fsm_deltimer(&grp->timer);
-
if (grp->channels_terminating)
- goto done;
+ return;
grp->channels_terminating = 1;
-
grp->saved_state = fsm_getstate(grp->fsm);
fsm_newstate(grp->fsm, MPCG_STATE_INOP);
if (grp->saved_state > MPCG_STATE_XID7INITF)
- printk(KERN_NOTICE "%s:MPC GROUP INOPERATIVE\n", dev->name);
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
+ "%s(%s): MPC GROUP INOPERATIVE",
+ CTCM_FUNTAIL, dev->name);
if ((grp->saved_state != MPCG_STATE_RESET) ||
/* dealloc_channel has been called */
- ((grp->saved_state == MPCG_STATE_RESET) &&
- (grp->port_persist == 0)))
+ ((grp->saved_state == MPCG_STATE_RESET) &&
+ (grp->port_persist == 0)))
fsm_deltimer(&priv->restart_timer);
wch = priv->channel[WRITE];
@@ -1567,29 +1443,24 @@ static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg)
/* This can result in INOP of VTAM PU due to halting of */
/* outstanding IO which causes a sense to be returned */
/* Only about 3 senses are allowed and then IOS/VTAM will*/
- /* ebcome unreachable without manual intervention */
- if ((grp->port_persist == 1) || (grp->alloc_called)) {
+ /* become unreachable without manual intervention */
+ if ((grp->port_persist == 1) || (grp->alloc_called)) {
grp->alloc_called = 0;
fsm_deltimer(&priv->restart_timer);
- fsm_addtimer(&priv->restart_timer,
- 500,
- DEV_EVENT_RESTART,
- dev);
+ fsm_addtimer(&priv->restart_timer, 500, DEV_EVENT_RESTART, dev);
fsm_newstate(grp->fsm, MPCG_STATE_RESET);
if (grp->saved_state > MPCG_STATE_XID7INITF)
- printk(KERN_NOTICE "%s:MPC GROUP RECOVERY SCHEDULED\n",
- dev->name);
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ALWAYS,
+ "%s(%s): MPC GROUP RECOVERY SCHEDULED",
+ CTCM_FUNTAIL, dev->name);
} else {
fsm_deltimer(&priv->restart_timer);
fsm_addtimer(&priv->restart_timer, 500, DEV_EVENT_STOP, dev);
fsm_newstate(grp->fsm, MPCG_STATE_RESET);
- printk(KERN_NOTICE "%s:MPC GROUP RECOVERY NOT ATTEMPTED\n",
- dev->name);
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ALWAYS,
+ "%s(%s): NO MPC GROUP RECOVERY ATTEMPTED",
+ CTCM_FUNTAIL, dev->name);
}
-
-done:
- ctcm_pr_debug("ctcmpc exit:%s %s()\n", dev->name, __FUNCTION__);
- return;
}
/**
@@ -1609,14 +1480,9 @@ static void mpc_action_timeout(fsm_instance *fi, int event, void *arg)
struct channel *wch;
struct channel *rch;
- CTCM_DBF_TEXT(MPC_TRACE, 6, __FUNCTION__);
+ BUG_ON(dev == NULL);
- if (dev == NULL) {
- CTCM_DBF_TEXT_(MPC_ERROR, 4, "%s: dev=NULL\n", __FUNCTION__);
- return;
- }
-
- priv = dev->priv;
+ priv = dev->ml_priv;
grp = priv->mpcg;
wch = priv->channel[WRITE];
rch = priv->channel[READ];
@@ -1633,8 +1499,9 @@ static void mpc_action_timeout(fsm_instance *fi, int event, void *arg)
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
}
- CTCM_DBF_TEXT_(MPC_TRACE, 6, "%s: dev=%s exit",
- __FUNCTION__, dev->name);
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ "%s: dev=%s exit",
+ CTCM_FUNTAIL, dev->name);
return;
}
@@ -1646,25 +1513,25 @@ void mpc_action_discontact(fsm_instance *fi, int event, void *arg)
{
struct mpcg_info *mpcginfo = arg;
struct channel *ch = mpcginfo->ch;
- struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
- struct mpc_group *grp = priv->mpcg;
+ struct net_device *dev;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
- if (ch == NULL) {
- printk(KERN_INFO "%s() ch=NULL\n", __FUNCTION__);
- return;
- }
- if (ch->netdev == NULL) {
- printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
- return;
+ if (ch) {
+ dev = ch->netdev;
+ if (dev) {
+ priv = dev->ml_priv;
+ if (priv) {
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
+ "%s: %s: %s\n",
+ CTCM_FUNTAIL, dev->name, ch->id);
+ grp = priv->mpcg;
+ grp->send_qllc_disc = 1;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+ }
}
- ctcm_pr_debug("ctcmpc enter: %s %s()\n", dev->name, __FUNCTION__);
-
- grp->send_qllc_disc = 1;
- fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
-
- ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__);
return;
}
@@ -1675,26 +1542,9 @@ void mpc_action_discontact(fsm_instance *fi, int event, void *arg)
*/
void mpc_action_send_discontact(unsigned long thischan)
{
- struct channel *ch;
- struct net_device *dev;
- struct ctcm_priv *priv;
- struct mpc_group *grp;
- int rc = 0;
- unsigned long saveflags;
-
- ch = (struct channel *)thischan;
- dev = ch->netdev;
- priv = dev->priv;
- grp = priv->mpcg;
-
- ctcm_pr_info("ctcmpc: %s cp:%i enter: %s() GrpState:%s ChState:%s\n",
- dev->name,
- smp_processor_id(),
- __FUNCTION__,
- fsm_getstate_str(grp->fsm),
- fsm_getstate_str(ch->fsm));
- saveflags = 0; /* avoids compiler warning with
- spin_unlock_irqrestore */
+ int rc;
+ struct channel *ch = (struct channel *)thischan;
+ unsigned long saveflags = 0;
spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
rc = ccw_device_start(ch->cdev, &ch->ccw[15],
@@ -1702,16 +1552,9 @@ void mpc_action_send_discontact(unsigned long thischan)
spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
if (rc != 0) {
- ctcm_pr_info("ctcmpc: %s() ch:%s IO failed \n",
- __FUNCTION__,
- ch->id);
- ctcm_ccw_check_rc(ch, rc, "send discontact");
- /* Not checking return code value here */
- /* Making best effort to notify partner*/
- /* that MPC Group is going down */
+ ctcm_ccw_check_rc(ch, rc, (char *)__func__);
}
- ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__);
return;
}
@@ -1723,49 +1566,50 @@ void mpc_action_send_discontact(unsigned long thischan)
*/
static int mpc_validate_xid(struct mpcg_info *mpcginfo)
{
- struct channel *ch = mpcginfo->ch;
- struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct channel *ch = mpcginfo->ch;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
- struct xid2 *xid = mpcginfo->xid;
- int failed = 0;
- int rc = 0;
- __u64 our_id, their_id = 0;
- int len;
+ struct xid2 *xid = mpcginfo->xid;
+ int rc = 0;
+ __u64 our_id = 0;
+ __u64 their_id = 0;
+ int len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
- len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
+ CTCM_PR_DEBUG("Enter %s: xid=%p\n", __func__, xid);
- ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
-
- if (mpcginfo->xid == NULL) {
- printk(KERN_INFO "%s() xid=NULL\n", __FUNCTION__);
+ if (xid == NULL) {
rc = 1;
- goto done;
+ /* XID REJECTED: xid == NULL */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): xid = NULL",
+ CTCM_FUNTAIL, ch->id);
+ goto done;
}
- ctcm_pr_debug("ctcmpc : %s xid received()\n", __FUNCTION__);
- ctcmpc_dumpit((char *)mpcginfo->xid, XID2_LENGTH);
+ CTCM_D3_DUMP((char *)xid, XID2_LENGTH);
/*the received direction should be the opposite of ours */
if (((CHANNEL_DIRECTION(ch->flags) == READ) ? XID2_WRITE_SIDE :
XID2_READ_SIDE) != xid->xid2_dlc_type) {
- failed = 1;
- printk(KERN_INFO "ctcmpc:%s() XID REJECTED - READ-WRITE CH "
- "Pairing Invalid \n", __FUNCTION__);
+ rc = 2;
+ /* XID REJECTED: r/w channel pairing mismatch */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): r/w channel pairing mismatch",
+ CTCM_FUNTAIL, ch->id);
+ goto done;
}
if (xid->xid2_dlc_type == XID2_READ_SIDE) {
- ctcm_pr_debug("ctcmpc: %s(): grpmaxbuf:%d xid2buflen:%d\n",
- __FUNCTION__, grp->group_max_buflen,
- xid->xid2_buf_len);
+ CTCM_PR_DEBUG("%s: grpmaxbuf:%d xid2buflen:%d\n", __func__,
+ grp->group_max_buflen, xid->xid2_buf_len);
- if (grp->group_max_buflen == 0 ||
- grp->group_max_buflen > xid->xid2_buf_len - len)
+ if (grp->group_max_buflen == 0 || grp->group_max_buflen >
+ xid->xid2_buf_len - len)
grp->group_max_buflen = xid->xid2_buf_len - len;
}
-
- if (grp->saved_xid2 == NULL) {
+ if (grp->saved_xid2 == NULL) {
grp->saved_xid2 =
(struct xid2 *)skb_tail_pointer(grp->rcvd_xid_skb);
@@ -1786,49 +1630,54 @@ static int mpc_validate_xid(struct mpcg_info *mpcginfo)
/* lower id assume the xside role */
if (our_id < their_id) {
grp->roll = XSIDE;
- ctcm_pr_debug("ctcmpc :%s() WE HAVE LOW ID-"
- "TAKE XSIDE\n", __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
+ "%s(%s): WE HAVE LOW ID - TAKE XSIDE",
+ CTCM_FUNTAIL, ch->id);
} else {
grp->roll = YSIDE;
- ctcm_pr_debug("ctcmpc :%s() WE HAVE HIGH ID-"
- "TAKE YSIDE\n", __FUNCTION__);
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
+ "%s(%s): WE HAVE HIGH ID - TAKE YSIDE",
+ CTCM_FUNTAIL, ch->id);
}
} else {
if (xid->xid2_flag4 != grp->saved_xid2->xid2_flag4) {
- failed = 1;
- printk(KERN_INFO "%s XID REJECTED - XID Flag Byte4\n",
- __FUNCTION__);
+ rc = 3;
+ /* XID REJECTED: xid flag byte4 mismatch */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): xid flag byte4 mismatch",
+ CTCM_FUNTAIL, ch->id);
}
if (xid->xid2_flag2 == 0x40) {
- failed = 1;
- printk(KERN_INFO "%s XID REJECTED - XID NOGOOD\n",
- __FUNCTION__);
+ rc = 4;
+ /* XID REJECTED - xid NOGOOD */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): xid NOGOOD",
+ CTCM_FUNTAIL, ch->id);
}
if (xid->xid2_adj_id != grp->saved_xid2->xid2_adj_id) {
- failed = 1;
- printk(KERN_INFO "%s XID REJECTED - "
- "Adjacent Station ID Mismatch\n",
- __FUNCTION__);
+ rc = 5;
+ /* XID REJECTED - Adjacent Station ID Mismatch */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): Adjacent Station ID Mismatch",
+ CTCM_FUNTAIL, ch->id);
}
if (xid->xid2_sender_id != grp->saved_xid2->xid2_sender_id) {
- failed = 1;
- printk(KERN_INFO "%s XID REJECTED - "
- "Sender Address Mismatch\n", __FUNCTION__);
-
+ rc = 6;
+ /* XID REJECTED - Sender Address Mismatch */
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): Sender Address Mismatch",
+ CTCM_FUNTAIL, ch->id);
}
}
- if (failed) {
+done:
+ if (rc) {
ctcm_pr_info("ctcmpc : %s() failed\n", __FUNCTION__);
priv->xid->xid2_flag2 = 0x40;
grp->saved_xid2->xid2_flag2 = 0x40;
- rc = 1;
}
-done:
-
- ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
return rc;
}
@@ -1839,46 +1688,20 @@ done:
static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side)
{
struct channel *ch = arg;
- struct ctcm_priv *priv;
- struct mpc_group *grp = NULL;
- struct net_device *dev = NULL;
int rc = 0;
int gotlock = 0;
unsigned long saveflags = 0; /* avoids compiler warning with
- spin_unlock_irqrestore */
+ spin_unlock_irqrestore */
- if (ch == NULL) {
- printk(KERN_INFO "%s ch=NULL\n", __FUNCTION__);
- goto done;
- }
-
- if (do_debug)
- ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
- __FUNCTION__, smp_processor_id(), ch, ch->id);
-
- dev = ch->netdev;
- if (dev == NULL) {
- printk(KERN_INFO "%s dev=NULL\n", __FUNCTION__);
- goto done;
- }
-
- priv = dev->priv;
- if (priv == NULL) {
- printk(KERN_INFO "%s priv=NULL\n", __FUNCTION__);
- goto done;
- }
-
- grp = priv->mpcg;
- if (grp == NULL) {
- printk(KERN_INFO "%s grp=NULL\n", __FUNCTION__);
- goto done;
- }
+ CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ch, ch->id);
if (ctcm_checkalloc_buffer(ch))
goto done;
- /* skb data-buffer referencing: */
-
+ /*
+ * skb data-buffer referencing:
+ */
ch->trans_skb->data = ch->trans_skb_data;
skb_reset_tail_pointer(ch->trans_skb);
ch->trans_skb->len = 0;
@@ -1911,22 +1734,22 @@ static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side)
ch->ccw[8].count = 0;
ch->ccw[8].cda = 0x00;
+ if (!(ch->xid_th && ch->xid && ch->xid_id))
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_INFO,
+ "%s(%s): xid_th=%p, xid=%p, xid_id=%p",
+ CTCM_FUNTAIL, ch->id, ch->xid_th, ch->xid, ch->xid_id);
+
if (side == XSIDE) {
/* mpc_action_xside_xid */
- if (ch->xid_th == NULL) {
- printk(KERN_INFO "%s ch->xid_th=NULL\n", __FUNCTION__);
- goto done;
- }
+ if (ch->xid_th == NULL)
+ goto done;
ch->ccw[9].cmd_code = CCW_CMD_WRITE;
ch->ccw[9].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
ch->ccw[9].count = TH_HEADER_LENGTH;
ch->ccw[9].cda = virt_to_phys(ch->xid_th);
- if (ch->xid == NULL) {
- printk(KERN_INFO "%s ch->xid=NULL\n", __FUNCTION__);
- goto done;
- }
-
+ if (ch->xid == NULL)
+ goto done;
ch->ccw[10].cmd_code = CCW_CMD_WRITE;
ch->ccw[10].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
ch->ccw[10].count = XID2_LENGTH;
@@ -1956,28 +1779,22 @@ static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side)
ch->ccw[10].count = XID2_LENGTH;
ch->ccw[10].cda = virt_to_phys(ch->rcvd_xid);
- if (ch->xid_th == NULL) {
- printk(KERN_INFO "%s ch->xid_th=NULL\n", __FUNCTION__);
- goto done;
- }
+ if (ch->xid_th == NULL)
+ goto done;
ch->ccw[11].cmd_code = CCW_CMD_WRITE;
ch->ccw[11].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
ch->ccw[11].count = TH_HEADER_LENGTH;
ch->ccw[11].cda = virt_to_phys(ch->xid_th);
- if (ch->xid == NULL) {
- printk(KERN_INFO "%s ch->xid=NULL\n", __FUNCTION__);
- goto done;
- }
+ if (ch->xid == NULL)
+ goto done;
ch->ccw[12].cmd_code = CCW_CMD_WRITE;
ch->ccw[12].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
ch->ccw[12].count = XID2_LENGTH;
ch->ccw[12].cda = virt_to_phys(ch->xid);
- if (ch->xid_id == NULL) {
- printk(KERN_INFO "%s ch->xid_id=NULL\n", __FUNCTION__);
- goto done;
- }
+ if (ch->xid_id == NULL)
+ goto done;
ch->ccw[13].cmd_code = CCW_CMD_WRITE;
ch->ccw[13].cda = virt_to_phys(ch->xid_id);
@@ -1990,12 +1807,11 @@ static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side)
ch->ccw[14].count = 0;
ch->ccw[14].cda = 0;
- if (do_debug_ccw)
- ctcmpc_dumpit((char *)&ch->ccw[8], sizeof(struct ccw1) * 7);
+ CTCM_CCW_DUMP((char *)&ch->ccw[8], sizeof(struct ccw1) * 7);
+ CTCM_D3_DUMP((char *)ch->xid_th, TH_HEADER_LENGTH);
+ CTCM_D3_DUMP((char *)ch->xid, XID2_LENGTH);
+ CTCM_D3_DUMP((char *)ch->xid_id, 4);
- ctcmpc_dumpit((char *)ch->xid_th, TH_HEADER_LENGTH);
- ctcmpc_dumpit((char *)ch->xid, XID2_LENGTH);
- ctcmpc_dumpit((char *)ch->xid_id, 4);
if (!in_irq()) {
/* Such conditional locking is a known problem for
* sparse because its static undeterministic.
@@ -2012,16 +1828,13 @@ static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side)
spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
if (rc != 0) {
- ctcm_pr_info("ctcmpc: %s() ch:%s IO failed \n",
- __FUNCTION__, ch->id);
ctcm_ccw_check_rc(ch, rc,
(side == XSIDE) ? "x-side XID" : "y-side XID");
}
done:
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
- __FUNCTION__, ch, ch->id);
+ CTCM_PR_DEBUG("Exit %s: ch=0x%p id=%s\n",
+ __func__, ch, ch->id);
return;
}
@@ -2050,41 +1863,19 @@ static void mpc_action_yside_xid(fsm_instance *fsm, int event, void *arg)
*/
static void mpc_action_doxid0(fsm_instance *fsm, int event, void *arg)
{
- struct channel *ch = arg;
- struct ctcm_priv *priv;
- struct mpc_group *grp = NULL;
- struct net_device *dev = NULL;
-
- if (do_debug)
- ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
- __FUNCTION__, smp_processor_id(), ch, ch->id);
-
- if (ch == NULL) {
- printk(KERN_WARNING "%s ch=NULL\n", __FUNCTION__);
- goto done;
- }
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
- dev = ch->netdev;
- if (dev == NULL) {
- printk(KERN_WARNING "%s dev=NULL\n", __FUNCTION__);
- goto done;
- }
-
- priv = dev->priv;
- if (priv == NULL) {
- printk(KERN_WARNING "%s priv=NULL\n", __FUNCTION__);
- goto done;
- }
-
- grp = priv->mpcg;
- if (grp == NULL) {
- printk(KERN_WARNING "%s grp=NULL\n", __FUNCTION__);
- goto done;
- }
+ CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ch, ch->id);
if (ch->xid == NULL) {
- printk(KERN_WARNING "%s ch-xid=NULL\n", __FUNCTION__);
- goto done;
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): ch->xid == NULL",
+ CTCM_FUNTAIL, dev->name);
+ return;
}
fsm_newstate(ch->fsm, CH_XID0_INPROGRESS);
@@ -2104,12 +1895,7 @@ static void mpc_action_doxid0(fsm_instance *fsm, int event, void *arg)
fsm_event(grp->fsm, MPCG_EVENT_DOIO, ch);
-done:
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
- __FUNCTION__, ch, ch->id);
return;
-
}
/*
@@ -2119,32 +1905,16 @@ done:
static void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg)
{
struct net_device *dev = arg;
- struct ctcm_priv *priv = NULL;
- struct mpc_group *grp = NULL;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = NULL;
int direction;
- int rc = 0;
int send = 0;
- ctcm_pr_debug("ctcmpc enter: %s() \n", __FUNCTION__);
-
- if (dev == NULL) {
- printk(KERN_INFO "%s dev=NULL \n", __FUNCTION__);
- rc = 1;
- goto done;
- }
-
- priv = dev->priv;
- if (priv == NULL) {
- printk(KERN_INFO "%s priv=NULL \n", __FUNCTION__);
- rc = 1;
- goto done;
- }
-
- grp = priv->mpcg;
+ if (priv)
+ grp = priv->mpcg;
if (grp == NULL) {
- printk(KERN_INFO "%s grp=NULL \n", __FUNCTION__);
- rc = 1;
- goto done;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ return;
}
for (direction = READ; direction <= WRITE; direction++) {
@@ -2199,11 +1969,6 @@ static void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg)
fsm_event(grp->fsm, MPCG_EVENT_DOIO, ch);
}
-done:
-
- if (rc != 0)
- fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
-
return;
}
@@ -2214,24 +1979,15 @@ done:
static void mpc_action_rcvd_xid0(fsm_instance *fsm, int event, void *arg)
{
- struct mpcg_info *mpcginfo = arg;
- struct channel *ch = mpcginfo->ch;
- struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv;
- struct mpc_group *grp;
-
- if (do_debug)
- ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
- __FUNCTION__, smp_processor_id(), ch, ch->id);
-
- priv = dev->priv;
- grp = priv->mpcg;
+ struct mpcg_info *mpcginfo = arg;
+ struct channel *ch = mpcginfo->ch;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
- ctcm_pr_debug("ctcmpc in:%s() %s xid2:%i xid7:%i xidt_p2:%i \n",
- __FUNCTION__, ch->id,
- grp->outstanding_xid2,
- grp->outstanding_xid7,
- grp->outstanding_xid7_p2);
+ CTCM_PR_DEBUG("%s: ch-id:%s xid2:%i xid7:%i xidt_p2:%i \n",
+ __func__, ch->id, grp->outstanding_xid2,
+ grp->outstanding_xid7, grp->outstanding_xid7_p2);
if (fsm_getstate(ch->fsm) < CH_XID7_PENDING)
fsm_newstate(ch->fsm, CH_XID7_PENDING);
@@ -2268,17 +2024,12 @@ static void mpc_action_rcvd_xid0(fsm_instance *fsm, int event, void *arg)
}
kfree(mpcginfo);
- if (do_debug) {
- ctcm_pr_debug("ctcmpc:%s() %s xid2:%i xid7:%i xidt_p2:%i \n",
- __FUNCTION__, ch->id,
- grp->outstanding_xid2,
- grp->outstanding_xid7,
- grp->outstanding_xid7_p2);
- ctcm_pr_debug("ctcmpc:%s() %s grpstate: %s chanstate: %s \n",
- __FUNCTION__, ch->id,
- fsm_getstate_str(grp->fsm),
- fsm_getstate_str(ch->fsm));
- }
+ CTCM_PR_DEBUG("ctcmpc:%s() %s xid2:%i xid7:%i xidt_p2:%i \n",
+ __func__, ch->id, grp->outstanding_xid2,
+ grp->outstanding_xid7, grp->outstanding_xid7_p2);
+ CTCM_PR_DEBUG("ctcmpc:%s() %s grpstate: %s chanstate: %s \n",
+ __func__, ch->id,
+ fsm_getstate_str(grp->fsm), fsm_getstate_str(ch->fsm));
return;
}
@@ -2293,18 +2044,13 @@ static void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg)
struct mpcg_info *mpcginfo = arg;
struct channel *ch = mpcginfo->ch;
struct net_device *dev = ch->netdev;
- struct ctcm_priv *priv = dev->priv;
+ struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
- if (do_debug) {
- ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
- __FUNCTION__, smp_processor_id(), ch, ch->id);
-
- ctcm_pr_debug("ctcmpc: outstanding_xid7: %i, "
- " outstanding_xid7_p2: %i\n",
- grp->outstanding_xid7,
- grp->outstanding_xid7_p2);
- }
+ CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
+ __func__, smp_processor_id(), ch, ch->id);
+ CTCM_PR_DEBUG("%s: outstanding_xid7: %i, outstanding_xid7_p2: %i\n",
+ __func__, grp->outstanding_xid7, grp->outstanding_xid7_p2);
grp->outstanding_xid7--;
ch->xid_skb->data = ch->xid_skb_data;
@@ -2337,14 +2083,8 @@ static void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg)
mpc_validate_xid(mpcginfo);
break;
}
-
kfree(mpcginfo);
-
- if (do_debug)
- ctcm_pr_debug("ctcmpc exit: %s(): cp=%i ch=0x%p id=%s\n",
- __FUNCTION__, smp_processor_id(), ch, ch->id);
return;
-
}
/*
@@ -2353,36 +2093,14 @@ static void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg)
*/
static int mpc_send_qllc_discontact(struct net_device *dev)
{
- int rc = 0;
__u32 new_len = 0;
struct sk_buff *skb;
struct qllc *qllcptr;
- struct ctcm_priv *priv;
- struct mpc_group *grp;
-
- ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
-
- if (dev == NULL) {
- printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
- rc = 1;
- goto done;
- }
-
- priv = dev->priv;
- if (priv == NULL) {
- printk(KERN_INFO "%s() priv=NULL\n", __FUNCTION__);
- rc = 1;
- goto done;
- }
+ struct ctcm_priv *priv = dev->ml_priv;
+ struct mpc_group *grp = priv->mpcg;
- grp = priv->mpcg;
- if (grp == NULL) {
- printk(KERN_INFO "%s() grp=NULL\n", __FUNCTION__);
- rc = 1;
- goto done;
- }
- ctcm_pr_info("ctcmpc: %s() GROUP STATE: %s\n", __FUNCTION__,
- mpcg_state_names[grp->saved_state]);
+ CTCM_PR_DEBUG("%s: GROUP STATE: %s\n",
+ __func__, mpcg_state_names[grp->saved_state]);
switch (grp->saved_state) {
/*
@@ -2408,11 +2126,10 @@ static int mpc_send_qllc_discontact(struct net_device *dev)
new_len = sizeof(struct qllc);
qllcptr = kzalloc(new_len, gfp_type() | GFP_DMA);
if (qllcptr == NULL) {
- printk(KERN_INFO
- "ctcmpc: Out of memory in %s()\n",
- dev->name);
- rc = 1;
- goto done;
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): qllcptr allocation error",
+ CTCM_FUNTAIL, dev->name);
+ return -ENOMEM;
}
qllcptr->qllc_address = 0xcc;
@@ -2421,31 +2138,29 @@ static int mpc_send_qllc_discontact(struct net_device *dev)
skb = __dev_alloc_skb(new_len, GFP_ATOMIC);
if (skb == NULL) {
- printk(KERN_INFO "%s Out of memory in mpc_send_qllc\n",
- dev->name);
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): skb allocation error",
+ CTCM_FUNTAIL, dev->name);
priv->stats.rx_dropped++;
- rc = 1;
kfree(qllcptr);
- goto done;
+ return -ENOMEM;
}
memcpy(skb_put(skb, new_len), qllcptr, new_len);
kfree(qllcptr);
if (skb_headroom(skb) < 4) {
- printk(KERN_INFO "ctcmpc: %s() Unable to"
- " build discontact for %s\n",
- __FUNCTION__, dev->name);
- rc = 1;
+ CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
+ "%s(%s): skb_headroom error",
+ CTCM_FUNTAIL, dev->name);
dev_kfree_skb_any(skb);
- goto done;
+ return -ENOMEM;
}
*((__u32 *)skb_push(skb, 4)) = priv->channel[READ]->pdu_seq;
priv->channel[READ]->pdu_seq++;
- if (do_debug_data)
- ctcm_pr_debug("ctcmpc: %s ToDCM_pdu_seq= %08x\n",
- __FUNCTION__, priv->channel[READ]->pdu_seq);
+ CTCM_PR_DBGDATA("ctcmpc: %s ToDCM_pdu_seq= %08x\n",
+ __func__, priv->channel[READ]->pdu_seq);
/* receipt of CC03 resets anticipated sequence number on
receiving side */
@@ -2455,7 +2170,7 @@ static int mpc_send_qllc_discontact(struct net_device *dev)
skb->protocol = htons(ETH_P_SNAP);
skb->ip_summed = CHECKSUM_UNNECESSARY;
- ctcmpc_dumpit((char *)skb->data, (sizeof(struct qllc) + 4));
+ CTCM_D3_DUMP(skb->data, (sizeof(struct qllc) + 4));
netif_rx(skb);
break;
@@ -2464,9 +2179,7 @@ static int mpc_send_qllc_discontact(struct net_device *dev)
}
-done:
- ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
- return rc;
+ return 0;
}
/* --- This is the END my friend --- */
diff --git a/drivers/s390/net/ctcm_mpc.h b/drivers/s390/net/ctcm_mpc.h
index f99686069a9..5336120cddf 100644
--- a/drivers/s390/net/ctcm_mpc.h
+++ b/drivers/s390/net/ctcm_mpc.h
@@ -231,7 +231,7 @@ static inline void ctcmpc_dump32(char *buf, int len)
int ctcmpc_open(struct net_device *);
void ctcm_ccw_check_rc(struct channel *, int, char *);
void mpc_group_ready(unsigned long adev);
-int mpc_channel_action(struct channel *ch, int direction, int action);
+void mpc_channel_action(struct channel *ch, int direction, int action);
void mpc_action_send_discontact(unsigned long thischan);
void mpc_action_discontact(fsm_instance *fi, int event, void *arg);
void ctcmpc_bh(unsigned long thischan);
diff --git a/drivers/s390/net/cu3088.c b/drivers/s390/net/cu3088.c
index 8e7697305a4..f4a32375c03 100644
--- a/drivers/s390/net/cu3088.c
+++ b/drivers/s390/net/cu3088.c
@@ -36,7 +36,6 @@ const char *cu3088_type[] = {
"CTC/A",
"ESCON channel",
"FICON channel",
- "P390 LCS card",
"OSA LCS card",
"CLAW channel device",
"unknown channel type",
@@ -49,7 +48,6 @@ static struct ccw_device_id cu3088_ids[] = {
{ CCW_DEVICE(0x3088, 0x08), .driver_info = channel_type_parallel },
{ CCW_DEVICE(0x3088, 0x1f), .driver_info = channel_type_escon },
{ CCW_DEVICE(0x3088, 0x1e), .driver_info = channel_type_ficon },
- { CCW_DEVICE(0x3088, 0x01), .driver_info = channel_type_p390 },
{ CCW_DEVICE(0x3088, 0x60), .driver_info = channel_type_osa2 },
{ CCW_DEVICE(0x3088, 0x61), .driver_info = channel_type_claw },
{ /* end of list */ }
diff --git a/drivers/s390/net/cu3088.h b/drivers/s390/net/cu3088.h
index 1753661f702..d8558a7105a 100644
--- a/drivers/s390/net/cu3088.h
+++ b/drivers/s390/net/cu3088.h
@@ -17,9 +17,6 @@ enum channel_types {
/* Device is a FICON channel */
channel_type_ficon,
- /* Device is a P390 LCS card */
- channel_type_p390,
-
/* Device is a OSA2 card */
channel_type_osa2,
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index dd22f4b3703..0825be87e5a 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -492,7 +492,7 @@ lcs_start_channel(struct lcs_channel *channel)
unsigned long flags;
int rc;
- LCS_DBF_TEXT_(4,trace,"ssch%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace,"ssch%s", dev_name(&channel->ccwdev->dev));
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
rc = ccw_device_start(channel->ccwdev,
channel->ccws + channel->io_idx, 0, 0,
@@ -501,7 +501,8 @@ lcs_start_channel(struct lcs_channel *channel)
channel->state = LCS_CH_STATE_RUNNING;
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
- LCS_DBF_TEXT_(4,trace,"essh%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4,trace,"essh%s",
+ dev_name(&channel->ccwdev->dev));
PRINT_ERR("Error in starting channel, rc=%d!\n", rc);
}
return rc;
@@ -514,12 +515,13 @@ lcs_clear_channel(struct lcs_channel *channel)
int rc;
LCS_DBF_TEXT(4,trace,"clearch");
- LCS_DBF_TEXT_(4,trace,"%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace, "%s", dev_name(&channel->ccwdev->dev));
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
rc = ccw_device_clear(channel->ccwdev, (addr_t) channel);
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
- LCS_DBF_TEXT_(4,trace,"ecsc%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace, "ecsc%s",
+ dev_name(&channel->ccwdev->dev));
return rc;
}
wait_event(channel->wait_q, (channel->state == LCS_CH_STATE_CLEARED));
@@ -540,13 +542,14 @@ lcs_stop_channel(struct lcs_channel *channel)
if (channel->state == LCS_CH_STATE_STOPPED)
return 0;
LCS_DBF_TEXT(4,trace,"haltsch");
- LCS_DBF_TEXT_(4,trace,"%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace, "%s", dev_name(&channel->ccwdev->dev));
channel->state = LCS_CH_STATE_INIT;
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
rc = ccw_device_halt(channel->ccwdev, (addr_t) channel);
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
- LCS_DBF_TEXT_(4,trace,"ehsc%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace, "ehsc%s",
+ dev_name(&channel->ccwdev->dev));
return rc;
}
/* Asynchronous halt initialted. Wait for its completion. */
@@ -632,10 +635,11 @@ __lcs_resume_channel(struct lcs_channel *channel)
return 0;
if (channel->ccws[channel->io_idx].flags & CCW_FLAG_SUSPEND)
return 0;
- LCS_DBF_TEXT_(5, trace, "rsch%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(5, trace, "rsch%s", dev_name(&channel->ccwdev->dev));
rc = ccw_device_resume(channel->ccwdev);
if (rc) {
- LCS_DBF_TEXT_(4, trace, "ersc%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace, "ersc%s",
+ dev_name(&channel->ccwdev->dev));
PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc);
} else
channel->state = LCS_CH_STATE_RUNNING;
@@ -1302,18 +1306,18 @@ lcs_check_irb_error(struct ccw_device *cdev, struct irb *irb)
switch (PTR_ERR(irb)) {
case -EIO:
- PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id);
+ PRINT_WARN("i/o-error on device %s\n", dev_name(&cdev->dev));
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT_(2, trace, " rc%d", -EIO);
break;
case -ETIMEDOUT:
- PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id);
+ PRINT_WARN("timeout on device %s\n", dev_name(&cdev->dev));
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT_(2, trace, " rc%d", -ETIMEDOUT);
break;
default:
PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT(2, trace, " rc???");
}
@@ -1327,8 +1331,8 @@ lcs_get_problem(struct ccw_device *cdev, struct irb *irb)
char *sense;
sense = (char *) irb->ecw;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
@@ -1388,17 +1392,19 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
else
channel = &card->write;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
- LCS_DBF_TEXT_(5, trace, "Rint%s",cdev->dev.bus_id);
- LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.cstat, irb->scsw.dstat);
- LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.fctl, irb->scsw.actl);
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
+ LCS_DBF_TEXT_(5, trace, "Rint%s", dev_name(&cdev->dev));
+ LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
+ LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.fctl,
+ irb->scsw.cmd.actl);
/* Check for channel and device errors presented */
rc = lcs_get_problem(cdev, irb);
if (rc || (dstat & DEV_STAT_UNIT_EXCEP)) {
PRINT_WARN("check on device %s, dstat=0x%X, cstat=0x%X \n",
- cdev->dev.bus_id, dstat, cstat);
+ dev_name(&cdev->dev), dstat, cstat);
if (rc) {
channel->state = LCS_CH_STATE_ERROR;
}
@@ -1410,11 +1416,12 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
}
/* How far in the ccw chain have we processed? */
if ((channel->state != LCS_CH_STATE_INIT) &&
- (irb->scsw.fctl & SCSW_FCTL_START_FUNC)) {
- index = (struct ccw1 *) __va((addr_t) irb->scsw.cpa)
+ (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) &&
+ (irb->scsw.cmd.cpa != 0)) {
+ index = (struct ccw1 *) __va((addr_t) irb->scsw.cmd.cpa)
- channel->ccws;
- if ((irb->scsw.actl & SCSW_ACTL_SUSPENDED) ||
- (irb->scsw.cstat & SCHN_STAT_PCI))
+ if ((irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED) ||
+ (irb->scsw.cmd.cstat & SCHN_STAT_PCI))
/* Bloody io subsystem tells us lies about cpa... */
index = (index - 1) & (LCS_NUM_BUFFS - 1);
while (channel->io_idx != index) {
@@ -1425,25 +1432,24 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
}
}
- if ((irb->scsw.dstat & DEV_STAT_DEV_END) ||
- (irb->scsw.dstat & DEV_STAT_CHN_END) ||
- (irb->scsw.dstat & DEV_STAT_UNIT_CHECK))
+ if ((irb->scsw.cmd.dstat & DEV_STAT_DEV_END) ||
+ (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) ||
+ (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK))
/* Mark channel as stopped. */
channel->state = LCS_CH_STATE_STOPPED;
- else if (irb->scsw.actl & SCSW_ACTL_SUSPENDED)
+ else if (irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED)
/* CCW execution stopped on a suspend bit. */
channel->state = LCS_CH_STATE_SUSPENDED;
- if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) {
- if (irb->scsw.cc != 0) {
+ if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) {
+ if (irb->scsw.cmd.cc != 0) {
ccw_device_halt(channel->ccwdev, (addr_t) channel);
return;
}
/* The channel has been stopped by halt_IO. */
channel->state = LCS_CH_STATE_HALTED;
}
- if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
+ if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC)
channel->state = LCS_CH_STATE_CLEARED;
- }
/* Do the rest in the tasklet. */
tasklet_schedule(&channel->irq_tasklet);
}
@@ -1461,7 +1467,7 @@ lcs_tasklet(unsigned long data)
int rc;
channel = (struct lcs_channel *) data;
- LCS_DBF_TEXT_(5, trace, "tlet%s",channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(5, trace, "tlet%s", dev_name(&channel->ccwdev->dev));
/* Check for processed buffers. */
iob = channel->iob;
@@ -1761,7 +1767,7 @@ lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd)
netif_carrier_off(card->dev);
break;
default:
- PRINT_INFO("UNRECOGNIZED LGW COMMAND\n");
+ LCS_DBF_TEXT(5, trace, "noLGWcmd");
break;
}
} else
@@ -2042,13 +2048,12 @@ lcs_probe_device(struct ccwgroup_device *ccwgdev)
LCS_DBF_TEXT(2, setup, "add_dev");
card = lcs_alloc_card();
if (!card) {
- PRINT_ERR("Allocation of lcs card failed\n");
+ LCS_DBF_TEXT_(2, setup, " rc%d", -ENOMEM);
put_device(&ccwgdev->dev);
return -ENOMEM;
}
ret = sysfs_create_group(&ccwgdev->dev.kobj, &lcs_attr_group);
if (ret) {
- PRINT_ERR("Creating attributes failed");
lcs_free_card(card);
put_device(&ccwgdev->dev);
return ret;
@@ -2140,7 +2145,6 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
default:
LCS_DBF_TEXT(3, setup, "errinit");
PRINT_ERR("LCS: Initialization failed\n");
- PRINT_ERR("LCS: No device found!\n");
goto out;
}
if (!dev)
@@ -2244,7 +2248,7 @@ lcs_recovery(void *ptr)
return 0;
LCS_DBF_TEXT(4, trace, "recover2");
gdev = card->gdev;
- PRINT_WARN("Recovery of device %s started...\n", gdev->dev.bus_id);
+ PRINT_WARN("Recovery of device %s started...\n", dev_name(&gdev->dev));
rc = __lcs_shutdown_device(gdev, 1);
rc = lcs_new_device(gdev);
if (!rc)
@@ -2269,7 +2273,6 @@ lcs_remove_device(struct ccwgroup_device *ccwgdev)
if (!card)
return;
- PRINT_INFO("Removing lcs group device ....\n");
LCS_DBF_TEXT(3, setup, "remdev");
LCS_DBF_HEX(3, setup, &card, sizeof(void*));
if (ccwgdev->state == CCWGROUP_ONLINE) {
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index e4ba6a0372a..0fea51e34b5 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -625,9 +625,6 @@ static void netiucv_unpack_skb(struct iucv_connection *conn,
offset += header->next;
header->next -= NETIUCV_HDRLEN;
if (skb_tailroom(pskb) < header->next) {
- PRINT_WARN("%s: Illegal next field in iucv header: "
- "%d > %d\n",
- dev->name, header->next, skb_tailroom(pskb));
IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
header->next, skb_tailroom(pskb));
return;
@@ -636,8 +633,6 @@ static void netiucv_unpack_skb(struct iucv_connection *conn,
skb_reset_mac_header(pskb);
skb = dev_alloc_skb(pskb->len);
if (!skb) {
- PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n",
- dev->name);
IUCV_DBF_TEXT(data, 2,
"Out of memory in netiucv_unpack_skb\n");
privptr->stats.rx_dropped++;
@@ -674,7 +669,6 @@ static void conn_action_rx(fsm_instance *fi, int event, void *arg)
if (!conn->netdev) {
iucv_message_reject(conn->path, msg);
- PRINT_WARN("Received data for unlinked connection\n");
IUCV_DBF_TEXT(data, 2,
"Received data for unlinked connection\n");
return;
@@ -682,8 +676,6 @@ static void conn_action_rx(fsm_instance *fi, int event, void *arg)
if (msg->length > conn->max_buffsize) {
iucv_message_reject(conn->path, msg);
privptr->stats.rx_dropped++;
- PRINT_WARN("msglen %d > max_buffsize %d\n",
- msg->length, conn->max_buffsize);
IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
msg->length, conn->max_buffsize);
return;
@@ -695,7 +687,6 @@ static void conn_action_rx(fsm_instance *fi, int event, void *arg)
msg->length, NULL);
if (rc || msg->length < 5) {
privptr->stats.rx_errors++;
- PRINT_WARN("iucv_receive returned %08x\n", rc);
IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
return;
}
@@ -778,7 +769,6 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
fsm_newstate(fi, CONN_STATE_IDLE);
if (privptr)
privptr->stats.tx_errors += txpackets;
- PRINT_WARN("iucv_send returned %08x\n", rc);
IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
} else {
if (privptr) {
@@ -806,8 +796,6 @@ static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
path->flags = 0;
rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);
if (rc) {
- PRINT_WARN("%s: IUCV accept failed with error %d\n",
- netdev->name, rc);
IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
return;
}
@@ -873,7 +861,7 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)
IUCV_DBF_TEXT(trace, 3, __func__);
fsm_newstate(fi, CONN_STATE_STARTWAIT);
- PRINT_DEBUG("%s('%s'): connecting ...\n",
+ IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n",
conn->netdev->name, conn->userid);
/*
@@ -968,8 +956,8 @@ static void conn_action_inval(fsm_instance *fi, int event, void *arg)
struct iucv_connection *conn = arg;
struct net_device *netdev = conn->netdev;
- PRINT_WARN("%s: Cannot connect without username\n", netdev->name);
- IUCV_DBF_TEXT(data, 2, "conn_action_inval called\n");
+ IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
+ netdev->name, conn->userid);
}
static const fsm_node conn_fsm[] = {
@@ -1077,9 +1065,6 @@ dev_action_connup(fsm_instance *fi, int event, void *arg)
"connection is up and running\n");
break;
case DEV_STATE_STOPWAIT:
- PRINT_INFO(
- "%s: got connection UP event during shutdown!\n",
- dev->name);
IUCV_DBF_TEXT(data, 2,
"dev_action_connup: in DEV_STATE_STOPWAIT\n");
break;
@@ -1174,8 +1159,6 @@ static int netiucv_transmit_skb(struct iucv_connection *conn,
nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
if (!nskb) {
- PRINT_WARN("%s: Could not allocate tx_skb\n",
- conn->netdev->name);
IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
rc = -ENOMEM;
return rc;
@@ -1223,7 +1206,6 @@ static int netiucv_transmit_skb(struct iucv_connection *conn,
skb_pull(skb, NETIUCV_HDRLEN);
skb_trim(skb, skb->len - NETIUCV_HDRLEN);
}
- PRINT_WARN("iucv_send returned %08x\n", rc);
IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
} else {
if (copied)
@@ -1293,14 +1275,11 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
* Some sanity checks ...
*/
if (skb == NULL) {
- PRINT_WARN("%s: NULL sk_buff passed\n", dev->name);
IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
privptr->stats.tx_dropped++;
return 0;
}
if (skb_headroom(skb) < NETIUCV_HDRLEN) {
- PRINT_WARN("%s: Got sk_buff with head room < %ld bytes\n",
- dev->name, NETIUCV_HDRLEN);
IUCV_DBF_TEXT(data, 2,
"netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
dev_kfree_skb(skb);
@@ -1393,7 +1372,6 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr,
IUCV_DBF_TEXT(trace, 3, __func__);
if (count > 9) {
- PRINT_WARN("netiucv: username too long (%d)!\n", (int) count);
IUCV_DBF_TEXT_(setup, 2,
"%d is length of username\n", (int) count);
return -EINVAL;
@@ -1409,7 +1387,6 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr,
/* trailing lf, grr */
break;
}
- PRINT_WARN("netiucv: Invalid char %c in username!\n", *p);
IUCV_DBF_TEXT_(setup, 2,
"username: invalid character %c\n", *p);
return -EINVAL;
@@ -1421,18 +1398,15 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr,
if (memcmp(username, priv->conn->userid, 9) &&
(ndev->flags & (IFF_UP | IFF_RUNNING))) {
/* username changed while the interface is active. */
- PRINT_WARN("netiucv: device %s active, connected to %s\n",
- dev->bus_id, priv->conn->userid);
- PRINT_WARN("netiucv: user cannot be updated\n");
IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
- return -EBUSY;
+ return -EPERM;
}
read_lock_bh(&iucv_connection_rwlock);
list_for_each_entry(cp, &iucv_connection_list, list) {
if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {
read_unlock_bh(&iucv_connection_rwlock);
- PRINT_WARN("netiucv: Connection to %s already "
- "exists\n", username);
+ IUCV_DBF_TEXT_(setup, 2, "user_write: Connection "
+ "to %s already exists\n", username);
return -EEXIST;
}
}
@@ -1466,13 +1440,10 @@ static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
bs1 = simple_strtoul(buf, &e, 0);
if (e && (!isspace(*e))) {
- PRINT_WARN("netiucv: Invalid character in buffer!\n");
IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);
return -EINVAL;
}
if (bs1 > NETIUCV_BUFSIZE_MAX) {
- PRINT_WARN("netiucv: Given buffer size %d too large.\n",
- bs1);
IUCV_DBF_TEXT_(setup, 2,
"buffer_write: buffer size %d too large\n",
bs1);
@@ -1480,16 +1451,12 @@ static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
}
if ((ndev->flags & IFF_RUNNING) &&
(bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
- PRINT_WARN("netiucv: Given buffer size %d too small.\n",
- bs1);
IUCV_DBF_TEXT_(setup, 2,
"buffer_write: buffer size %d too small\n",
bs1);
return -EINVAL;
}
if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
- PRINT_WARN("netiucv: Given buffer size %d too small.\n",
- bs1);
IUCV_DBF_TEXT_(setup, 2,
"buffer_write: buffer size %d too small\n",
bs1);
@@ -1757,7 +1724,7 @@ static int netiucv_register_device(struct net_device *ndev)
IUCV_DBF_TEXT(trace, 3, __func__);
if (dev) {
- snprintf(dev->bus_id, BUS_ID_SIZE, "net%s", ndev->name);
+ dev_set_name(dev, "net%s", ndev->name);
dev->bus = &iucv_bus;
dev->parent = iucv_root;
/*
@@ -1963,7 +1930,6 @@ static ssize_t conn_write(struct device_driver *drv,
IUCV_DBF_TEXT(trace, 3, __func__);
if (count>9) {
- PRINT_WARN("netiucv: username too long (%d)!\n", (int)count);
IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
return -EINVAL;
}
@@ -1976,7 +1942,6 @@ static ssize_t conn_write(struct device_driver *drv,
if (*p == '\n')
/* trailing lf, grr */
break;
- PRINT_WARN("netiucv: Invalid character in username!\n");
IUCV_DBF_TEXT_(setup, 2,
"conn_write: invalid character %c\n", *p);
return -EINVAL;
@@ -1989,8 +1954,8 @@ static ssize_t conn_write(struct device_driver *drv,
list_for_each_entry(cp, &iucv_connection_list, list) {
if (!strncmp(username, cp->userid, 9)) {
read_unlock_bh(&iucv_connection_rwlock);
- PRINT_WARN("netiucv: Connection to %s already "
- "exists\n", username);
+ IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection "
+ "to %s already exists\n", username);
return -EEXIST;
}
}
@@ -1998,9 +1963,6 @@ static ssize_t conn_write(struct device_driver *drv,
dev = netiucv_init_netdevice(username);
if (!dev) {
- PRINT_WARN("netiucv: Could not allocate network device "
- "structure for user '%s'\n",
- netiucv_printname(username));
IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
return -ENODEV;
}
@@ -2020,15 +1982,12 @@ static ssize_t conn_write(struct device_driver *drv,
if (rc)
goto out_unreg;
- PRINT_INFO("%s: '%s'\n", dev->name, netiucv_printname(username));
return count;
out_unreg:
netiucv_unregister_device(priv->dev);
out_free_ndev:
- PRINT_WARN("netiucv: Could not register '%s'\n", dev->name);
- IUCV_DBF_TEXT(setup, 2, "conn_write: could not register\n");
netiucv_free_netdevice(dev);
return rc;
}
@@ -2073,14 +2032,13 @@ static ssize_t remove_write (struct device_driver *drv,
PRINT_WARN("netiucv: %s cannot be removed\n",
ndev->name);
IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
- return -EBUSY;
+ return -EPERM;
}
unregister_netdev(ndev);
netiucv_unregister_device(dev);
return count;
}
read_unlock_bh(&iucv_connection_rwlock);
- PRINT_WARN("netiucv: net device %s unknown\n", name);
IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
return -EINVAL;
}
@@ -2148,7 +2106,6 @@ static int __init netiucv_init(void)
netiucv_driver.groups = netiucv_drv_attr_groups;
rc = driver_register(&netiucv_driver);
if (rc) {
- PRINT_ERR("NETIUCV: failed to register driver.\n");
IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
goto out_iucv;
}
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 699ac11debd..af6d6045851 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -90,11 +90,11 @@ struct qeth_dbf_info {
#define CARD_RDEV(card) card->read.ccwdev
#define CARD_WDEV(card) card->write.ccwdev
#define CARD_DDEV(card) card->data.ccwdev
-#define CARD_BUS_ID(card) card->gdev->dev.bus_id
-#define CARD_RDEV_ID(card) card->read.ccwdev->dev.bus_id
-#define CARD_WDEV_ID(card) card->write.ccwdev->dev.bus_id
-#define CARD_DDEV_ID(card) card->data.ccwdev->dev.bus_id
-#define CHANNEL_ID(channel) channel->ccwdev->dev.bus_id
+#define CARD_BUS_ID(card) dev_name(&card->gdev->dev)
+#define CARD_RDEV_ID(card) dev_name(&card->read.ccwdev->dev)
+#define CARD_WDEV_ID(card) dev_name(&card->write.ccwdev->dev)
+#define CARD_DDEV_ID(card) dev_name(&card->data.ccwdev->dev)
+#define CHANNEL_ID(channel) dev_name(&channel->ccwdev->dev)
/**
* card stuff
@@ -239,11 +239,6 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,
/*not used unless the microcode gets patched*/
#define QETH_PCI_TIMER_VALUE(card) 3
-#define QETH_MIN_INPUT_THRESHOLD 1
-#define QETH_MAX_INPUT_THRESHOLD 500
-#define QETH_MIN_OUTPUT_THRESHOLD 1
-#define QETH_MAX_OUTPUT_THRESHOLD 300
-
/* priority queing */
#define QETH_PRIOQ_DEFAULT QETH_NO_PRIO_QUEUEING
#define QETH_DEFAULT_QUEUE 2
@@ -424,6 +419,7 @@ struct qeth_qdio_out_buffer {
int next_element_to_fill;
struct sk_buff_head skb_list;
struct list_head ctx_list;
+ int is_header[16];
};
struct qeth_card;
@@ -693,6 +689,7 @@ struct qeth_mc_mac {
struct list_head list;
__u8 mc_addr[MAX_ADDR_LEN];
unsigned char mc_addrlen;
+ int is_vmac;
};
struct qeth_card {
@@ -790,7 +787,7 @@ void qeth_core_remove_osn_attributes(struct device *);
/* exports for qeth discipline device drivers */
extern struct qeth_card_list_struct qeth_core_card_list;
-
+extern struct kmem_cache *qeth_core_header_cache;
extern struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS];
void qeth_set_allowed_threads(struct qeth_card *, unsigned long , int);
@@ -811,17 +808,14 @@ int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *,
struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *,
enum qeth_ipa_cmds, enum qeth_prot_versions);
int qeth_query_setadapterparms(struct qeth_card *);
-int qeth_check_qdio_errors(struct qdio_buffer *, unsigned int,
- unsigned int, const char *);
+int qeth_check_qdio_errors(struct qdio_buffer *, unsigned int, const char *);
void qeth_queue_input_buffer(struct qeth_card *, int);
struct sk_buff *qeth_core_get_next_skb(struct qeth_card *,
struct qdio_buffer *, struct qdio_buffer_element **, int *,
struct qeth_hdr **);
void qeth_schedule_recovery(struct qeth_card *);
void qeth_qdio_output_handler(struct ccw_device *, unsigned int,
- unsigned int, unsigned int,
- unsigned int, int, int,
- unsigned long);
+ int, int, int, unsigned long);
void qeth_clear_ipacmd_list(struct qeth_card *);
int qeth_qdio_clear_card(struct qeth_card *, int);
void qeth_clear_working_pool_list(struct qeth_card *);
@@ -851,7 +845,7 @@ int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int);
int qeth_get_elements_no(struct qeth_card *, void *, struct sk_buff *, int);
int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *,
struct sk_buff *, struct qeth_hdr *, int,
- struct qeth_eddp_context *);
+ struct qeth_eddp_context *, int, int);
int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *,
struct sk_buff *, struct qeth_hdr *,
int, struct qeth_eddp_context *);
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 9a71dae223e..7de410d5be4 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -19,8 +19,8 @@
#include <linux/mii.h>
#include <linux/kthread.h>
-#include <asm-s390/ebcdic.h>
-#include <asm-s390/io.h>
+#include <asm/ebcdic.h>
+#include <asm/io.h>
#include <asm/s390_rdev.h>
#include "qeth_core.h"
@@ -48,6 +48,8 @@ EXPORT_SYMBOL_GPL(qeth_dbf);
struct qeth_card_list_struct qeth_core_card_list;
EXPORT_SYMBOL_GPL(qeth_core_card_list);
+struct kmem_cache *qeth_core_header_cache;
+EXPORT_SYMBOL_GPL(qeth_core_header_cache);
static struct device *qeth_core_root_dev;
static unsigned int known_devices[][10] = QETH_MODELLIST_ARRAY;
@@ -420,7 +422,7 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
QETH_DBF_TEXT(TRACE, 3, "urla");
break;
default:
- PRINT_WARN("Received data is IPA "
+ QETH_DBF_MESSAGE(2, "Received data is IPA "
"but not a reply!\n");
break;
}
@@ -735,15 +737,15 @@ static int qeth_get_problem(struct ccw_device *cdev, struct irb *irb)
char *sense;
sense = (char *) irb->ecw;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) {
QETH_DBF_TEXT(TRACE, 2, "CGENCHK");
PRINT_WARN("check on device %s, dstat=x%x, cstat=x%x ",
- cdev->dev.bus_id, dstat, cstat);
+ dev_name(&cdev->dev), dstat, cstat);
print_hex_dump(KERN_WARNING, "qeth: irb ", DUMP_PREFIX_OFFSET,
16, 1, irb, 64, 1);
return 1;
@@ -758,7 +760,7 @@ static int qeth_get_problem(struct ccw_device *cdev, struct irb *irb)
if (sense[SENSE_COMMAND_REJECT_BYTE] &
SENSE_COMMAND_REJECT_FLAG) {
QETH_DBF_TEXT(TRACE, 2, "CMDREJi");
- return 0;
+ return 1;
}
if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) {
QETH_DBF_TEXT(TRACE, 2, "AFFE");
@@ -782,12 +784,12 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
switch (PTR_ERR(irb)) {
case -EIO:
- PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id);
+ PRINT_WARN("i/o-error on device %s\n", dev_name(&cdev->dev));
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT_(TRACE, 2, " rc%d", -EIO);
break;
case -ETIMEDOUT:
- PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id);
+ PRINT_WARN("timeout on device %s\n", dev_name(&cdev->dev));
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT_(TRACE, 2, " rc%d", -ETIMEDOUT);
if (intparm == QETH_RCD_PARM) {
@@ -801,7 +803,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
break;
default:
PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT(TRACE, 2, " rc???");
}
@@ -823,8 +825,8 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
if (__qeth_check_irb_error(cdev, intparm, irb))
return;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
card = CARD_FROM_CDEV(cdev);
if (!card)
@@ -842,10 +844,10 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
}
atomic_set(&channel->irq_pending, 0);
- if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC))
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC))
channel->state = CH_STATE_STOPPED;
- if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC))
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC))
channel->state = CH_STATE_HALTED;
/*let's wake up immediately on data channel*/
@@ -882,6 +884,7 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
}
rc = qeth_get_problem(cdev, irb);
if (rc) {
+ qeth_clear_ipacmd_list(card);
qeth_schedule_recovery(card);
goto out;
}
@@ -933,6 +936,10 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
}
qeth_eddp_buf_release_contexts(buf);
for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) {
+ if (buf->buffer->element[i].addr && buf->is_header[i])
+ kmem_cache_free(qeth_core_header_cache,
+ buf->buffer->element[i].addr);
+ buf->is_header[i] = 0;
buf->buffer->element[i].length = 0;
buf->buffer->element[i].addr = NULL;
buf->buffer->element[i].flags = 0;
@@ -2073,7 +2080,7 @@ static void qeth_create_qib_param_field_blkt(struct qeth_card *card,
static int qeth_qdio_activate(struct qeth_card *card)
{
QETH_DBF_TEXT(SETUP, 3, "qdioact");
- return qdio_activate(CARD_DDEV(card), 0);
+ return qdio_activate(CARD_DDEV(card));
}
static int qeth_dm_act(struct qeth_card *card)
@@ -2349,16 +2356,11 @@ int qeth_init_qdio_queues(struct qeth_card *card)
card->qdio.in_q->next_buf_to_init =
card->qdio.in_buf_pool.buf_count - 1;
rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0, 0,
- card->qdio.in_buf_pool.buf_count - 1, NULL);
+ card->qdio.in_buf_pool.buf_count - 1);
if (rc) {
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
return rc;
}
- rc = qdio_synchronize(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0);
- if (rc) {
- QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
- return rc;
- }
/* outbound queue */
for (i = 0; i < card->qdio.no_out_queues; ++i) {
memset(card->qdio.out_qs[i]->qdio_bufs, 0,
@@ -2559,9 +2561,9 @@ int qeth_query_setadapterparms(struct qeth_card *card)
EXPORT_SYMBOL_GPL(qeth_query_setadapterparms);
int qeth_check_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,
- unsigned int siga_error, const char *dbftext)
+ const char *dbftext)
{
- if (qdio_error || siga_error) {
+ if (qdio_error) {
QETH_DBF_TEXT(TRACE, 2, dbftext);
QETH_DBF_TEXT(QERR, 2, dbftext);
QETH_DBF_TEXT_(QERR, 2, " F15=%02X",
@@ -2569,7 +2571,6 @@ int qeth_check_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,
QETH_DBF_TEXT_(QERR, 2, " F14=%02X",
buf->element[14].flags & 0xff);
QETH_DBF_TEXT_(QERR, 2, " qerr=%X", qdio_error);
- QETH_DBF_TEXT_(QERR, 2, " serr=%X", siga_error);
return 1;
}
return 0;
@@ -2622,9 +2623,8 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index)
card->perf_stats.inbound_do_qdio_start_time =
qeth_get_micros();
}
- rc = do_QDIO(CARD_DDEV(card),
- QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
- 0, queue->next_buf_to_init, count, NULL);
+ rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0,
+ queue->next_buf_to_init, count);
if (card->options.performance_stats)
card->perf_stats.inbound_do_qdio_time +=
qeth_get_micros() -
@@ -2643,14 +2643,13 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index)
EXPORT_SYMBOL_GPL(qeth_queue_input_buffer);
static int qeth_handle_send_error(struct qeth_card *card,
- struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err,
- unsigned int siga_err)
+ struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err)
{
int sbalf15 = buffer->buffer->element[15].flags & 0xff;
- int cc = siga_err & 3;
+ int cc = qdio_err & 3;
QETH_DBF_TEXT(TRACE, 6, "hdsnderr");
- qeth_check_qdio_errors(buffer->buffer, qdio_err, siga_err, "qouterr");
+ qeth_check_qdio_errors(buffer->buffer, qdio_err, "qouterr");
switch (cc) {
case 0:
if (qdio_err) {
@@ -2662,7 +2661,7 @@ static int qeth_handle_send_error(struct qeth_card *card,
}
return QETH_SEND_ERROR_NONE;
case 2:
- if (siga_err & QDIO_SIGA_ERROR_B_BIT_SET) {
+ if (qdio_err & QDIO_ERROR_SIGA_BUSY) {
QETH_DBF_TEXT(TRACE, 1, "SIGAcc2B");
QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
return QETH_SEND_ERROR_KICK_IT;
@@ -2758,8 +2757,8 @@ static int qeth_flush_buffers_on_no_pci(struct qeth_qdio_out_q *queue)
return 0;
}
-static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
- int index, int count)
+static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
+ int count)
{
struct qeth_qdio_out_buffer *buf;
int rc;
@@ -2807,12 +2806,10 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
qeth_get_micros();
}
qdio_flags = QDIO_FLAG_SYNC_OUTPUT;
- if (under_int)
- qdio_flags |= QDIO_FLAG_UNDER_INTERRUPT;
if (atomic_read(&queue->set_pci_flags_count))
qdio_flags |= QDIO_FLAG_PCI_OUT;
rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags,
- queue->queue_no, index, count, NULL);
+ queue->queue_no, index, count);
if (queue->card->options.performance_stats)
queue->card->perf_stats.outbound_do_qdio_time +=
qeth_get_micros() -
@@ -2866,16 +2863,15 @@ static void qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
queue->card->perf_stats.bufs_sent_pack +=
flush_cnt;
if (flush_cnt)
- qeth_flush_buffers(queue, 1, index, flush_cnt);
+ qeth_flush_buffers(queue, index, flush_cnt);
atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
}
}
}
-void qeth_qdio_output_handler(struct ccw_device *ccwdev, unsigned int status,
- unsigned int qdio_error, unsigned int siga_error,
- unsigned int __queue, int first_element, int count,
- unsigned long card_ptr)
+void qeth_qdio_output_handler(struct ccw_device *ccwdev,
+ unsigned int qdio_error, int __queue, int first_element,
+ int count, unsigned long card_ptr)
{
struct qeth_card *card = (struct qeth_card *) card_ptr;
struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue];
@@ -2883,15 +2879,12 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, unsigned int status,
int i;
QETH_DBF_TEXT(TRACE, 6, "qdouhdl");
- if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
- if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
- QETH_DBF_TEXT(TRACE, 2, "achkcond");
- QETH_DBF_TEXT_(TRACE, 2, "%s", CARD_BUS_ID(card));
- QETH_DBF_TEXT_(TRACE, 2, "%08x", status);
- netif_stop_queue(card->dev);
- qeth_schedule_recovery(card);
- return;
- }
+ if (qdio_error & QDIO_ERROR_ACTIVATE_CHECK_CONDITION) {
+ QETH_DBF_TEXT(TRACE, 2, "achkcond");
+ QETH_DBF_TEXT_(TRACE, 2, "%s", CARD_BUS_ID(card));
+ netif_stop_queue(card->dev);
+ qeth_schedule_recovery(card);
+ return;
}
if (card->options.performance_stats) {
card->perf_stats.outbound_handler_cnt++;
@@ -2901,8 +2894,7 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, unsigned int status,
for (i = first_element; i < (first_element + count); ++i) {
buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
/*we only handle the KICK_IT error by doing a recovery */
- if (qeth_handle_send_error(card, buffer,
- qdio_error, siga_error)
+ if (qeth_handle_send_error(card, buffer, qdio_error)
== QETH_SEND_ERROR_KICK_IT){
netif_stop_queue(card->dev);
qeth_schedule_recovery(card);
@@ -3017,8 +3009,8 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr,
if (skb_shinfo(skb)->nr_frags > 0)
elements_needed = (skb_shinfo(skb)->nr_frags + 1);
if (elements_needed == 0)
- elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE)
- + skb->len) >> PAGE_SHIFT);
+ elements_needed = 1 + (((((unsigned long) skb->data) %
+ PAGE_SIZE) + skb->len) >> PAGE_SHIFT);
if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
QETH_DBF_MESSAGE(2, "Invalid size of IP packet "
"(Number=%d / Length=%d). Discarded.\n",
@@ -3030,9 +3022,10 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr,
EXPORT_SYMBOL_GPL(qeth_get_elements_no);
static inline void __qeth_fill_buffer(struct sk_buff *skb,
- struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill)
+ struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill,
+ int offset)
{
- int length = skb->len;
+ int length = skb->len - offset;
int length_here;
int element;
char *data;
@@ -3042,6 +3035,11 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb,
data = skb->data;
first_lap = (is_tso == 0 ? 1 : 0);
+ if (offset >= 0) {
+ data = skb->data + offset;
+ first_lap = 0;
+ }
+
while (length > 0) {
/* length_here is the remaining amount of data in this page */
length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE);
@@ -3073,22 +3071,22 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb,
}
static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
- struct qeth_qdio_out_buffer *buf, struct sk_buff *skb)
+ struct qeth_qdio_out_buffer *buf, struct sk_buff *skb,
+ struct qeth_hdr *hdr, int offset, int hd_len)
{
struct qdio_buffer *buffer;
- struct qeth_hdr_tso *hdr;
int flush_cnt = 0, hdr_len, large_send = 0;
buffer = buf->buffer;
atomic_inc(&skb->users);
skb_queue_tail(&buf->skb_list, skb);
- hdr = (struct qeth_hdr_tso *) skb->data;
/*check first on TSO ....*/
- if (hdr->hdr.hdr.l3.id == QETH_HEADER_TYPE_TSO) {
+ if (hdr->hdr.l3.id == QETH_HEADER_TYPE_TSO) {
int element = buf->next_element_to_fill;
- hdr_len = sizeof(struct qeth_hdr_tso) + hdr->ext.dg_hdr_len;
+ hdr_len = sizeof(struct qeth_hdr_tso) +
+ ((struct qeth_hdr_tso *)hdr)->ext.dg_hdr_len;
/*fill first buffer entry only with header information */
buffer->element[element].addr = skb->data;
buffer->element[element].length = hdr_len;
@@ -3098,9 +3096,20 @@ static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
skb->len -= hdr_len;
large_send = 1;
}
+
+ if (offset >= 0) {
+ int element = buf->next_element_to_fill;
+ buffer->element[element].addr = hdr;
+ buffer->element[element].length = sizeof(struct qeth_hdr) +
+ hd_len;
+ buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG;
+ buf->is_header[element] = 1;
+ buf->next_element_to_fill++;
+ }
+
if (skb_shinfo(skb)->nr_frags == 0)
__qeth_fill_buffer(skb, buffer, large_send,
- (int *)&buf->next_element_to_fill);
+ (int *)&buf->next_element_to_fill, offset);
else
__qeth_fill_buffer_frag(skb, buffer, large_send,
(int *)&buf->next_element_to_fill);
@@ -3130,7 +3139,7 @@ static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
int qeth_do_send_packet_fast(struct qeth_card *card,
struct qeth_qdio_out_q *queue, struct sk_buff *skb,
struct qeth_hdr *hdr, int elements_needed,
- struct qeth_eddp_context *ctx)
+ struct qeth_eddp_context *ctx, int offset, int hd_len)
{
struct qeth_qdio_out_buffer *buffer;
int buffers_needed = 0;
@@ -3163,12 +3172,12 @@ int qeth_do_send_packet_fast(struct qeth_card *card,
}
atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
if (ctx == NULL) {
- qeth_fill_buffer(queue, buffer, skb);
- qeth_flush_buffers(queue, 0, index, 1);
+ qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len);
+ qeth_flush_buffers(queue, index, 1);
} else {
flush_cnt = qeth_eddp_fill_buffer(queue, ctx, index);
WARN_ON(buffers_needed != flush_cnt);
- qeth_flush_buffers(queue, 0, index, flush_cnt);
+ qeth_flush_buffers(queue, index, flush_cnt);
}
return 0;
out:
@@ -3221,8 +3230,8 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
* again */
if (atomic_read(&buffer->state) !=
QETH_QDIO_BUF_EMPTY){
- qeth_flush_buffers(queue, 0,
- start_index, flush_count);
+ qeth_flush_buffers(queue, start_index,
+ flush_count);
atomic_set(&queue->state,
QETH_OUT_Q_UNLOCKED);
return -EBUSY;
@@ -3239,7 +3248,7 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
}
}
if (ctx == NULL)
- tmp = qeth_fill_buffer(queue, buffer, skb);
+ tmp = qeth_fill_buffer(queue, buffer, skb, hdr, -1, 0);
else {
tmp = qeth_eddp_fill_buffer(queue, ctx,
queue->next_buf_to_fill);
@@ -3253,7 +3262,7 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
flush_count += tmp;
out:
if (flush_count)
- qeth_flush_buffers(queue, 0, start_index, flush_count);
+ qeth_flush_buffers(queue, start_index, flush_count);
else if (!atomic_read(&queue->set_pci_flags_count))
atomic_xchg(&queue->state, QETH_OUT_Q_LOCKED_FLUSH);
/*
@@ -3274,7 +3283,7 @@ out:
if (!flush_count && !atomic_read(&queue->set_pci_flags_count))
flush_count += qeth_flush_buffers_on_no_pci(queue);
if (flush_count)
- qeth_flush_buffers(queue, 0, start_index, flush_count);
+ qeth_flush_buffers(queue, start_index, flush_count);
}
/* at this point the queue is UNLOCKED again */
if (queue->card->options.performance_stats && do_pack)
@@ -3336,7 +3345,7 @@ int qeth_change_mtu(struct net_device *dev, int new_mtu)
struct qeth_card *card;
char dbf_text[15];
- card = netdev_priv(dev);
+ card = dev->ml_priv;
QETH_DBF_TEXT(TRACE, 4, "chgmtu");
sprintf(dbf_text, "%8x", new_mtu);
@@ -3358,7 +3367,7 @@ struct net_device_stats *qeth_get_stats(struct net_device *dev)
{
struct qeth_card *card;
- card = netdev_priv(dev);
+ card = dev->ml_priv;
QETH_DBF_TEXT(TRACE, 5, "getstat");
@@ -3410,7 +3419,7 @@ void qeth_tx_timeout(struct net_device *dev)
{
struct qeth_card *card;
- card = netdev_priv(dev);
+ card = dev->ml_priv;
card->stats.tx_errors++;
qeth_schedule_recovery(card);
}
@@ -3418,7 +3427,7 @@ EXPORT_SYMBOL_GPL(qeth_tx_timeout);
int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
int rc = 0;
switch (regnum) {
@@ -3686,10 +3695,6 @@ static int qeth_qdio_establish(struct qeth_card *card)
init_data.q_format = qeth_get_qdio_q_format(card);
init_data.qib_param_field_format = 0;
init_data.qib_param_field = qib_param_field;
- init_data.min_input_threshold = QETH_MIN_INPUT_THRESHOLD;
- init_data.max_input_threshold = QETH_MAX_INPUT_THRESHOLD;
- init_data.min_output_threshold = QETH_MIN_OUTPUT_THRESHOLD;
- init_data.max_output_threshold = QETH_MAX_OUTPUT_THRESHOLD;
init_data.no_input_qs = 1;
init_data.no_output_qs = card->qdio.no_out_queues;
init_data.input_handler = card->discipline.input_handler;
@@ -3751,8 +3756,9 @@ static int qeth_core_driver_group(const char *buf, struct device *root_dev,
int qeth_core_hardsetup_card(struct qeth_card *card)
{
+ struct qdio_ssqd_desc *qdio_ssqd;
int retries = 3;
- int mpno;
+ int mpno = 0;
int rc;
QETH_DBF_TEXT(SETUP, 2, "hrdsetup");
@@ -3784,7 +3790,10 @@ retry:
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
return rc;
}
- mpno = qdio_get_ssqd_pct(CARD_DDEV(card));
+
+ qdio_ssqd = qdio_get_ssqd_desc(CARD_DDEV(card));
+ if (qdio_ssqd)
+ mpno = qdio_ssqd->pcnt;
if (mpno)
mpno = min(mpno - 1, QETH_MAX_PORTNO);
if (card->info.portno > mpno) {
@@ -4073,7 +4082,7 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
if (!get_device(dev))
return -ENODEV;
- QETH_DBF_TEXT_(SETUP, 2, "%s", gdev->dev.bus_id);
+ QETH_DBF_TEXT_(SETUP, 2, "%s", dev_name(&gdev->dev));
card = qeth_alloc_card();
if (!card) {
@@ -4092,7 +4101,6 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
rc = qeth_determine_card_type(card);
if (rc) {
- PRINT_WARN("%s: not a valid card type\n", __func__);
QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
goto err_card;
}
@@ -4140,6 +4148,7 @@ static void qeth_core_remove_device(struct ccwgroup_device *gdev)
unsigned long flags;
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ QETH_DBF_TEXT(SETUP, 2, "removedv");
if (card->discipline.ccwgdriver) {
card->discipline.ccwgdriver->remove(gdev);
qeth_core_free_discipline(card);
@@ -4269,7 +4278,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_stats_count);
void qeth_core_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
data[0] = card->stats.rx_packets -
card->perf_stats.initial_rx_packets;
data[1] = card->perf_stats.bufs_rec;
@@ -4329,7 +4338,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_strings);
void qeth_core_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
if (card->options.layer2)
strcpy(info->driver, "qeth_l2");
else
@@ -4347,7 +4356,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo);
int qeth_core_ethtool_get_settings(struct net_device *netdev,
struct ethtool_cmd *ecmd)
{
- struct qeth_card *card = netdev_priv(netdev);
+ struct qeth_card *card = netdev->ml_priv;
enum qeth_link_types link_type;
if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))
@@ -4459,8 +4468,17 @@ static int __init qeth_core_init(void)
rc = IS_ERR(qeth_core_root_dev) ? PTR_ERR(qeth_core_root_dev) : 0;
if (rc)
goto register_err;
- return 0;
+ qeth_core_header_cache = kmem_cache_create("qeth_hdr",
+ sizeof(struct qeth_hdr) + ETH_HLEN, 64, 0, NULL);
+ if (!qeth_core_header_cache) {
+ rc = -ENOMEM;
+ goto slab_err;
+ }
+
+ return 0;
+slab_err:
+ s390_root_dev_unregister(qeth_core_root_dev);
register_err:
driver_remove_file(&qeth_core_ccwgroup_driver.driver,
&driver_attr_group);
@@ -4482,6 +4500,7 @@ static void __exit qeth_core_exit(void)
&driver_attr_group);
ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver);
ccw_driver_unregister(&qeth_ccw_driver);
+ kmem_cache_destroy(qeth_core_header_cache);
qeth_unregister_dbf_views();
PRINT_INFO("core functions removed\n");
}
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index f682f7b1448..955ba7a31b9 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -35,7 +35,7 @@ static int qeth_l2_recover(void *);
static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
struct mii_ioctl_data *mii_data;
int rc = 0;
@@ -177,9 +177,10 @@ static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
qeth_l2_send_delgroupmac_cb);
}
-static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac)
+static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac)
{
struct qeth_mc_mac *mc;
+ int rc;
mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC);
@@ -188,8 +189,16 @@ static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac)
memcpy(mc->mc_addr, mac, OSA_ADDR_LEN);
mc->mc_addrlen = OSA_ADDR_LEN;
+ mc->is_vmac = vmac;
- if (!qeth_l2_send_setgroupmac(card, mac))
+ if (vmac) {
+ rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC,
+ NULL);
+ } else {
+ rc = qeth_l2_send_setgroupmac(card, mac);
+ }
+
+ if (!rc)
list_add_tail(&mc->list, &card->mc_list);
else
kfree(mc);
@@ -201,7 +210,11 @@ static void qeth_l2_del_all_mc(struct qeth_card *card)
spin_lock_bh(&card->mclock);
list_for_each_entry_safe(mc, tmp, &card->mc_list, list) {
- qeth_l2_send_delgroupmac(card, mc->mc_addr);
+ if (mc->is_vmac)
+ qeth_l2_send_setdelmac(card, mc->mc_addr,
+ IPA_CMD_DELVMAC, NULL);
+ else
+ qeth_l2_send_delgroupmac(card, mc->mc_addr);
list_del(&mc->list);
kfree(mc);
}
@@ -243,8 +256,7 @@ static void qeth_l2_get_packet_type(struct qeth_card *card,
static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
struct sk_buff *skb, int ipv, int cast_type)
{
- struct vlan_ethhdr *veth = (struct vlan_ethhdr *)((skb->data) +
- QETH_HEADER_SIZE);
+ struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
memset(hdr, 0, sizeof(struct qeth_hdr));
hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
@@ -317,7 +329,7 @@ static void qeth_l2_process_vlans(struct qeth_card *card, int clear)
static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
struct qeth_vlan_vid *id;
QETH_DBF_TEXT_(TRACE, 4, "aid:%d", vid);
@@ -334,7 +346,7 @@ static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
{
struct qeth_vlan_vid *id, *tmpid = NULL;
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
QETH_DBF_TEXT_(TRACE, 4, "kid:%d", vid);
spin_lock_bh(&card->vlanlock);
@@ -383,7 +395,8 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
}
if (card->state == CARD_STATE_SOFTSETUP) {
qeth_l2_process_vlans(card, 1);
- qeth_l2_del_all_mc(card);
+ if (!card->use_hard_stop)
+ qeth_l2_del_all_mc(card);
qeth_clear_ipacmd_list(card);
card->state = CARD_STATE_HARDSETUP;
}
@@ -547,7 +560,8 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card)
"device %s: x%x\n", CARD_BUS_ID(card), rc);
}
- if (card->info.guestlan) {
+ if ((card->info.type == QETH_CARD_TYPE_IQD) ||
+ (card->info.guestlan)) {
rc = qeth_setadpparms_change_macaddr(card);
if (rc) {
QETH_DBF_MESSAGE(2, "couldn't get MAC address on "
@@ -566,7 +580,7 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card)
static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
int rc = 0;
QETH_DBF_TEXT(TRACE, 3, "setmac");
@@ -590,8 +604,8 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
static void qeth_l2_set_multicast_list(struct net_device *dev)
{
- struct qeth_card *card = netdev_priv(dev);
- struct dev_mc_list *dm;
+ struct qeth_card *card = dev->ml_priv;
+ struct dev_addr_list *dm;
if (card->info.type == QETH_CARD_TYPE_OSN)
return ;
@@ -600,7 +614,11 @@ static void qeth_l2_set_multicast_list(struct net_device *dev)
qeth_l2_del_all_mc(card);
spin_lock_bh(&card->mclock);
for (dm = dev->mc_list; dm; dm = dm->next)
- qeth_l2_add_mc(card, dm->dmi_addr);
+ qeth_l2_add_mc(card, dm->da_addr, 0);
+
+ for (dm = dev->uc_list; dm; dm = dm->next)
+ qeth_l2_add_mc(card, dm->da_addr, 1);
+
spin_unlock_bh(&card->mclock);
if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
return;
@@ -612,7 +630,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
int rc;
struct qeth_hdr *hdr = NULL;
int elements = 0;
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
struct sk_buff *new_skb = skb;
int ipv = qeth_get_ip_version(skb);
int cast_type = qeth_get_cast_type(card, skb);
@@ -621,6 +639,9 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
int tx_bytes = skb->len;
enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
struct qeth_eddp_context *ctx = NULL;
+ int data_offset = -1;
+ int elements_needed = 0;
+ int hd_len = 0;
if ((card->state != CARD_STATE_UP) || !card->lan_online) {
card->stats.tx_carrier_errors++;
@@ -643,13 +664,32 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (card->info.type == QETH_CARD_TYPE_OSN)
hdr = (struct qeth_hdr *)skb->data;
else {
- /* create a clone with writeable headroom */
- new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr));
- if (!new_skb)
- goto tx_drop;
- hdr = (struct qeth_hdr *)skb_push(new_skb,
+ if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) &&
+ (skb_shinfo(skb)->nr_frags == 0)) {
+ new_skb = skb;
+ data_offset = ETH_HLEN;
+ hd_len = ETH_HLEN;
+ hdr = kmem_cache_alloc(qeth_core_header_cache,
+ GFP_ATOMIC);
+ if (!hdr)
+ goto tx_drop;
+ elements_needed++;
+ skb_reset_mac_header(new_skb);
+ qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
+ hdr->hdr.l2.pkt_length = new_skb->len;
+ memcpy(((char *)hdr) + sizeof(struct qeth_hdr),
+ skb_mac_header(new_skb), ETH_HLEN);
+ } else {
+ /* create a clone with writeable headroom */
+ new_skb = skb_realloc_headroom(skb,
sizeof(struct qeth_hdr));
- qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
+ if (!new_skb)
+ goto tx_drop;
+ hdr = (struct qeth_hdr *)skb_push(new_skb,
+ sizeof(struct qeth_hdr));
+ skb_set_mac_header(new_skb, sizeof(struct qeth_hdr));
+ qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
+ }
}
if (large_send == QETH_LARGE_SEND_EDDP) {
@@ -660,9 +700,13 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
goto tx_drop;
}
} else {
- elements = qeth_get_elements_no(card, (void *)hdr, new_skb, 0);
- if (!elements)
+ elements = qeth_get_elements_no(card, (void *)hdr, new_skb,
+ elements_needed);
+ if (!elements) {
+ if (data_offset >= 0)
+ kmem_cache_free(qeth_core_header_cache, hdr);
goto tx_drop;
+ }
}
if ((large_send == QETH_LARGE_SEND_NO) &&
@@ -674,7 +718,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
elements, ctx);
else
rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
- elements, ctx);
+ elements, ctx, data_offset, hd_len);
if (!rc) {
card->stats.tx_packets++;
card->stats.tx_bytes += tx_bytes;
@@ -701,6 +745,9 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (ctx != NULL)
qeth_eddp_put_context(ctx);
+ if (data_offset >= 0)
+ kmem_cache_free(qeth_core_header_cache, hdr);
+
if (rc == -EBUSY) {
if (new_skb != skb)
dev_kfree_skb_any(new_skb);
@@ -726,8 +773,7 @@ tx_drop:
}
static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
- unsigned int status, unsigned int qdio_err,
- unsigned int siga_err, unsigned int queue,
+ unsigned int qdio_err, unsigned int queue,
int first_element, int count, unsigned long card_ptr)
{
struct net_device *net_dev;
@@ -742,23 +788,20 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
card->perf_stats.inbound_cnt++;
card->perf_stats.inbound_start_time = qeth_get_micros();
}
- if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
- if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
- QETH_DBF_TEXT(TRACE, 1, "qdinchk");
- QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
- QETH_DBF_TEXT_(TRACE, 1, "%04X%04X", first_element,
- count);
- QETH_DBF_TEXT_(TRACE, 1, "%04X%04X", queue, status);
- qeth_schedule_recovery(card);
- return;
- }
+ if (qdio_err & QDIO_ERROR_ACTIVATE_CHECK_CONDITION) {
+ QETH_DBF_TEXT(TRACE, 1, "qdinchk");
+ QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
+ QETH_DBF_TEXT_(TRACE, 1, "%04X%04X", first_element,
+ count);
+ QETH_DBF_TEXT_(TRACE, 1, "%04X", queue);
+ qeth_schedule_recovery(card);
+ return;
}
for (i = first_element; i < (first_element + count); ++i) {
index = i % QDIO_MAX_BUFFERS_PER_Q;
buffer = &card->qdio.in_q->bufs[index];
- if (!((status & QDIO_STATUS_LOOK_FOR_ERROR) &&
- qeth_check_qdio_errors(buffer->buffer,
- qdio_err, siga_err, "qinerr")))
+ if (!(qdio_err &&
+ qeth_check_qdio_errors(buffer->buffer, qdio_err, "qinerr")))
qeth_l2_process_inbound_buffer(card, buffer, index);
/* clear buffer and give back to hardware */
qeth_put_buffer_pool_entry(card, buffer->pool_entry);
@@ -771,7 +814,7 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
static int qeth_l2_open(struct net_device *dev)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
QETH_DBF_TEXT(TRACE, 4, "qethopen");
if (card->state != CARD_STATE_SOFTSETUP)
@@ -784,7 +827,6 @@ static int qeth_l2_open(struct net_device *dev)
}
card->data.state = CH_STATE_UP;
card->state = CARD_STATE_UP;
- card->dev->flags |= IFF_UP;
netif_start_queue(dev);
if (!card->lan_online && netif_carrier_ok(dev))
@@ -795,11 +837,10 @@ static int qeth_l2_open(struct net_device *dev)
static int qeth_l2_stop(struct net_device *dev)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
QETH_DBF_TEXT(TRACE, 4, "qethstop");
netif_tx_disable(dev);
- card->dev->flags &= ~IFF_UP;
if (card->state == CARD_STATE_UP)
card->state = CARD_STATE_SOFTSETUP;
return 0;
@@ -842,7 +883,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
static int qeth_l2_ethtool_set_tso(struct net_device *dev, u32 data)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
if (data) {
if (card->options.large_send == QETH_LARGE_SEND_NO) {
@@ -898,7 +939,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
if (!card->dev)
return -ENODEV;
- card->dev->priv = card;
+ card->dev->ml_priv = card;
card->dev->tx_timeout = &qeth_tx_timeout;
card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
card->dev->open = qeth_l2_open;
@@ -1096,9 +1137,13 @@ static int qeth_l2_recover(void *ptr)
if (!rc)
PRINT_INFO("Device %s successfully recovered!\n",
CARD_BUS_ID(card));
- else
+ else {
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
PRINT_INFO("Device %s could not be recovered!\n",
CARD_BUS_ID(card));
+ }
return 0;
}
@@ -1182,7 +1227,7 @@ int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
QETH_DBF_TEXT(TRACE, 2, "osnsdmc");
if (!dev)
return -ENODEV;
- card = netdev_priv(dev);
+ card = dev->ml_priv;
if (!card)
return -ENODEV;
if ((card->state != CARD_STATE_UP) &&
@@ -1205,7 +1250,7 @@ int qeth_osn_register(unsigned char *read_dev_no, struct net_device **dev,
*dev = qeth_l2_netdev_by_devno(read_dev_no);
if (*dev == NULL)
return -ENODEV;
- card = netdev_priv(*dev);
+ card = (*dev)->ml_priv;
if (!card)
return -ENODEV;
if ((assist_cb == NULL) || (data_cb == NULL))
@@ -1223,7 +1268,7 @@ void qeth_osn_deregister(struct net_device *dev)
QETH_DBF_TEXT(TRACE, 2, "osndereg");
if (!dev)
return;
- card = netdev_priv(dev);
+ card = dev->ml_priv;
if (!card)
return;
card->osn_info.assist_cb = NULL;
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 999552c83bb..99547dea44d 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -944,15 +944,8 @@ static int qeth_l3_deregister_addr_entry(struct qeth_card *card,
else
rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP,
addr->del_flags);
- if (rc) {
+ if (rc)
QETH_DBF_TEXT(TRACE, 2, "failed");
- /* TODO: re-activate this warning as soon as we have a
- * clean mirco code
- qeth_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
- PRINT_WARN("Could not deregister IP address %s (rc=%x)\n",
- buf, rc);
- */
- }
return rc;
}
@@ -1820,7 +1813,7 @@ static void qeth_l3_free_vlan_addresses(struct qeth_card *card,
static void qeth_l3_vlan_rx_register(struct net_device *dev,
struct vlan_group *grp)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
unsigned long flags;
QETH_DBF_TEXT(TRACE, 4, "vlanreg");
@@ -1832,7 +1825,7 @@ static void qeth_l3_vlan_rx_register(struct net_device *dev,
static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
{
struct net_device *vlandev;
- struct qeth_card *card = (struct qeth_card *) dev->priv;
+ struct qeth_card *card = dev->ml_priv;
struct in_device *in_dev;
if (card->info.type == QETH_CARD_TYPE_IQD)
@@ -1858,7 +1851,7 @@ static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
static void qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
unsigned long flags;
QETH_DBF_TEXT_(TRACE, 4, "kid:%d", vid);
@@ -2020,7 +2013,7 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev,
}
}
- if (rc && !(netdev_priv(vlan_dev_info(dev)->real_dev) == (void *)card))
+ if (rc && !(vlan_dev_real_dev(dev)->ml_priv == (void *)card))
return 0;
return rc;
@@ -2054,9 +2047,9 @@ static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
rc = qeth_l3_verify_dev(dev);
if (rc == QETH_REAL_CARD)
- card = netdev_priv(dev);
+ card = dev->ml_priv;
else if (rc == QETH_VLAN_CARD)
- card = netdev_priv(vlan_dev_info(dev)->real_dev);
+ card = vlan_dev_real_dev(dev)->ml_priv;
if (card && card->options.layer2)
card = NULL;
QETH_DBF_TEXT_(TRACE, 4, "%d", rc);
@@ -2117,7 +2110,7 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
static void qeth_l3_set_multicast_list(struct net_device *dev)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
QETH_DBF_TEXT(TRACE, 3, "setmulti");
qeth_l3_delete_mc_addresses(card);
@@ -2445,7 +2438,7 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card)
static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
struct qeth_arp_cache_entry arp_entry;
struct mii_ioctl_data *mii_data;
int rc = 0;
@@ -2602,7 +2595,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
u16 *tag;
struct qeth_hdr *hdr = NULL;
int elements_needed = 0;
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
struct sk_buff *new_skb = NULL;
int ipv = qeth_get_ip_version(skb);
int cast_type = qeth_get_cast_type(card, skb);
@@ -2611,6 +2604,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
int tx_bytes = skb->len;
enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
struct qeth_eddp_context *ctx = NULL;
+ int data_offset = -1;
if ((card->info.type == QETH_CARD_TYPE_IQD) &&
(skb->protocol != htons(ETH_P_IPV6)) &&
@@ -2631,14 +2625,28 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
card->perf_stats.outbound_start_time = qeth_get_micros();
}
- /* create a clone with writeable headroom */
- new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso) +
- VLAN_HLEN);
- if (!new_skb)
- goto tx_drop;
+ if (skb_is_gso(skb))
+ large_send = card->options.large_send;
+
+ if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) &&
+ (skb_shinfo(skb)->nr_frags == 0)) {
+ new_skb = skb;
+ data_offset = ETH_HLEN;
+ hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
+ if (!hdr)
+ goto tx_drop;
+ elements_needed++;
+ } else {
+ /* create a clone with writeable headroom */
+ new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso)
+ + VLAN_HLEN);
+ if (!new_skb)
+ goto tx_drop;
+ }
if (card->info.type == QETH_CARD_TYPE_IQD) {
- skb_pull(new_skb, ETH_HLEN);
+ if (data_offset < 0)
+ skb_pull(new_skb, ETH_HLEN);
} else {
if (new_skb->protocol == htons(ETH_P_IP)) {
if (card->dev->type == ARPHRD_IEEE802_TR)
@@ -2658,15 +2666,12 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
tag = (u16 *)(new_skb->data + 12);
*tag = __constant_htons(ETH_P_8021Q);
*(tag + 1) = htons(vlan_tx_tag_get(new_skb));
- VLAN_TX_SKB_CB(new_skb)->magic = 0;
+ new_skb->vlan_tci = 0;
}
}
netif_stop_queue(dev);
- if (skb_is_gso(new_skb))
- large_send = card->options.large_send;
-
/* fix hardware limitation: as long as we do not have sbal
* chaining we can not send long frag lists so we temporary
* switch to EDDP
@@ -2684,9 +2689,16 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
qeth_tso_fill_header(card, hdr, new_skb);
elements_needed++;
} else {
- hdr = (struct qeth_hdr *)skb_push(new_skb,
+ if (data_offset < 0) {
+ hdr = (struct qeth_hdr *)skb_push(new_skb,
sizeof(struct qeth_hdr));
- qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type);
+ qeth_l3_fill_header(card, hdr, new_skb, ipv,
+ cast_type);
+ } else {
+ qeth_l3_fill_header(card, hdr, new_skb, ipv,
+ cast_type);
+ hdr->hdr.l3.length = new_skb->len - data_offset;
+ }
}
if (large_send == QETH_LARGE_SEND_EDDP) {
@@ -2702,8 +2714,11 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
} else {
int elems = qeth_get_elements_no(card, (void *)hdr, new_skb,
elements_needed);
- if (!elems)
+ if (!elems) {
+ if (data_offset >= 0)
+ kmem_cache_free(qeth_core_header_cache, hdr);
goto tx_drop;
+ }
elements_needed += elems;
}
@@ -2716,7 +2731,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
elements_needed, ctx);
else
rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
- elements_needed, ctx);
+ elements_needed, ctx, data_offset, 0);
if (!rc) {
card->stats.tx_packets++;
@@ -2744,6 +2759,9 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (ctx != NULL)
qeth_eddp_put_context(ctx);
+ if (data_offset >= 0)
+ kmem_cache_free(qeth_core_header_cache, hdr);
+
if (rc == -EBUSY) {
if (new_skb != skb)
dev_kfree_skb_any(new_skb);
@@ -2770,14 +2788,13 @@ tx_drop:
static int qeth_l3_open(struct net_device *dev)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
QETH_DBF_TEXT(TRACE, 4, "qethopen");
if (card->state != CARD_STATE_SOFTSETUP)
return -ENODEV;
card->data.state = CH_STATE_UP;
card->state = CARD_STATE_UP;
- card->dev->flags |= IFF_UP;
netif_start_queue(dev);
if (!card->lan_online && netif_carrier_ok(dev))
@@ -2787,11 +2804,10 @@ static int qeth_l3_open(struct net_device *dev)
static int qeth_l3_stop(struct net_device *dev)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
QETH_DBF_TEXT(TRACE, 4, "qethstop");
netif_tx_disable(dev);
- card->dev->flags &= ~IFF_UP;
if (card->state == CARD_STATE_UP)
card->state = CARD_STATE_SOFTSETUP;
return 0;
@@ -2799,14 +2815,14 @@ static int qeth_l3_stop(struct net_device *dev)
static u32 qeth_l3_ethtool_get_rx_csum(struct net_device *dev)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
return (card->options.checksum_type == HW_CHECKSUMMING);
}
static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
enum qeth_card_states old_state;
enum qeth_checksum_types csum_type;
@@ -2832,7 +2848,7 @@ static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data)
static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data)
{
- struct qeth_card *card = netdev_priv(dev);
+ struct qeth_card *card = dev->ml_priv;
if (data) {
if (card->options.large_send == QETH_LARGE_SEND_NO) {
@@ -2922,7 +2938,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
return -ENODEV;
card->dev->hard_start_xmit = qeth_l3_hard_start_xmit;
- card->dev->priv = card;
+ card->dev->ml_priv = card;
card->dev->tx_timeout = &qeth_tx_timeout;
card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
card->dev->open = qeth_l3_open;
@@ -2946,8 +2962,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
}
static void qeth_l3_qdio_input_handler(struct ccw_device *ccwdev,
- unsigned int status, unsigned int qdio_err,
- unsigned int siga_err, unsigned int queue, int first_element,
+ unsigned int qdio_err, unsigned int queue, int first_element,
int count, unsigned long card_ptr)
{
struct net_device *net_dev;
@@ -2962,23 +2977,21 @@ static void qeth_l3_qdio_input_handler(struct ccw_device *ccwdev,
card->perf_stats.inbound_cnt++;
card->perf_stats.inbound_start_time = qeth_get_micros();
}
- if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
- if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
- QETH_DBF_TEXT(TRACE, 1, "qdinchk");
- QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
- QETH_DBF_TEXT_(TRACE, 1, "%04X%04X",
- first_element, count);
- QETH_DBF_TEXT_(TRACE, 1, "%04X%04X", queue, status);
- qeth_schedule_recovery(card);
- return;
- }
+ if (qdio_err & QDIO_ERROR_ACTIVATE_CHECK_CONDITION) {
+ QETH_DBF_TEXT(TRACE, 1, "qdinchk");
+ QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
+ QETH_DBF_TEXT_(TRACE, 1, "%04X%04X",
+ first_element, count);
+ QETH_DBF_TEXT_(TRACE, 1, "%04X", queue);
+ qeth_schedule_recovery(card);
+ return;
}
for (i = first_element; i < (first_element + count); ++i) {
index = i % QDIO_MAX_BUFFERS_PER_Q;
buffer = &card->qdio.in_q->bufs[index];
- if (!((status & QDIO_STATUS_LOOK_FOR_ERROR) &&
+ if (!(qdio_err &&
qeth_check_qdio_errors(buffer->buffer,
- qdio_err, siga_err, "qinerr")))
+ qdio_err, "qinerr")))
qeth_l3_process_inbound_buffer(card, buffer, index);
/* clear buffer and give back to hardware */
qeth_put_buffer_pool_entry(card, buffer->pool_entry);
@@ -3203,9 +3216,13 @@ static int qeth_l3_recover(void *ptr)
if (!rc)
PRINT_INFO("Device %s successfully recovered!\n",
CARD_BUS_ID(card));
- else
+ else {
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
PRINT_INFO("Device %s could not be recovered!\n",
CARD_BUS_ID(card));
+ }
return 0;
}
diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c
index ac1993708ae..210ddb63974 100644
--- a/drivers/s390/net/qeth_l3_sys.c
+++ b/drivers/s390/net/qeth_l3_sys.c
@@ -136,7 +136,7 @@ static ssize_t qeth_l3_dev_route6_store(struct device *dev,
return -EINVAL;
if (!qeth_is_supported(card, IPA_IPV6)) {
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
return qeth_l3_dev_route_store(card, &card->options.route6,
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index 8735a415a11..164e090c262 100644
--- a/drivers/s390/net/smsgiucv.c
+++ b/drivers/s390/net/smsgiucv.c
@@ -156,11 +156,8 @@ static int __init smsg_init(void)
if (rc != 0)
goto out;
rc = iucv_register(&smsg_handler, 1);
- if (rc) {
- printk(KERN_ERR "SMSGIUCV: failed to register to iucv");
- rc = -EIO; /* better errno ? */
+ if (rc)
goto out_driver;
- }
smsg_path = iucv_path_alloc(255, 0, GFP_KERNEL);
if (!smsg_path) {
rc = -ENOMEM;
@@ -168,11 +165,8 @@ static int __init smsg_init(void)
}
rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ",
NULL, NULL, NULL);
- if (rc) {
- printk(KERN_ERR "SMSGIUCV: failed to connect to *MSG");
- rc = -EIO; /* better errno ? */
+ if (rc)
goto out_free;
- }
cpcmd("SET SMSG IUCV", NULL, 0, NULL);
return 0;
diff --git a/drivers/s390/s390_rdev.c b/drivers/s390/s390_rdev.c
index 3c7145d9f9a..64371c05a3b 100644
--- a/drivers/s390/s390_rdev.c
+++ b/drivers/s390/s390_rdev.c
@@ -30,7 +30,7 @@ s390_root_dev_register(const char *name)
dev = kzalloc(sizeof(struct device), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
- strncpy(dev->bus_id, name, min(strlen(name), (size_t)BUS_ID_SIZE));
+ dev_set_name(dev, name);
dev->release = s390_root_dev_release;
ret = device_register(dev);
if (ret) {
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
index 5bfbe765983..834e9ee7e93 100644
--- a/drivers/s390/s390mach.c
+++ b/drivers/s390/s390mach.c
@@ -2,10 +2,10 @@
* drivers/s390/s390mach.c
* S/390 machine check handler
*
- * S390 version
- * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Copyright IBM Corp. 2000,2008
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
+ * Cornelia Huck <cornelia.huck@de.ibm.com>
*/
#include <linux/init.h>
@@ -18,10 +18,6 @@
#include <asm/etr.h>
#include <asm/lowcore.h>
#include <asm/cio.h>
-#include "cio/cio.h"
-#include "cio/chsc.h"
-#include "cio/css.h"
-#include "cio/chp.h"
#include "s390mach.h"
static struct semaphore m_sem;
@@ -36,13 +32,40 @@ s390_handle_damage(char *msg)
for(;;);
}
+static crw_handler_t crw_handlers[NR_RSCS];
+
+/**
+ * s390_register_crw_handler() - register a channel report word handler
+ * @rsc: reporting source code to handle
+ * @handler: handler to be registered
+ *
+ * Returns %0 on success and a negative error value otherwise.
+ */
+int s390_register_crw_handler(int rsc, crw_handler_t handler)
+{
+ if ((rsc < 0) || (rsc >= NR_RSCS))
+ return -EINVAL;
+ if (!cmpxchg(&crw_handlers[rsc], NULL, handler))
+ return 0;
+ return -EBUSY;
+}
+
+/**
+ * s390_unregister_crw_handler() - unregister a channel report word handler
+ * @rsc: reporting source code to handle
+ */
+void s390_unregister_crw_handler(int rsc)
+{
+ if ((rsc < 0) || (rsc >= NR_RSCS))
+ return;
+ xchg(&crw_handlers[rsc], NULL);
+ synchronize_sched();
+}
+
/*
* Retrieve CRWs and call function to handle event.
- *
- * Note : we currently process CRWs for io and chsc subchannels only
*/
-static int
-s390_collect_crw_info(void *param)
+static int s390_collect_crw_info(void *param)
{
struct crw crw[2];
int ccode;
@@ -84,57 +107,24 @@ repeat:
crw[chain].rsid);
/* Check for overflows. */
if (crw[chain].oflw) {
+ int i;
+
pr_debug("%s: crw overflow detected!\n", __func__);
- css_schedule_eval_all();
+ for (i = 0; i < NR_RSCS; i++) {
+ if (crw_handlers[i])
+ crw_handlers[i](NULL, NULL, 1);
+ }
chain = 0;
continue;
}
- switch (crw[chain].rsc) {
- case CRW_RSC_SCH:
- if (crw[0].chn && !chain)
- break;
- pr_debug("source is subchannel %04X\n", crw[0].rsid);
- css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0);
- break;
- case CRW_RSC_MONITOR:
- pr_debug("source is monitoring facility\n");
- break;
- case CRW_RSC_CPATH:
- pr_debug("source is channel path %02X\n", crw[0].rsid);
- /*
- * Check for solicited machine checks. These are
- * created by reset channel path and need not be
- * reported to the common I/O layer.
- */
- if (crw[chain].slct) {
- pr_debug("solicited machine check for "
- "channel path %02X\n", crw[0].rsid);
- break;
- }
- switch (crw[0].erc) {
- case CRW_ERC_IPARM: /* Path has come. */
- chp_process_crw(crw[0].rsid, 1);
- break;
- case CRW_ERC_PERRI: /* Path has gone. */
- case CRW_ERC_PERRN:
- chp_process_crw(crw[0].rsid, 0);
- break;
- default:
- pr_debug("Don't know how to handle erc=%x\n",
- crw[0].erc);
- }
- break;
- case CRW_RSC_CONFIG:
- pr_debug("source is configuration-alert facility\n");
- break;
- case CRW_RSC_CSS:
- pr_debug("source is channel subsystem\n");
- chsc_process_crw();
- break;
- default:
- pr_debug("unknown source\n");
- break;
+ if (crw[0].chn && !chain) {
+ chain++;
+ continue;
}
+ if (crw_handlers[crw[chain].rsc])
+ crw_handlers[crw[chain].rsc](&crw[0],
+ chain ? &crw[1] : NULL,
+ 0);
/* chain is always 0 or 1 here. */
chain = crw[chain].chn ? chain + 1 : 0;
}
@@ -468,6 +458,10 @@ s390_do_machine_check(struct pt_regs *regs)
etr_sync_check();
if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH))
etr_switch_to_local();
+ if (S390_lowcore.external_damage_code & (1U << ED_STP_SYNC))
+ stp_sync_check();
+ if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND))
+ stp_island_check();
}
if (mci->se)
diff --git a/drivers/s390/s390mach.h b/drivers/s390/s390mach.h
index ca681f9b67f..d39f8b697d2 100644
--- a/drivers/s390/s390mach.h
+++ b/drivers/s390/s390mach.h
@@ -72,6 +72,13 @@ struct crw {
__u32 rsid : 16; /* reporting-source ID */
} __attribute__ ((packed));
+typedef void (*crw_handler_t)(struct crw *, struct crw *, int);
+
+extern int s390_register_crw_handler(int rsc, crw_handler_t handler);
+extern void s390_unregister_crw_handler(int rsc);
+
+#define NR_RSCS 16
+
#define CRW_RSC_MONITOR 0x2 /* monitoring facility */
#define CRW_RSC_SCH 0x3 /* subchannel */
#define CRW_RSC_CPATH 0x4 /* channel path */
@@ -105,6 +112,9 @@ static inline int stcrw(struct crw *pcrw )
#define ED_ETR_SYNC 12 /* External damage ETR sync check */
#define ED_ETR_SWITCH 13 /* External damage ETR switch to local */
+#define ED_STP_SYNC 7 /* External damage STP sync check */
+#define ED_STP_ISLAND 6 /* External damage STP island check */
+
struct pt_regs;
void s390_handle_mcck(void);
diff --git a/drivers/s390/scsi/Makefile b/drivers/s390/scsi/Makefile
index d6a78f1a2f1..cb301cc6178 100644
--- a/drivers/s390/scsi/Makefile
+++ b/drivers/s390/scsi/Makefile
@@ -3,7 +3,6 @@
#
zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_scsi.o zfcp_erp.o zfcp_qdio.o \
- zfcp_fsf.o zfcp_dbf.o zfcp_sysfs_adapter.o zfcp_sysfs_port.o \
- zfcp_sysfs_unit.o zfcp_sysfs_driver.o
+ zfcp_fsf.o zfcp_dbf.o zfcp_sysfs.o zfcp_fc.o zfcp_cfdc.o
obj-$(CONFIG_ZFCP) += zfcp.o
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 8c7e2b778ef..3b56220fb90 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -1,22 +1,9 @@
/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
*
- * (C) Copyright IBM Corp. 2002, 2006
+ * Module interface and handling of zfcp data structures.
*
- * 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 Corporation 2002, 2008
*/
/*
@@ -31,93 +18,25 @@
* Maxim Shchetynin
* Volker Sameske
* Ralph Wuerthner
+ * Michael Loehr
+ * Swen Schillig
+ * Christof Schmitt
+ * Martin Petermann
+ * Sven Schuetz
*/
+#include <linux/miscdevice.h>
#include "zfcp_ext.h"
-/* accumulated log level (module parameter) */
-static u32 loglevel = ZFCP_LOG_LEVEL_DEFAULTS;
static char *device;
-/*********************** FUNCTION PROTOTYPES *********************************/
-
-/* written against the module interface */
-static int __init zfcp_module_init(void);
-
-/* FCP related */
-static void zfcp_ns_gid_pn_handler(unsigned long);
-
-/* miscellaneous */
-static int zfcp_sg_list_alloc(struct zfcp_sg_list *, size_t);
-static void zfcp_sg_list_free(struct zfcp_sg_list *);
-static int zfcp_sg_list_copy_from_user(struct zfcp_sg_list *,
- void __user *, size_t);
-static int zfcp_sg_list_copy_to_user(void __user *,
- struct zfcp_sg_list *, size_t);
-static long zfcp_cfdc_dev_ioctl(struct file *, unsigned int, unsigned long);
-
-#define ZFCP_CFDC_IOC_MAGIC 0xDD
-#define ZFCP_CFDC_IOC \
- _IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_sense_data)
-
-
-static const struct file_operations zfcp_cfdc_fops = {
- .unlocked_ioctl = zfcp_cfdc_dev_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = zfcp_cfdc_dev_ioctl
-#endif
-};
-
-static struct miscdevice zfcp_cfdc_misc = {
- .minor = ZFCP_CFDC_DEV_MINOR,
- .name = ZFCP_CFDC_DEV_NAME,
- .fops = &zfcp_cfdc_fops
-};
-
-/*********************** KERNEL/MODULE PARAMETERS ***************************/
-
-/* declare driver module init/cleanup functions */
-module_init(zfcp_module_init);
MODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com");
-MODULE_DESCRIPTION
- ("FCP (SCSI over Fibre Channel) HBA driver for IBM System z9 and zSeries");
+MODULE_DESCRIPTION("FCP HBA driver");
MODULE_LICENSE("GPL");
module_param(device, charp, 0400);
MODULE_PARM_DESC(device, "specify initial device");
-module_param(loglevel, uint, 0400);
-MODULE_PARM_DESC(loglevel,
- "log levels, 8 nibbles: "
- "FC ERP QDIO CIO Config FSF SCSI Other, "
- "levels: 0=none 1=normal 2=devel 3=trace");
-
-/****************************************************************/
-/************** Functions without logging ***********************/
-/****************************************************************/
-
-void
-_zfcp_hex_dump(char *addr, int count)
-{
- int i;
- for (i = 0; i < count; i++) {
- printk("%02x", addr[i]);
- if ((i % 4) == 3)
- printk(" ");
- if ((i % 32) == 31)
- printk("\n");
- }
- if (((i-1) % 32) != 31)
- printk("\n");
-}
-
-
-/****************************************************************/
-/****** Functions to handle the request ID hash table ********/
-/****************************************************************/
-
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_FSF
-
static int zfcp_reqlist_alloc(struct zfcp_adapter *adapter)
{
int idx;
@@ -132,11 +51,12 @@ static int zfcp_reqlist_alloc(struct zfcp_adapter *adapter)
return 0;
}
-static void zfcp_reqlist_free(struct zfcp_adapter *adapter)
-{
- kfree(adapter->req_list);
-}
-
+/**
+ * zfcp_reqlist_isempty - is the request list empty
+ * @adapter: pointer to struct zfcp_adapter
+ *
+ * Returns: true if list is empty, false otherwise
+ */
int zfcp_reqlist_isempty(struct zfcp_adapter *adapter)
{
unsigned int idx;
@@ -147,62 +67,46 @@ int zfcp_reqlist_isempty(struct zfcp_adapter *adapter)
return 1;
}
-#undef ZFCP_LOG_AREA
-
-/****************************************************************/
-/************** Uncategorised Functions *************************/
-/****************************************************************/
-
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER
-
-/**
- * zfcp_device_setup - setup function
- * @str: pointer to parameter string
- *
- * Parse "device=..." parameter string.
- */
-static int __init
-zfcp_device_setup(char *devstr)
+static int __init zfcp_device_setup(char *devstr)
{
- char *tmp, *str;
- size_t len;
+ char *token;
+ char *str;
if (!devstr)
return 0;
- len = strlen(devstr) + 1;
- str = kmalloc(len, GFP_KERNEL);
+ /* duplicate devstr and keep the original for sysfs presentation*/
+ str = kmalloc(strlen(devstr) + 1, GFP_KERNEL);
if (!str)
- goto err_out;
- memcpy(str, devstr, len);
+ return 0;
- tmp = strchr(str, ',');
- if (!tmp)
- goto err_out;
- *tmp++ = '\0';
- strncpy(zfcp_data.init_busid, str, BUS_ID_SIZE);
- zfcp_data.init_busid[BUS_ID_SIZE-1] = '\0';
+ strcpy(str, devstr);
- zfcp_data.init_wwpn = simple_strtoull(tmp, &tmp, 0);
- if (*tmp++ != ',')
+ token = strsep(&str, ",");
+ if (!token || strlen(token) >= BUS_ID_SIZE)
goto err_out;
- if (*tmp == '\0')
+ strncpy(zfcp_data.init_busid, token, BUS_ID_SIZE);
+
+ token = strsep(&str, ",");
+ if (!token || strict_strtoull(token, 0,
+ (unsigned long long *) &zfcp_data.init_wwpn))
goto err_out;
- zfcp_data.init_fcp_lun = simple_strtoull(tmp, &tmp, 0);
- if (*tmp != '\0')
+ token = strsep(&str, ",");
+ if (!token || strict_strtoull(token, 0,
+ (unsigned long long *) &zfcp_data.init_fcp_lun))
goto err_out;
+
kfree(str);
return 1;
err_out:
- ZFCP_LOG_NORMAL("Parse error for device parameter string %s\n", str);
kfree(str);
+ pr_err("zfcp: %s is not a valid SCSI device\n", devstr);
return 0;
}
-static void __init
-zfcp_init_device_configure(void)
+static void __init zfcp_init_device_configure(void)
{
struct zfcp_adapter *adapter;
struct zfcp_port *port;
@@ -215,75 +119,65 @@ zfcp_init_device_configure(void)
zfcp_adapter_get(adapter);
read_unlock_irq(&zfcp_data.config_lock);
- if (adapter == NULL)
+ if (!adapter)
goto out_adapter;
port = zfcp_port_enqueue(adapter, zfcp_data.init_wwpn, 0, 0);
- if (!port)
+ if (IS_ERR(port))
goto out_port;
unit = zfcp_unit_enqueue(port, zfcp_data.init_fcp_lun);
- if (!unit)
+ if (IS_ERR(unit))
goto out_unit;
up(&zfcp_data.config_sema);
ccw_device_set_online(adapter->ccw_device);
+
zfcp_erp_wait(adapter);
+ wait_event(adapter->erp_done_wqh,
+ !(atomic_read(&unit->status) &
+ ZFCP_STATUS_UNIT_SCSI_WORK_PENDING));
+
down(&zfcp_data.config_sema);
zfcp_unit_put(unit);
- out_unit:
+out_unit:
zfcp_port_put(port);
- out_port:
+out_port:
zfcp_adapter_put(adapter);
- out_adapter:
+out_adapter:
up(&zfcp_data.config_sema);
return;
}
-static int calc_alignment(int size)
+static struct kmem_cache *zfcp_cache_create(int size, char *name)
{
int align = 1;
-
- if (!size)
- return 0;
-
while ((size - align) > 0)
align <<= 1;
-
- return align;
+ return kmem_cache_create(name , size, align, 0, NULL);
}
-static int __init
-zfcp_module_init(void)
+static int __init zfcp_module_init(void)
{
int retval = -ENOMEM;
- int size, align;
- size = sizeof(struct zfcp_fsf_req_qtcb);
- align = calc_alignment(size);
- zfcp_data.fsf_req_qtcb_cache =
- kmem_cache_create("zfcp_fsf", size, align, 0, NULL);
+ zfcp_data.fsf_req_qtcb_cache = zfcp_cache_create(
+ sizeof(struct zfcp_fsf_req_qtcb), "zfcp_fsf");
if (!zfcp_data.fsf_req_qtcb_cache)
goto out;
- size = sizeof(struct fsf_status_read_buffer);
- align = calc_alignment(size);
- zfcp_data.sr_buffer_cache =
- kmem_cache_create("zfcp_sr", size, align, 0, NULL);
+ zfcp_data.sr_buffer_cache = zfcp_cache_create(
+ sizeof(struct fsf_status_read_buffer), "zfcp_sr");
if (!zfcp_data.sr_buffer_cache)
goto out_sr_cache;
- size = sizeof(struct zfcp_gid_pn_data);
- align = calc_alignment(size);
- zfcp_data.gid_pn_cache =
- kmem_cache_create("zfcp_gid", size, align, 0, NULL);
+ zfcp_data.gid_pn_cache = zfcp_cache_create(
+ sizeof(struct zfcp_gid_pn_data), "zfcp_gid");
if (!zfcp_data.gid_pn_cache)
goto out_gid_cache;
- atomic_set(&zfcp_data.loglevel, loglevel);
+ zfcp_data.work_queue = create_singlethread_workqueue("zfcp_wq");
- /* initialize adapter list */
INIT_LIST_HEAD(&zfcp_data.adapter_list_head);
-
- /* initialize adapters to be removed list head */
- INIT_LIST_HEAD(&zfcp_data.adapter_remove_lh);
+ sema_init(&zfcp_data.config_sema, 1);
+ rwlock_init(&zfcp_data.config_lock);
zfcp_data.scsi_transport_template =
fc_attach_transport(&zfcp_transport_functions);
@@ -291,25 +185,15 @@ zfcp_module_init(void)
goto out_transport;
retval = misc_register(&zfcp_cfdc_misc);
- if (retval != 0) {
- ZFCP_LOG_INFO("registration of misc device "
- "zfcp_cfdc failed\n");
+ if (retval) {
+ pr_err("zfcp: Registering the misc device zfcp_cfdc failed\n");
goto out_misc;
}
- ZFCP_LOG_TRACE("major/minor for zfcp_cfdc: %d/%d\n",
- ZFCP_CFDC_DEV_MAJOR, zfcp_cfdc_misc.minor);
-
- /* Initialise proc semaphores */
- sema_init(&zfcp_data.config_sema, 1);
-
- /* initialise configuration rw lock */
- rwlock_init(&zfcp_data.config_lock);
-
- /* setup dynamic I/O */
retval = zfcp_ccw_register();
if (retval) {
- ZFCP_LOG_NORMAL("registration with common I/O layer failed\n");
+ pr_err("zfcp: The zfcp device driver could not register with "
+ "the common I/O layer\n");
goto out_ccw_register;
}
@@ -318,528 +202,89 @@ zfcp_module_init(void)
goto out;
- out_ccw_register:
+out_ccw_register:
misc_deregister(&zfcp_cfdc_misc);
- out_misc:
+out_misc:
fc_release_transport(zfcp_data.scsi_transport_template);
- out_transport:
+out_transport:
kmem_cache_destroy(zfcp_data.gid_pn_cache);
- out_gid_cache:
+out_gid_cache:
kmem_cache_destroy(zfcp_data.sr_buffer_cache);
- out_sr_cache:
+out_sr_cache:
kmem_cache_destroy(zfcp_data.fsf_req_qtcb_cache);
- out:
+out:
return retval;
}
-/*
- * function: zfcp_cfdc_dev_ioctl
- *
- * purpose: Handle control file upload/download transaction via IOCTL
- * interface
- *
- * returns: 0 - Operation completed successfuly
- * -ENOTTY - Unknown IOCTL command
- * -EINVAL - Invalid sense data record
- * -ENXIO - The FCP adapter is not available
- * -EOPNOTSUPP - The FCP adapter does not have CFDC support
- * -ENOMEM - Insufficient memory
- * -EFAULT - User space memory I/O operation fault
- * -EPERM - Cannot create or queue FSF request or create SBALs
- * -ERESTARTSYS- Received signal (is mapped to EAGAIN by VFS)
- */
-static long
-zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
- unsigned long buffer)
-{
- struct zfcp_cfdc_sense_data *sense_data, __user *sense_data_user;
- struct zfcp_adapter *adapter = NULL;
- struct zfcp_fsf_req *fsf_req = NULL;
- struct zfcp_sg_list *sg_list = NULL;
- u32 fsf_command, option;
- char *bus_id = NULL;
- int retval = 0;
-
- sense_data = kmalloc(sizeof(struct zfcp_cfdc_sense_data), GFP_KERNEL);
- if (sense_data == NULL) {
- retval = -ENOMEM;
- goto out;
- }
-
- sg_list = kzalloc(sizeof(struct zfcp_sg_list), GFP_KERNEL);
- if (sg_list == NULL) {
- retval = -ENOMEM;
- goto out;
- }
-
- if (command != ZFCP_CFDC_IOC) {
- ZFCP_LOG_INFO("IOC request code 0x%x invalid\n", command);
- retval = -ENOTTY;
- goto out;
- }
-
- if ((sense_data_user = (void __user *) buffer) == NULL) {
- ZFCP_LOG_INFO("sense data record is required\n");
- retval = -EINVAL;
- goto out;
- }
-
- retval = copy_from_user(sense_data, sense_data_user,
- sizeof(struct zfcp_cfdc_sense_data));
- if (retval) {
- retval = -EFAULT;
- goto out;
- }
-
- if (sense_data->signature != ZFCP_CFDC_SIGNATURE) {
- ZFCP_LOG_INFO("invalid sense data request signature 0x%08x\n",
- ZFCP_CFDC_SIGNATURE);
- retval = -EINVAL;
- goto out;
- }
-
- switch (sense_data->command) {
-
- case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL:
- fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
- option = FSF_CFDC_OPTION_NORMAL_MODE;
- break;
-
- case ZFCP_CFDC_CMND_DOWNLOAD_FORCE:
- fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
- option = FSF_CFDC_OPTION_FORCE;
- break;
-
- case ZFCP_CFDC_CMND_FULL_ACCESS:
- fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
- option = FSF_CFDC_OPTION_FULL_ACCESS;
- break;
-
- case ZFCP_CFDC_CMND_RESTRICTED_ACCESS:
- fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
- option = FSF_CFDC_OPTION_RESTRICTED_ACCESS;
- break;
-
- case ZFCP_CFDC_CMND_UPLOAD:
- fsf_command = FSF_QTCB_UPLOAD_CONTROL_FILE;
- option = 0;
- break;
-
- default:
- ZFCP_LOG_INFO("invalid command code 0x%08x\n",
- sense_data->command);
- retval = -EINVAL;
- goto out;
- }
-
- bus_id = kmalloc(BUS_ID_SIZE, GFP_KERNEL);
- if (bus_id == NULL) {
- retval = -ENOMEM;
- goto out;
- }
- snprintf(bus_id, BUS_ID_SIZE, "%d.%d.%04x",
- (sense_data->devno >> 24),
- (sense_data->devno >> 16) & 0xFF,
- (sense_data->devno & 0xFFFF));
-
- read_lock_irq(&zfcp_data.config_lock);
- adapter = zfcp_get_adapter_by_busid(bus_id);
- if (adapter)
- zfcp_adapter_get(adapter);
- read_unlock_irq(&zfcp_data.config_lock);
-
- kfree(bus_id);
-
- if (adapter == NULL) {
- ZFCP_LOG_INFO("invalid adapter\n");
- retval = -ENXIO;
- goto out;
- }
-
- if (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE) {
- retval = zfcp_sg_list_alloc(sg_list,
- ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
- if (retval) {
- retval = -ENOMEM;
- goto out;
- }
- }
-
- if ((sense_data->command & ZFCP_CFDC_DOWNLOAD) &&
- (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE)) {
- retval = zfcp_sg_list_copy_from_user(
- sg_list, &sense_data_user->control_file,
- ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
- if (retval) {
- retval = -EFAULT;
- goto out;
- }
- }
-
- retval = zfcp_fsf_control_file(adapter, &fsf_req, fsf_command,
- option, sg_list);
- if (retval)
- goto out;
-
- if ((fsf_req->qtcb->prefix.prot_status != FSF_PROT_GOOD) &&
- (fsf_req->qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) {
- retval = -ENXIO;
- goto out;
- }
-
- sense_data->fsf_status = fsf_req->qtcb->header.fsf_status;
- memcpy(&sense_data->fsf_status_qual,
- &fsf_req->qtcb->header.fsf_status_qual,
- sizeof(union fsf_status_qual));
- memcpy(&sense_data->payloads, &fsf_req->qtcb->bottom.support.els, 256);
-
- retval = copy_to_user(sense_data_user, sense_data,
- sizeof(struct zfcp_cfdc_sense_data));
- if (retval) {
- retval = -EFAULT;
- goto out;
- }
-
- if (sense_data->command & ZFCP_CFDC_UPLOAD) {
- retval = zfcp_sg_list_copy_to_user(
- &sense_data_user->control_file, sg_list,
- ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
- if (retval) {
- retval = -EFAULT;
- goto out;
- }
- }
-
- out:
- if (fsf_req != NULL)
- zfcp_fsf_req_free(fsf_req);
-
- if ((adapter != NULL) && (retval != -ENXIO))
- zfcp_adapter_put(adapter);
-
- if (sg_list != NULL) {
- zfcp_sg_list_free(sg_list);
- kfree(sg_list);
- }
-
- kfree(sense_data);
-
- return retval;
-}
-
-
-/**
- * zfcp_sg_list_alloc - create a scatter-gather list of the specified size
- * @sg_list: structure describing a scatter gather list
- * @size: size of scatter-gather list
- * Return: 0 on success, else -ENOMEM
- *
- * In sg_list->sg a pointer to the created scatter-gather list is returned,
- * or NULL if we run out of memory. sg_list->count specifies the number of
- * elements of the scatter-gather list. The maximum size of a single element
- * in the scatter-gather list is PAGE_SIZE.
- */
-static int
-zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size)
-{
- struct scatterlist *sg;
- unsigned int i;
- int retval = 0;
- void *address;
-
- BUG_ON(sg_list == NULL);
-
- sg_list->count = size >> PAGE_SHIFT;
- if (size & ~PAGE_MASK)
- sg_list->count++;
- sg_list->sg = kcalloc(sg_list->count, sizeof(struct scatterlist),
- GFP_KERNEL);
- if (sg_list->sg == NULL) {
- sg_list->count = 0;
- retval = -ENOMEM;
- goto out;
- }
- sg_init_table(sg_list->sg, sg_list->count);
-
- for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) {
- address = (void *) get_zeroed_page(GFP_KERNEL);
- if (address == NULL) {
- sg_list->count = i;
- zfcp_sg_list_free(sg_list);
- retval = -ENOMEM;
- goto out;
- }
- zfcp_address_to_sg(address, sg, min(size, PAGE_SIZE));
- size -= sg->length;
- }
-
- out:
- return retval;
-}
-
-
-/**
- * zfcp_sg_list_free - free memory of a scatter-gather list
- * @sg_list: structure describing a scatter-gather list
- *
- * Memory for each element in the scatter-gather list is freed.
- * Finally sg_list->sg is freed itself and sg_list->count is reset.
- */
-static void
-zfcp_sg_list_free(struct zfcp_sg_list *sg_list)
-{
- struct scatterlist *sg;
- unsigned int i;
-
- BUG_ON(sg_list == NULL);
-
- for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++)
- free_page((unsigned long) zfcp_sg_to_address(sg));
-
- sg_list->count = 0;
- kfree(sg_list->sg);
-}
-
-/**
- * zfcp_sg_size - determine size of a scatter-gather list
- * @sg: array of (struct scatterlist)
- * @sg_count: elements in array
- * Return: size of entire scatter-gather list
- */
-static size_t zfcp_sg_size(struct scatterlist *sg, unsigned int sg_count)
-{
- unsigned int i;
- struct scatterlist *p;
- size_t size;
-
- size = 0;
- for (i = 0, p = sg; i < sg_count; i++, p++) {
- BUG_ON(p == NULL);
- size += p->length;
- }
-
- return size;
-}
-
-
-/**
- * zfcp_sg_list_copy_from_user -copy data from user space to scatter-gather list
- * @sg_list: structure describing a scatter-gather list
- * @user_buffer: pointer to buffer in user space
- * @size: number of bytes to be copied
- * Return: 0 on success, -EFAULT if copy_from_user fails.
- */
-static int
-zfcp_sg_list_copy_from_user(struct zfcp_sg_list *sg_list,
- void __user *user_buffer,
- size_t size)
-{
- struct scatterlist *sg;
- unsigned int length;
- void *zfcp_buffer;
- int retval = 0;
-
- BUG_ON(sg_list == NULL);
-
- if (zfcp_sg_size(sg_list->sg, sg_list->count) < size)
- return -EFAULT;
-
- for (sg = sg_list->sg; size > 0; sg++) {
- length = min((unsigned int)size, sg->length);
- zfcp_buffer = zfcp_sg_to_address(sg);
- if (copy_from_user(zfcp_buffer, user_buffer, length)) {
- retval = -EFAULT;
- goto out;
- }
- user_buffer += length;
- size -= length;
- }
-
- out:
- return retval;
-}
-
-
-/**
- * zfcp_sg_list_copy_to_user - copy data from scatter-gather list to user space
- * @user_buffer: pointer to buffer in user space
- * @sg_list: structure describing a scatter-gather list
- * @size: number of bytes to be copied
- * Return: 0 on success, -EFAULT if copy_to_user fails
- */
-static int
-zfcp_sg_list_copy_to_user(void __user *user_buffer,
- struct zfcp_sg_list *sg_list,
- size_t size)
-{
- struct scatterlist *sg;
- unsigned int length;
- void *zfcp_buffer;
- int retval = 0;
-
- BUG_ON(sg_list == NULL);
-
- if (zfcp_sg_size(sg_list->sg, sg_list->count) < size)
- return -EFAULT;
-
- for (sg = sg_list->sg; size > 0; sg++) {
- length = min((unsigned int) size, sg->length);
- zfcp_buffer = zfcp_sg_to_address(sg);
- if (copy_to_user(user_buffer, zfcp_buffer, length)) {
- retval = -EFAULT;
- goto out;
- }
- user_buffer += length;
- size -= length;
- }
-
- out:
- return retval;
-}
-
-
-#undef ZFCP_LOG_AREA
-
-/****************************************************************/
-/****** Functions for configuration/set-up of structures ********/
-/****************************************************************/
-
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
+module_init(zfcp_module_init);
/**
* zfcp_get_unit_by_lun - find unit in unit list of port by FCP LUN
* @port: pointer to port to search for unit
* @fcp_lun: FCP LUN to search for
- * Traverse list of all units of a port and return pointer to a unit
- * with the given FCP LUN.
+ *
+ * Returns: pointer to zfcp_unit or NULL
*/
-struct zfcp_unit *
-zfcp_get_unit_by_lun(struct zfcp_port *port, fcp_lun_t fcp_lun)
+struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun)
{
struct zfcp_unit *unit;
- int found = 0;
- list_for_each_entry(unit, &port->unit_list_head, list) {
+ list_for_each_entry(unit, &port->unit_list_head, list)
if ((unit->fcp_lun == fcp_lun) &&
- !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status))
- {
- found = 1;
- break;
- }
- }
- return found ? unit : NULL;
+ !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE))
+ return unit;
+ return NULL;
}
/**
* zfcp_get_port_by_wwpn - find port in port list of adapter by wwpn
* @adapter: pointer to adapter to search for port
* @wwpn: wwpn to search for
- * Traverse list of all ports of an adapter and return pointer to a port
- * with the given wwpn.
- */
-struct zfcp_port *
-zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, wwn_t wwpn)
-{
- struct zfcp_port *port;
- int found = 0;
-
- list_for_each_entry(port, &adapter->port_list_head, list) {
- if ((port->wwpn == wwpn) &&
- !(atomic_read(&port->status) &
- (ZFCP_STATUS_PORT_NO_WWPN | ZFCP_STATUS_COMMON_REMOVE))) {
- found = 1;
- break;
- }
- }
- return found ? port : NULL;
-}
-
-/**
- * zfcp_get_port_by_did - find port in port list of adapter by d_id
- * @adapter: pointer to adapter to search for port
- * @d_id: d_id to search for
- * Traverse list of all ports of an adapter and return pointer to a port
- * with the given d_id.
+ *
+ * Returns: pointer to zfcp_port or NULL
*/
-struct zfcp_port *
-zfcp_get_port_by_did(struct zfcp_adapter *adapter, u32 d_id)
+struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter,
+ u64 wwpn)
{
struct zfcp_port *port;
- int found = 0;
- list_for_each_entry(port, &adapter->port_list_head, list) {
- if ((port->d_id == d_id) &&
- !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status))
- {
- found = 1;
- break;
- }
- }
- return found ? port : NULL;
+ list_for_each_entry(port, &adapter->port_list_head, list)
+ if ((port->wwpn == wwpn) && !(atomic_read(&port->status) &
+ (ZFCP_STATUS_PORT_NO_WWPN | ZFCP_STATUS_COMMON_REMOVE)))
+ return port;
+ return NULL;
}
-/**
- * zfcp_get_adapter_by_busid - find adpater in adapter list by bus_id
- * @bus_id: bus_id to search for
- * Traverse list of all adapters and return pointer to an adapter
- * with the given bus_id.
- */
-struct zfcp_adapter *
-zfcp_get_adapter_by_busid(char *bus_id)
+static void zfcp_sysfs_unit_release(struct device *dev)
{
- struct zfcp_adapter *adapter;
- int found = 0;
-
- list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list) {
- if ((strncmp(bus_id, zfcp_get_busid_by_adapter(adapter),
- BUS_ID_SIZE) == 0) &&
- !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE,
- &adapter->status)){
- found = 1;
- break;
- }
- }
- return found ? adapter : NULL;
+ kfree(container_of(dev, struct zfcp_unit, sysfs_device));
}
/**
* zfcp_unit_enqueue - enqueue unit to unit list of a port.
* @port: pointer to port where unit is added
* @fcp_lun: FCP LUN of unit to be enqueued
- * Return: pointer to enqueued unit on success, NULL on error
+ * Returns: pointer to enqueued unit on success, ERR_PTR on error
* Locks: config_sema must be held to serialize changes to the unit list
*
* Sets up some unit internal structures and creates sysfs entry.
*/
-struct zfcp_unit *
-zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun)
+struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
{
struct zfcp_unit *unit;
- /*
- * check that there is no unit with this FCP_LUN already in list
- * and enqueue it.
- * Note: Unlike for the adapter and the port, this is an error
- */
- read_lock_irq(&zfcp_data.config_lock);
- unit = zfcp_get_unit_by_lun(port, fcp_lun);
- read_unlock_irq(&zfcp_data.config_lock);
- if (unit)
- return NULL;
-
- unit = kzalloc(sizeof (struct zfcp_unit), GFP_KERNEL);
+ unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
if (!unit)
- return NULL;
+ return ERR_PTR(-ENOMEM);
- /* initialise reference count stuff */
atomic_set(&unit->refcount, 0);
init_waitqueue_head(&unit->remove_wq);
unit->port = port;
unit->fcp_lun = fcp_lun;
- /* setup for sysfs registration */
- snprintf(unit->sysfs_device.bus_id, BUS_ID_SIZE, "0x%016llx", fcp_lun);
+ dev_set_name(&unit->sysfs_device, "0x%016llx",
+ (unsigned long long) fcp_lun);
unit->sysfs_device.parent = &port->sysfs_device;
unit->sysfs_device.release = zfcp_sysfs_unit_release;
dev_set_drvdata(&unit->sysfs_device, unit);
@@ -847,102 +292,107 @@ zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun)
/* mark unit unusable as long as sysfs registration is not complete */
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
- if (device_register(&unit->sysfs_device)) {
- kfree(unit);
- return NULL;
+ spin_lock_init(&unit->latencies.lock);
+ unit->latencies.write.channel.min = 0xFFFFFFFF;
+ unit->latencies.write.fabric.min = 0xFFFFFFFF;
+ unit->latencies.read.channel.min = 0xFFFFFFFF;
+ unit->latencies.read.fabric.min = 0xFFFFFFFF;
+ unit->latencies.cmd.channel.min = 0xFFFFFFFF;
+ unit->latencies.cmd.fabric.min = 0xFFFFFFFF;
+
+ read_lock_irq(&zfcp_data.config_lock);
+ if (zfcp_get_unit_by_lun(port, fcp_lun)) {
+ read_unlock_irq(&zfcp_data.config_lock);
+ goto err_out_free;
}
+ read_unlock_irq(&zfcp_data.config_lock);
- if (zfcp_sysfs_unit_create_files(&unit->sysfs_device)) {
+ if (device_register(&unit->sysfs_device))
+ goto err_out_free;
+
+ if (sysfs_create_group(&unit->sysfs_device.kobj,
+ &zfcp_sysfs_unit_attrs)) {
device_unregister(&unit->sysfs_device);
- return NULL;
+ return ERR_PTR(-EIO);
}
zfcp_unit_get(unit);
- unit->scsi_lun = scsilun_to_int((struct scsi_lun *)&unit->fcp_lun);
write_lock_irq(&zfcp_data.config_lock);
list_add_tail(&unit->list, &port->unit_list_head);
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
+
write_unlock_irq(&zfcp_data.config_lock);
- port->units++;
zfcp_port_get(port);
return unit;
+
+err_out_free:
+ kfree(unit);
+ return ERR_PTR(-EINVAL);
}
-void
-zfcp_unit_dequeue(struct zfcp_unit *unit)
+/**
+ * zfcp_unit_dequeue - dequeue unit
+ * @unit: pointer to zfcp_unit
+ *
+ * waits until all work is done on unit and removes it then from the unit->list
+ * of the associated port.
+ */
+void zfcp_unit_dequeue(struct zfcp_unit *unit)
{
- zfcp_unit_wait(unit);
+ wait_event(unit->remove_wq, atomic_read(&unit->refcount) == 0);
write_lock_irq(&zfcp_data.config_lock);
list_del(&unit->list);
write_unlock_irq(&zfcp_data.config_lock);
- unit->port->units--;
zfcp_port_put(unit->port);
- zfcp_sysfs_unit_remove_files(&unit->sysfs_device);
+ sysfs_remove_group(&unit->sysfs_device.kobj, &zfcp_sysfs_unit_attrs);
device_unregister(&unit->sysfs_device);
}
-/*
- * Allocates a combined QTCB/fsf_req buffer for erp actions and fcp/SCSI
- * commands.
- * It also genrates fcp-nameserver request/response buffer and unsolicited
- * status read fsf_req buffers.
- *
- * locks: must only be called with zfcp_data.config_sema taken
- */
-static int
-zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
+static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
{
+ /* must only be called with zfcp_data.config_sema taken */
adapter->pool.fsf_req_erp =
- mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_ERP_NR,
- zfcp_data.fsf_req_qtcb_cache);
+ mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache);
if (!adapter->pool.fsf_req_erp)
return -ENOMEM;
adapter->pool.fsf_req_scsi =
- mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_SCSI_NR,
- zfcp_data.fsf_req_qtcb_cache);
+ mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache);
if (!adapter->pool.fsf_req_scsi)
return -ENOMEM;
adapter->pool.fsf_req_abort =
- mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_ABORT_NR,
- zfcp_data.fsf_req_qtcb_cache);
+ mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache);
if (!adapter->pool.fsf_req_abort)
return -ENOMEM;
adapter->pool.fsf_req_status_read =
- mempool_create_kmalloc_pool(ZFCP_POOL_STATUS_READ_NR,
+ mempool_create_kmalloc_pool(FSF_STATUS_READS_RECOM,
sizeof(struct zfcp_fsf_req));
if (!adapter->pool.fsf_req_status_read)
return -ENOMEM;
adapter->pool.data_status_read =
- mempool_create_slab_pool(ZFCP_POOL_STATUS_READ_NR,
+ mempool_create_slab_pool(FSF_STATUS_READS_RECOM,
zfcp_data.sr_buffer_cache);
if (!adapter->pool.data_status_read)
return -ENOMEM;
adapter->pool.data_gid_pn =
- mempool_create_slab_pool(ZFCP_POOL_DATA_GID_PN_NR,
- zfcp_data.gid_pn_cache);
+ mempool_create_slab_pool(1, zfcp_data.gid_pn_cache);
if (!adapter->pool.data_gid_pn)
return -ENOMEM;
return 0;
}
-/**
- * zfcp_free_low_mem_buffers - free memory pools of an adapter
- * @adapter: pointer to zfcp_adapter for which memory pools should be freed
- * locking: zfcp_data.config_sema must be held
- */
-static void
-zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
+static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
{
+ /* zfcp_data.config_sema must be held */
if (adapter->pool.fsf_req_erp)
mempool_destroy(adapter->pool.fsf_req_erp);
if (adapter->pool.fsf_req_scsi)
@@ -957,25 +407,48 @@ zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
mempool_destroy(adapter->pool.data_gid_pn);
}
-static void zfcp_dummy_release(struct device *dev)
+/**
+ * zfcp_status_read_refill - refill the long running status_read_requests
+ * @adapter: ptr to struct zfcp_adapter for which the buffers should be refilled
+ *
+ * Returns: 0 on success, 1 otherwise
+ *
+ * if there are 16 or more status_read requests missing an adapter_reopen
+ * is triggered
+ */
+int zfcp_status_read_refill(struct zfcp_adapter *adapter)
{
- return;
+ while (atomic_read(&adapter->stat_miss) > 0)
+ if (zfcp_fsf_status_read(adapter)) {
+ if (atomic_read(&adapter->stat_miss) >= 16) {
+ zfcp_erp_adapter_reopen(adapter, 0, 103, NULL);
+ return 1;
+ }
+ break;
+ } else
+ atomic_dec(&adapter->stat_miss);
+ return 0;
}
-/*
+static void _zfcp_status_read_scheduler(struct work_struct *work)
+{
+ zfcp_status_read_refill(container_of(work, struct zfcp_adapter,
+ stat_work));
+}
+
+/**
+ * zfcp_adapter_enqueue - enqueue a new adapter to the list
+ * @ccw_device: pointer to the struct cc_device
+ *
+ * Returns: 0 if a new adapter was successfully enqueued
+ * -ENOMEM if alloc failed
* Enqueues an adapter at the end of the adapter list in the driver data.
* All adapter internal structures are set up.
* Proc-fs entries are also created.
- *
- * returns: 0 if a new adapter was successfully enqueued
- * ZFCP_KNOWN if an adapter with this devno was already present
- * -ENOMEM if alloc failed
* locks: config_sema must be held to serialise changes to the adapter list
*/
-struct zfcp_adapter *
-zfcp_adapter_enqueue(struct ccw_device *ccw_device)
+int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
{
- int retval = 0;
struct zfcp_adapter *adapter;
/*
@@ -983,154 +456,103 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device)
* are protected by the config_sema, which must be held to get here
*/
- /* try to allocate new adapter data structure (zeroed) */
- adapter = kzalloc(sizeof (struct zfcp_adapter), GFP_KERNEL);
- if (!adapter) {
- ZFCP_LOG_INFO("error: allocation of base adapter "
- "structure failed\n");
- goto out;
- }
+ adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL);
+ if (!adapter)
+ return -ENOMEM;
ccw_device->handler = NULL;
-
- /* save ccw_device pointer */
adapter->ccw_device = ccw_device;
+ atomic_set(&adapter->refcount, 0);
- retval = zfcp_qdio_allocate_queues(adapter);
- if (retval)
- goto queues_alloc_failed;
-
- retval = zfcp_qdio_allocate(adapter);
- if (retval)
+ if (zfcp_qdio_allocate(adapter))
goto qdio_allocate_failed;
- retval = zfcp_allocate_low_mem_buffers(adapter);
- if (retval) {
- ZFCP_LOG_INFO("error: pool allocation failed\n");
+ if (zfcp_allocate_low_mem_buffers(adapter))
goto failed_low_mem_buffers;
- }
- /* initialise reference count stuff */
- atomic_set(&adapter->refcount, 0);
+ if (zfcp_reqlist_alloc(adapter))
+ goto failed_low_mem_buffers;
+
+ if (zfcp_adapter_debug_register(adapter))
+ goto debug_register_failed;
+
init_waitqueue_head(&adapter->remove_wq);
+ init_waitqueue_head(&adapter->erp_thread_wqh);
+ init_waitqueue_head(&adapter->erp_done_wqh);
- /* initialise list of ports */
INIT_LIST_HEAD(&adapter->port_list_head);
+ INIT_LIST_HEAD(&adapter->erp_ready_head);
+ INIT_LIST_HEAD(&adapter->erp_running_head);
- /* initialise list of ports to be removed */
- INIT_LIST_HEAD(&adapter->port_remove_lh);
-
- /* initialize list of fsf requests */
spin_lock_init(&adapter->req_list_lock);
- retval = zfcp_reqlist_alloc(adapter);
- if (retval) {
- ZFCP_LOG_INFO("request list initialization failed\n");
- goto failed_low_mem_buffers;
- }
-
- /* initialize debug locks */
spin_lock_init(&adapter->hba_dbf_lock);
spin_lock_init(&adapter->san_dbf_lock);
spin_lock_init(&adapter->scsi_dbf_lock);
spin_lock_init(&adapter->rec_dbf_lock);
-
- retval = zfcp_adapter_debug_register(adapter);
- if (retval)
- goto debug_register_failed;
-
- /* initialize error recovery stuff */
+ spin_lock_init(&adapter->req_q_lock);
rwlock_init(&adapter->erp_lock);
- sema_init(&adapter->erp_ready_sem, 0);
- INIT_LIST_HEAD(&adapter->erp_ready_head);
- INIT_LIST_HEAD(&adapter->erp_running_head);
-
- /* initialize abort lock */
rwlock_init(&adapter->abort_lock);
- /* initialise some erp stuff */
- init_waitqueue_head(&adapter->erp_thread_wqh);
- init_waitqueue_head(&adapter->erp_done_wqh);
+ sema_init(&adapter->erp_ready_sem, 0);
- /* initialize lock of associated request queue */
- rwlock_init(&adapter->request_queue.queue_lock);
+ INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
+ INIT_WORK(&adapter->scan_work, _zfcp_scan_ports_later);
/* mark adapter unusable as long as sysfs registration is not complete */
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
dev_set_drvdata(&ccw_device->dev, adapter);
- if (zfcp_sysfs_adapter_create_files(&ccw_device->dev))
+ if (sysfs_create_group(&ccw_device->dev.kobj,
+ &zfcp_sysfs_adapter_attrs))
goto sysfs_failed;
- adapter->generic_services.parent = &adapter->ccw_device->dev;
- adapter->generic_services.release = zfcp_dummy_release;
- snprintf(adapter->generic_services.bus_id, BUS_ID_SIZE,
- "generic_services");
-
- if (device_register(&adapter->generic_services))
- goto generic_services_failed;
-
- /* put allocated adapter at list tail */
write_lock_irq(&zfcp_data.config_lock);
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
list_add_tail(&adapter->list, &zfcp_data.adapter_list_head);
write_unlock_irq(&zfcp_data.config_lock);
- zfcp_data.adapters++;
+ zfcp_fc_nameserver_init(adapter);
- goto out;
+ return 0;
- generic_services_failed:
- zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev);
- sysfs_failed:
+sysfs_failed:
zfcp_adapter_debug_unregister(adapter);
- debug_register_failed:
+debug_register_failed:
dev_set_drvdata(&ccw_device->dev, NULL);
- zfcp_reqlist_free(adapter);
- failed_low_mem_buffers:
+ kfree(adapter->req_list);
+failed_low_mem_buffers:
zfcp_free_low_mem_buffers(adapter);
- if (qdio_free(ccw_device) != 0)
- ZFCP_LOG_NORMAL("bug: qdio_free for adapter %s failed\n",
- zfcp_get_busid_by_adapter(adapter));
- qdio_allocate_failed:
- zfcp_qdio_free_queues(adapter);
- queues_alloc_failed:
+qdio_allocate_failed:
+ zfcp_qdio_free(adapter);
kfree(adapter);
- adapter = NULL;
- out:
- return adapter;
+ return -ENOMEM;
}
-/*
- * returns: 0 - struct zfcp_adapter data structure successfully removed
- * !0 - struct zfcp_adapter data structure could not be removed
- * (e.g. still used)
+/**
+ * zfcp_adapter_dequeue - remove the adapter from the resource list
+ * @adapter: pointer to struct zfcp_adapter which should be removed
* locks: adapter list write lock is assumed to be held by caller
*/
-void
-zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
+void zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
{
int retval = 0;
unsigned long flags;
+ cancel_work_sync(&adapter->scan_work);
+ cancel_work_sync(&adapter->stat_work);
zfcp_adapter_scsi_unregister(adapter);
- device_unregister(&adapter->generic_services);
- zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev);
+ sysfs_remove_group(&adapter->ccw_device->dev.kobj,
+ &zfcp_sysfs_adapter_attrs);
dev_set_drvdata(&adapter->ccw_device->dev, NULL);
/* sanity check: no pending FSF requests */
spin_lock_irqsave(&adapter->req_list_lock, flags);
retval = zfcp_reqlist_isempty(adapter);
spin_unlock_irqrestore(&adapter->req_list_lock, flags);
- if (!retval) {
- ZFCP_LOG_NORMAL("bug: adapter %s (%p) still in use, "
- "%i requests outstanding\n",
- zfcp_get_busid_by_adapter(adapter), adapter,
- atomic_read(&adapter->reqs_active));
- retval = -EBUSY;
- goto out;
- }
+ if (!retval)
+ return;
zfcp_adapter_debug_unregister(adapter);
@@ -1139,29 +561,18 @@ zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
list_del(&adapter->list);
write_unlock_irq(&zfcp_data.config_lock);
- /* decrease number of adapters in list */
- zfcp_data.adapters--;
-
- ZFCP_LOG_TRACE("adapter %s (%p) removed from list, "
- "%i adapters still in list\n",
- zfcp_get_busid_by_adapter(adapter),
- adapter, zfcp_data.adapters);
-
- retval = qdio_free(adapter->ccw_device);
- if (retval)
- ZFCP_LOG_NORMAL("bug: qdio_free for adapter %s failed\n",
- zfcp_get_busid_by_adapter(adapter));
+ zfcp_qdio_free(adapter);
zfcp_free_low_mem_buffers(adapter);
- /* free memory of adapter data structure and queues */
- zfcp_qdio_free_queues(adapter);
- zfcp_reqlist_free(adapter);
+ kfree(adapter->req_list);
kfree(adapter->fc_stats);
kfree(adapter->stats_reset_data);
- ZFCP_LOG_TRACE("freeing adapter structure\n");
kfree(adapter);
- out:
- return;
+}
+
+static void zfcp_sysfs_port_release(struct device *dev)
+{
+ kfree(container_of(dev, struct zfcp_port, sysfs_device));
}
/**
@@ -1170,98 +581,58 @@ zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
* @wwpn: WWPN of the remote port to be enqueued
* @status: initial status for the port
* @d_id: destination id of the remote port to be enqueued
- * Return: pointer to enqueued port on success, NULL on error
+ * Returns: pointer to enqueued port on success, ERR_PTR on error
* Locks: config_sema must be held to serialize changes to the port list
*
* All port internal structures are set up and the sysfs entry is generated.
* d_id is used to enqueue ports with a well known address like the Directory
* Service for nameserver lookup.
*/
-struct zfcp_port *
-zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status,
- u32 d_id)
+struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
+ u32 status, u32 d_id)
{
struct zfcp_port *port;
- int check_wwpn;
+ int retval;
- check_wwpn = !(status & ZFCP_STATUS_PORT_NO_WWPN);
- /*
- * check that there is no port with this WWPN already in list
- */
- if (check_wwpn) {
- read_lock_irq(&zfcp_data.config_lock);
- port = zfcp_get_port_by_wwpn(adapter, wwpn);
- read_unlock_irq(&zfcp_data.config_lock);
- if (port)
- return NULL;
- }
-
- port = kzalloc(sizeof (struct zfcp_port), GFP_KERNEL);
+ port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL);
if (!port)
- return NULL;
+ return ERR_PTR(-ENOMEM);
- /* initialise reference count stuff */
- atomic_set(&port->refcount, 0);
init_waitqueue_head(&port->remove_wq);
-
INIT_LIST_HEAD(&port->unit_list_head);
- INIT_LIST_HEAD(&port->unit_remove_lh);
+ INIT_WORK(&port->gid_pn_work, zfcp_erp_port_strategy_open_lookup);
port->adapter = adapter;
+ port->d_id = d_id;
+ port->wwpn = wwpn;
- if (check_wwpn)
- port->wwpn = wwpn;
+ /* mark port unusable as long as sysfs registration is not complete */
+ atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status);
+ atomic_set(&port->refcount, 0);
- atomic_set_mask(status, &port->status);
+ dev_set_name(&port->sysfs_device, "0x%016llx", wwpn);
+ port->sysfs_device.parent = &adapter->ccw_device->dev;
- /* setup for sysfs registration */
- if (status & ZFCP_STATUS_PORT_WKA) {
- switch (d_id) {
- case ZFCP_DID_DIRECTORY_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "directory");
- break;
- case ZFCP_DID_MANAGEMENT_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "management");
- break;
- case ZFCP_DID_KEY_DISTRIBUTION_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "key_distribution");
- break;
- case ZFCP_DID_ALIAS_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "alias");
- break;
- case ZFCP_DID_TIME_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "time");
- break;
- default:
- kfree(port);
- return NULL;
- }
- port->d_id = d_id;
- port->sysfs_device.parent = &adapter->generic_services;
- } else {
- snprintf(port->sysfs_device.bus_id,
- BUS_ID_SIZE, "0x%016llx", wwpn);
- port->sysfs_device.parent = &adapter->ccw_device->dev;
- }
port->sysfs_device.release = zfcp_sysfs_port_release;
dev_set_drvdata(&port->sysfs_device, port);
- /* mark port unusable as long as sysfs registration is not complete */
- atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
+ read_lock_irq(&zfcp_data.config_lock);
+ if (!(status & ZFCP_STATUS_PORT_NO_WWPN))
+ if (zfcp_get_port_by_wwpn(adapter, wwpn)) {
+ read_unlock_irq(&zfcp_data.config_lock);
+ goto err_out_free;
+ }
+ read_unlock_irq(&zfcp_data.config_lock);
- if (device_register(&port->sysfs_device)) {
- kfree(port);
- return NULL;
- }
+ if (device_register(&port->sysfs_device))
+ goto err_out_free;
+
+ retval = sysfs_create_group(&port->sysfs_device.kobj,
+ &zfcp_sysfs_port_attrs);
- if (zfcp_sysfs_port_create_files(&port->sysfs_device, status)) {
+ if (retval) {
device_unregister(&port->sysfs_device);
- return NULL;
+ goto err_out;
}
zfcp_port_get(port);
@@ -1270,569 +641,74 @@ zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status,
list_add_tail(&port->list, &adapter->port_list_head);
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status);
- if (d_id == ZFCP_DID_DIRECTORY_SERVICE)
- if (!adapter->nameserver_port)
- adapter->nameserver_port = port;
- adapter->ports++;
+
write_unlock_irq(&zfcp_data.config_lock);
zfcp_adapter_get(adapter);
-
return port;
+
+err_out_free:
+ kfree(port);
+err_out:
+ return ERR_PTR(-EINVAL);
}
-void
-zfcp_port_dequeue(struct zfcp_port *port)
+/**
+ * zfcp_port_dequeue - dequeues a port from the port list of the adapter
+ * @port: pointer to struct zfcp_port which should be removed
+ */
+void zfcp_port_dequeue(struct zfcp_port *port)
{
- zfcp_port_wait(port);
+ wait_event(port->remove_wq, atomic_read(&port->refcount) == 0);
write_lock_irq(&zfcp_data.config_lock);
list_del(&port->list);
- port->adapter->ports--;
write_unlock_irq(&zfcp_data.config_lock);
if (port->rport)
fc_remote_port_delete(port->rport);
port->rport = NULL;
zfcp_adapter_put(port->adapter);
- zfcp_sysfs_port_remove_files(&port->sysfs_device,
- atomic_read(&port->status));
+ sysfs_remove_group(&port->sysfs_device.kobj, &zfcp_sysfs_port_attrs);
device_unregister(&port->sysfs_device);
}
-/* Enqueues a nameserver port */
-int
-zfcp_nameserver_enqueue(struct zfcp_adapter *adapter)
-{
- struct zfcp_port *port;
-
- port = zfcp_port_enqueue(adapter, 0, ZFCP_STATUS_PORT_WKA,
- ZFCP_DID_DIRECTORY_SERVICE);
- if (!port) {
- ZFCP_LOG_INFO("error: enqueue of nameserver port for "
- "adapter %s failed\n",
- zfcp_get_busid_by_adapter(adapter));
- return -ENXIO;
- }
- zfcp_port_put(port);
-
- return 0;
-}
-
-#undef ZFCP_LOG_AREA
-
-/****************************************************************/
-/******* Fibre Channel Standard related Functions **************/
-/****************************************************************/
-
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_FC
-
-static void zfcp_fsf_incoming_els_rscn(struct zfcp_fsf_req *fsf_req)
-{
- struct fsf_status_read_buffer *status_buffer = (void*)fsf_req->data;
- struct zfcp_adapter *adapter = fsf_req->adapter;
- struct fcp_rscn_head *fcp_rscn_head;
- struct fcp_rscn_element *fcp_rscn_element;
- struct zfcp_port *port;
- u16 i;
- u16 no_entries;
- u32 range_mask;
- unsigned long flags;
-
- fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload;
- fcp_rscn_element = (struct fcp_rscn_element *) status_buffer->payload;
-
- /* see FC-FS */
- no_entries = (fcp_rscn_head->payload_len / 4);
-
- for (i = 1; i < no_entries; i++) {
- /* skip head and start with 1st element */
- fcp_rscn_element++;
- switch (fcp_rscn_element->addr_format) {
- case ZFCP_PORT_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_PORT;
- break;
- case ZFCP_AREA_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_AREA;
- break;
- case ZFCP_DOMAIN_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_DOMAIN;
- break;
- case ZFCP_FABRIC_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_FABRIC;
- break;
- default:
- ZFCP_LOG_INFO("incoming RSCN with unknown "
- "address format\n");
- continue;
- }
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- list_for_each_entry(port, &adapter->port_list_head, list) {
- if (atomic_test_mask
- (ZFCP_STATUS_PORT_WKA, &port->status))
- continue;
- /* Do we know this port? If not skip it. */
- if (!atomic_test_mask
- (ZFCP_STATUS_PORT_DID_DID, &port->status)) {
- ZFCP_LOG_INFO("incoming RSCN, trying to open "
- "port 0x%016Lx\n", port->wwpn);
- zfcp_erp_port_reopen(port,
- ZFCP_STATUS_COMMON_ERP_FAILED,
- 82, fsf_req);
- continue;
- }
-
- /*
- * FIXME: race: d_id might being invalidated
- * (...DID_DID reset)
- */
- if ((port->d_id & range_mask)
- == (fcp_rscn_element->nport_did & range_mask)) {
- ZFCP_LOG_TRACE("reopen did 0x%08x\n",
- fcp_rscn_element->nport_did);
- /*
- * Unfortunately, an RSCN does not specify the
- * type of change a target underwent. We assume
- * that it makes sense to reopen the link.
- * FIXME: Shall we try to find out more about
- * the target and link state before closing it?
- * How to accomplish this? (nameserver?)
- * Where would such code be put in?
- * (inside or outside erp)
- */
- ZFCP_LOG_INFO("incoming RSCN, trying to open "
- "port 0x%016Lx\n", port->wwpn);
- zfcp_test_link(port);
- }
- }
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
- }
-}
-
-static void zfcp_fsf_incoming_els_plogi(struct zfcp_fsf_req *fsf_req)
-{
- struct fsf_status_read_buffer *status_buffer = (void*)fsf_req->data;
- struct zfcp_adapter *adapter = fsf_req->adapter;
- struct fsf_plogi *els_plogi;
- struct zfcp_port *port;
- unsigned long flags;
-
- els_plogi = (struct fsf_plogi *) status_buffer->payload;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- list_for_each_entry(port, &adapter->port_list_head, list) {
- if (port->wwpn == (*(wwn_t *) &els_plogi->serv_param.wwpn))
- break;
- }
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
- if (!port || (port->wwpn != (*(wwn_t *) &els_plogi->serv_param.wwpn))) {
- ZFCP_LOG_DEBUG("ignored incoming PLOGI for nonexisting port "
- "with d_id 0x%06x on adapter %s\n",
- status_buffer->d_id,
- zfcp_get_busid_by_adapter(adapter));
- } else {
- zfcp_erp_port_forced_reopen(port, 0, 83, fsf_req);
- }
-}
-
-static void zfcp_fsf_incoming_els_logo(struct zfcp_fsf_req *fsf_req)
-{
- struct fsf_status_read_buffer *status_buffer = (void*)fsf_req->data;
- struct zfcp_adapter *adapter = fsf_req->adapter;
- struct fcp_logo *els_logo = (struct fcp_logo *) status_buffer->payload;
- struct zfcp_port *port;
- unsigned long flags;
-
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- list_for_each_entry(port, &adapter->port_list_head, list) {
- if (port->wwpn == els_logo->nport_wwpn)
- break;
- }
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
- if (!port || (port->wwpn != els_logo->nport_wwpn)) {
- ZFCP_LOG_DEBUG("ignored incoming LOGO for nonexisting port "
- "with d_id 0x%06x on adapter %s\n",
- status_buffer->d_id,
- zfcp_get_busid_by_adapter(adapter));
- } else {
- zfcp_erp_port_forced_reopen(port, 0, 84, fsf_req);
- }
-}
-
-static void
-zfcp_fsf_incoming_els_unknown(struct zfcp_adapter *adapter,
- struct fsf_status_read_buffer *status_buffer)
-{
- ZFCP_LOG_NORMAL("warning: unknown incoming ELS 0x%08x "
- "for adapter %s\n", *(u32 *) (status_buffer->payload),
- zfcp_get_busid_by_adapter(adapter));
-
-}
-
-void
-zfcp_fsf_incoming_els(struct zfcp_fsf_req *fsf_req)
-{
- struct fsf_status_read_buffer *status_buffer;
- u32 els_type;
- struct zfcp_adapter *adapter;
-
- status_buffer = (struct fsf_status_read_buffer *) fsf_req->data;
- els_type = *(u32 *) (status_buffer->payload);
- adapter = fsf_req->adapter;
-
- zfcp_san_dbf_event_incoming_els(fsf_req);
- if (els_type == LS_PLOGI)
- zfcp_fsf_incoming_els_plogi(fsf_req);
- else if (els_type == LS_LOGO)
- zfcp_fsf_incoming_els_logo(fsf_req);
- else if ((els_type & 0xffff0000) == LS_RSCN)
- /* we are only concerned with the command, not the length */
- zfcp_fsf_incoming_els_rscn(fsf_req);
- else
- zfcp_fsf_incoming_els_unknown(adapter, status_buffer);
-}
-
-
-/**
- * zfcp_gid_pn_buffers_alloc - allocate buffers for GID_PN nameserver request
- * @gid_pn: pointer to return pointer to struct zfcp_gid_pn_data
- * @pool: pointer to mempool_t if non-null memory pool is used for allocation
- */
-static int
-zfcp_gid_pn_buffers_alloc(struct zfcp_gid_pn_data **gid_pn, mempool_t *pool)
-{
- struct zfcp_gid_pn_data *data;
-
- if (pool != NULL) {
- data = mempool_alloc(pool, GFP_ATOMIC);
- if (likely(data != NULL)) {
- data->ct.pool = pool;
- }
- } else {
- data = kmem_cache_alloc(zfcp_data.gid_pn_cache, GFP_ATOMIC);
- }
-
- if (NULL == data)
- return -ENOMEM;
-
- memset(data, 0, sizeof(*data));
- sg_init_table(&data->req , 1);
- sg_init_table(&data->resp , 1);
- data->ct.req = &data->req;
- data->ct.resp = &data->resp;
- data->ct.req_count = data->ct.resp_count = 1;
- zfcp_address_to_sg(&data->ct_iu_req, &data->req, sizeof(struct ct_iu_gid_pn_req));
- zfcp_address_to_sg(&data->ct_iu_resp, &data->resp, sizeof(struct ct_iu_gid_pn_resp));
-
- *gid_pn = data;
- return 0;
-}
-
-/**
- * zfcp_gid_pn_buffers_free - free buffers for GID_PN nameserver request
- * @gid_pn: pointer to struct zfcp_gid_pn_data which has to be freed
- */
-static void zfcp_gid_pn_buffers_free(struct zfcp_gid_pn_data *gid_pn)
-{
- if (gid_pn->ct.pool)
- mempool_free(gid_pn, gid_pn->ct.pool);
- else
- kmem_cache_free(zfcp_data.gid_pn_cache, gid_pn);
-}
-
/**
- * zfcp_ns_gid_pn_request - initiate GID_PN nameserver request
- * @erp_action: pointer to zfcp_erp_action where GID_PN request is needed
+ * zfcp_sg_free_table - free memory used by scatterlists
+ * @sg: pointer to scatterlist
+ * @count: number of scatterlist which are to be free'ed
+ * the scatterlist are expected to reference pages always
*/
-int
-zfcp_ns_gid_pn_request(struct zfcp_erp_action *erp_action)
+void zfcp_sg_free_table(struct scatterlist *sg, int count)
{
- int ret;
- struct ct_iu_gid_pn_req *ct_iu_req;
- struct zfcp_gid_pn_data *gid_pn;
- struct zfcp_adapter *adapter = erp_action->adapter;
-
- ret = zfcp_gid_pn_buffers_alloc(&gid_pn, adapter->pool.data_gid_pn);
- if (ret < 0) {
- ZFCP_LOG_INFO("error: buffer allocation for gid_pn nameserver "
- "request failed for adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- goto out;
- }
-
- /* setup nameserver request */
- ct_iu_req = zfcp_sg_to_address(gid_pn->ct.req);
- ct_iu_req->header.revision = ZFCP_CT_REVISION;
- ct_iu_req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
- ct_iu_req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
- ct_iu_req->header.options = ZFCP_CT_SYNCHRONOUS;
- ct_iu_req->header.cmd_rsp_code = ZFCP_CT_GID_PN;
- ct_iu_req->header.max_res_size = ZFCP_CT_MAX_SIZE;
- ct_iu_req->wwpn = erp_action->port->wwpn;
-
- /* setup parameters for send generic command */
- gid_pn->ct.port = adapter->nameserver_port;
- gid_pn->ct.handler = zfcp_ns_gid_pn_handler;
- gid_pn->ct.handler_data = (unsigned long) gid_pn;
- gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
- gid_pn->port = erp_action->port;
-
- ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp,
- erp_action);
- if (ret) {
- ZFCP_LOG_INFO("error: initiation of gid_pn nameserver request "
- "failed for adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
-
- zfcp_gid_pn_buffers_free(gid_pn);
- }
-
- out:
- return ret;
-}
-
-/**
- * zfcp_ns_gid_pn_handler - handler for GID_PN nameserver request
- * @data: unsigned long, contains pointer to struct zfcp_gid_pn_data
- */
-static void zfcp_ns_gid_pn_handler(unsigned long data)
-{
- struct zfcp_port *port;
- struct zfcp_send_ct *ct;
- struct ct_iu_gid_pn_req *ct_iu_req;
- struct ct_iu_gid_pn_resp *ct_iu_resp;
- struct zfcp_gid_pn_data *gid_pn;
-
-
- gid_pn = (struct zfcp_gid_pn_data *) data;
- port = gid_pn->port;
- ct = &gid_pn->ct;
- ct_iu_req = zfcp_sg_to_address(ct->req);
- ct_iu_resp = zfcp_sg_to_address(ct->resp);
-
- if (ct->status != 0)
- goto failed;
-
- if (zfcp_check_ct_response(&ct_iu_resp->header)) {
- /* FIXME: do we need some specific erp entry points */
- atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
- goto failed;
- }
- /* paranoia */
- if (ct_iu_req->wwpn != port->wwpn) {
- ZFCP_LOG_NORMAL("bug: wwpn 0x%016Lx returned by nameserver "
- "lookup does not match expected wwpn 0x%016Lx "
- "for adapter %s\n", ct_iu_req->wwpn, port->wwpn,
- zfcp_get_busid_by_port(port));
- goto mismatch;
- }
-
- /* looks like a valid d_id */
- port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
- atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
- ZFCP_LOG_DEBUG("adapter %s: wwpn=0x%016Lx ---> d_id=0x%06x\n",
- zfcp_get_busid_by_port(port), port->wwpn, port->d_id);
- goto out;
-
- mismatch:
- ZFCP_LOG_DEBUG("CT IUs do not match:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_req,
- sizeof(struct ct_iu_gid_pn_req));
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_resp,
- sizeof(struct ct_iu_gid_pn_resp));
-
- failed:
- ZFCP_LOG_NORMAL("warning: failed gid_pn nameserver request for wwpn "
- "0x%016Lx for adapter %s\n",
- port->wwpn, zfcp_get_busid_by_port(port));
- out:
- zfcp_gid_pn_buffers_free(gid_pn);
- return;
-}
-
-/* reject CT_IU reason codes acc. to FC-GS-4 */
-static const struct zfcp_rc_entry zfcp_ct_rc[] = {
- {0x01, "invalid command code"},
- {0x02, "invalid version level"},
- {0x03, "logical error"},
- {0x04, "invalid CT_IU size"},
- {0x05, "logical busy"},
- {0x07, "protocol error"},
- {0x09, "unable to perform command request"},
- {0x0b, "command not supported"},
- {0x0d, "server not available"},
- {0x0e, "session could not be established"},
- {0xff, "vendor specific error"},
- {0, NULL},
-};
-
-/* LS_RJT reason codes acc. to FC-FS */
-static const struct zfcp_rc_entry zfcp_ls_rjt_rc[] = {
- {0x01, "invalid LS_Command code"},
- {0x03, "logical error"},
- {0x05, "logical busy"},
- {0x07, "protocol error"},
- {0x09, "unable to perform command request"},
- {0x0b, "command not supported"},
- {0x0e, "command already in progress"},
- {0xff, "vendor specific error"},
- {0, NULL},
-};
-
-/* reject reason codes according to FC-PH/FC-FS */
-static const struct zfcp_rc_entry zfcp_p_rjt_rc[] = {
- {0x01, "invalid D_ID"},
- {0x02, "invalid S_ID"},
- {0x03, "Nx_Port not available, temporary"},
- {0x04, "Nx_Port not available, permament"},
- {0x05, "class not supported"},
- {0x06, "delimiter usage error"},
- {0x07, "TYPE not supported"},
- {0x08, "invalid Link_Control"},
- {0x09, "invalid R_CTL field"},
- {0x0a, "invalid F_CTL field"},
- {0x0b, "invalid OX_ID"},
- {0x0c, "invalid RX_ID"},
- {0x0d, "invalid SEQ_ID"},
- {0x0e, "invalid DF_CTL"},
- {0x0f, "invalid SEQ_CNT"},
- {0x10, "invalid parameter field"},
- {0x11, "exchange error"},
- {0x12, "protocol error"},
- {0x13, "incorrect length"},
- {0x14, "unsupported ACK"},
- {0x15, "class of service not supported by entity at FFFFFE"},
- {0x16, "login required"},
- {0x17, "excessive sequences attempted"},
- {0x18, "unable to establish exchange"},
- {0x1a, "fabric path not available"},
- {0x1b, "invalid VC_ID (class 4)"},
- {0x1c, "invalid CS_CTL field"},
- {0x1d, "insufficient resources for VC (class 4)"},
- {0x1f, "invalid class of service"},
- {0x20, "preemption request rejected"},
- {0x21, "preemption not enabled"},
- {0x22, "multicast error"},
- {0x23, "multicast error terminate"},
- {0x24, "process login required"},
- {0xff, "vendor specific reject"},
- {0, NULL},
-};
-
-/**
- * zfcp_rc_description - return description for given reaon code
- * @code: reason code
- * @rc_table: table of reason codes and descriptions
- */
-static const char *
-zfcp_rc_description(u8 code, const struct zfcp_rc_entry *rc_table)
-{
- const char *descr = "unknown reason code";
+ int i;
- do {
- if (code == rc_table->code) {
- descr = rc_table->description;
+ for (i = 0; i < count; i++, sg++)
+ if (sg)
+ free_page((unsigned long) sg_virt(sg));
+ else
break;
- }
- rc_table++;
- } while (rc_table->code && rc_table->description);
-
- return descr;
}
/**
- * zfcp_check_ct_response - evaluate reason code for CT_IU
- * @rjt: response payload to an CT_IU request
- * Return: 0 for accept CT_IU, 1 for reject CT_IU or invlid response code
+ * zfcp_sg_setup_table - init scatterlist and allocate, assign buffers
+ * @sg: pointer to struct scatterlist
+ * @count: number of scatterlists which should be assigned with buffers
+ * of size page
+ *
+ * Returns: 0 on success, -ENOMEM otherwise
*/
-int
-zfcp_check_ct_response(struct ct_hdr *rjt)
+int zfcp_sg_setup_table(struct scatterlist *sg, int count)
{
- if (rjt->cmd_rsp_code == ZFCP_CT_ACCEPT)
- return 0;
+ void *addr;
+ int i;
- if (rjt->cmd_rsp_code != ZFCP_CT_REJECT) {
- ZFCP_LOG_NORMAL("error: invalid Generic Service command/"
- "response code (0x%04hx)\n",
- rjt->cmd_rsp_code);
- return 1;
+ sg_init_table(sg, count);
+ for (i = 0; i < count; i++, sg++) {
+ addr = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!addr) {
+ zfcp_sg_free_table(sg, i);
+ return -ENOMEM;
+ }
+ sg_set_buf(sg, addr, PAGE_SIZE);
}
-
- ZFCP_LOG_INFO("Generic Service command rejected\n");
- ZFCP_LOG_INFO("%s (0x%02x, 0x%02x, 0x%02x)\n",
- zfcp_rc_description(rjt->reason_code, zfcp_ct_rc),
- (u32) rjt->reason_code, (u32) rjt->reason_code_expl,
- (u32) rjt->vendor_unique);
-
- return 1;
-}
-
-/**
- * zfcp_print_els_rjt - print reject parameter and description for ELS reject
- * @rjt_par: reject parameter acc. to FC-PH/FC-FS
- * @rc_table: table of reason codes and descriptions
- */
-static void
-zfcp_print_els_rjt(struct zfcp_ls_rjt_par *rjt_par,
- const struct zfcp_rc_entry *rc_table)
-{
- ZFCP_LOG_INFO("%s (%02x %02x %02x %02x)\n",
- zfcp_rc_description(rjt_par->reason_code, rc_table),
- (u32) rjt_par->action, (u32) rjt_par->reason_code,
- (u32) rjt_par->reason_expl, (u32) rjt_par->vendor_unique);
-}
-
-/**
- * zfcp_fsf_handle_els_rjt - evaluate status qualifier/reason code on ELS reject
- * @sq: status qualifier word
- * @rjt_par: reject parameter as described in FC-PH and FC-FS
- * Return: -EROMTEIO for LS_RJT, -EREMCHG for invalid D_ID, -EIO else
- */
-int
-zfcp_handle_els_rjt(u32 sq, struct zfcp_ls_rjt_par *rjt_par)
-{
- int ret = -EIO;
-
- if (sq == FSF_IOSTAT_NPORT_RJT) {
- ZFCP_LOG_INFO("ELS rejected (P_RJT)\n");
- zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc);
- /* invalid d_id */
- if (rjt_par->reason_code == 0x01)
- ret = -EREMCHG;
- } else if (sq == FSF_IOSTAT_FABRIC_RJT) {
- ZFCP_LOG_INFO("ELS rejected (F_RJT)\n");
- zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc);
- /* invalid d_id */
- if (rjt_par->reason_code == 0x01)
- ret = -EREMCHG;
- } else if (sq == FSF_IOSTAT_LS_RJT) {
- ZFCP_LOG_INFO("ELS rejected (LS_RJT)\n");
- zfcp_print_els_rjt(rjt_par, zfcp_ls_rjt_rc);
- ret = -EREMOTEIO;
- } else
- ZFCP_LOG_INFO("unexpected SQ: 0x%02x\n", sq);
-
- return ret;
-}
-
-/**
- * zfcp_plogi_evaluate - evaluate PLOGI playload and copy important fields
- * into zfcp_port structure
- * @port: zfcp_port structure
- * @plogi: plogi payload
- */
-void
-zfcp_plogi_evaluate(struct zfcp_port *port, struct fsf_plogi *plogi)
-{
- port->maxframe_size = plogi->serv_param.common_serv_param[7] |
- ((plogi->serv_param.common_serv_param[6] & 0x0F) << 8);
- if (plogi->serv_param.class1_serv_param[0] & 0x80)
- port->supported_classes |= FC_COS_CLASS1;
- if (plogi->serv_param.class2_serv_param[0] & 0x80)
- port->supported_classes |= FC_COS_CLASS2;
- if (plogi->serv_param.class3_serv_param[0] & 0x80)
- port->supported_classes |= FC_COS_CLASS3;
- if (plogi->serv_param.class4_serv_param[0] & 0x80)
- port->supported_classes |= FC_COS_CLASS4;
+ return 0;
}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index 66d3b88844b..b04038c7478 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -1,64 +1,13 @@
/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
*
- * (C) Copyright IBM Corp. 2002, 2006
+ * Registration and callback for the s390 common I/O layer.
*
- * 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 Corporation 2002, 2008
*/
#include "zfcp_ext.h"
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
-
-static int zfcp_ccw_probe(struct ccw_device *);
-static void zfcp_ccw_remove(struct ccw_device *);
-static int zfcp_ccw_set_online(struct ccw_device *);
-static int zfcp_ccw_set_offline(struct ccw_device *);
-static int zfcp_ccw_notify(struct ccw_device *, int);
-static void zfcp_ccw_shutdown(struct ccw_device *);
-
-static struct ccw_device_id zfcp_ccw_device_id[] = {
- {CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE,
- ZFCP_CONTROL_UNIT_MODEL,
- ZFCP_DEVICE_TYPE,
- ZFCP_DEVICE_MODEL)},
- {CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE,
- ZFCP_CONTROL_UNIT_MODEL,
- ZFCP_DEVICE_TYPE,
- ZFCP_DEVICE_MODEL_PRIV)},
- {},
-};
-
-static struct ccw_driver zfcp_ccw_driver = {
- .owner = THIS_MODULE,
- .name = ZFCP_NAME,
- .ids = zfcp_ccw_device_id,
- .probe = zfcp_ccw_probe,
- .remove = zfcp_ccw_remove,
- .set_online = zfcp_ccw_set_online,
- .set_offline = zfcp_ccw_set_offline,
- .notify = zfcp_ccw_notify,
- .shutdown = zfcp_ccw_shutdown,
- .driver = {
- .groups = zfcp_driver_attr_groups,
- },
-};
-
-MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
-
/**
* zfcp_ccw_probe - probe function of zfcp driver
* @ccw_device: pointer to belonging ccw device
@@ -69,19 +18,17 @@ MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
* In addition the nameserver port will be added to the ports of the adapter
* and its sysfs representation will be created too.
*/
-static int
-zfcp_ccw_probe(struct ccw_device *ccw_device)
+static int zfcp_ccw_probe(struct ccw_device *ccw_device)
{
- struct zfcp_adapter *adapter;
int retval = 0;
down(&zfcp_data.config_sema);
- adapter = zfcp_adapter_enqueue(ccw_device);
- if (!adapter)
+ if (zfcp_adapter_enqueue(ccw_device)) {
+ dev_err(&ccw_device->dev,
+ "Setting up data structures for the "
+ "FCP adapter failed\n");
retval = -EINVAL;
- else
- ZFCP_LOG_DEBUG("Probed adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
+ }
up(&zfcp_data.config_sema);
return retval;
}
@@ -95,42 +42,41 @@ zfcp_ccw_probe(struct ccw_device *ccw_device)
* ports that belong to this adapter. And in addition all resources of this
* adapter will be freed too.
*/
-static void
-zfcp_ccw_remove(struct ccw_device *ccw_device)
+static void zfcp_ccw_remove(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
struct zfcp_port *port, *p;
struct zfcp_unit *unit, *u;
+ LIST_HEAD(unit_remove_lh);
+ LIST_HEAD(port_remove_lh);
ccw_device_set_offline(ccw_device);
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
- ZFCP_LOG_DEBUG("Removing adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
write_lock_irq(&zfcp_data.config_lock);
list_for_each_entry_safe(port, p, &adapter->port_list_head, list) {
list_for_each_entry_safe(unit, u, &port->unit_list_head, list) {
- list_move(&unit->list, &port->unit_remove_lh);
+ list_move(&unit->list, &unit_remove_lh);
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE,
&unit->status);
}
- list_move(&port->list, &adapter->port_remove_lh);
+ list_move(&port->list, &port_remove_lh);
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
}
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
write_unlock_irq(&zfcp_data.config_lock);
- list_for_each_entry_safe(port, p, &adapter->port_remove_lh, list) {
- list_for_each_entry_safe(unit, u, &port->unit_remove_lh, list) {
- if (atomic_test_mask(ZFCP_STATUS_UNIT_REGISTERED,
- &unit->status))
+ list_for_each_entry_safe(port, p, &port_remove_lh, list) {
+ list_for_each_entry_safe(unit, u, &unit_remove_lh, list) {
+ if (atomic_read(&unit->status) &
+ ZFCP_STATUS_UNIT_REGISTERED)
scsi_remove_device(unit->device);
zfcp_unit_dequeue(unit);
}
zfcp_port_dequeue(port);
}
- zfcp_adapter_wait(adapter);
+ wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0);
zfcp_adapter_dequeue(adapter);
up(&zfcp_data.config_sema);
@@ -145,8 +91,7 @@ zfcp_ccw_remove(struct ccw_device *ccw_device)
* registered with the SCSI stack, that the QDIO queues will be set up
* and that the adapter will be opened (asynchronously).
*/
-static int
-zfcp_ccw_set_online(struct ccw_device *ccw_device)
+static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
int retval;
@@ -155,12 +100,8 @@ zfcp_ccw_set_online(struct ccw_device *ccw_device)
adapter = dev_get_drvdata(&ccw_device->dev);
retval = zfcp_erp_thread_setup(adapter);
- if (retval) {
- ZFCP_LOG_INFO("error: start of error recovery thread for "
- "adapter %s failed\n",
- zfcp_get_busid_by_adapter(adapter));
+ if (retval)
goto out;
- }
retval = zfcp_adapter_scsi_register(adapter);
if (retval)
@@ -191,8 +132,7 @@ zfcp_ccw_set_online(struct ccw_device *ccw_device)
* This function gets called by the common i/o layer and sets an adapter
* into state offline.
*/
-static int
-zfcp_ccw_set_offline(struct ccw_device *ccw_device)
+static int zfcp_ccw_set_offline(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
@@ -206,34 +146,31 @@ zfcp_ccw_set_offline(struct ccw_device *ccw_device)
}
/**
- * zfcp_ccw_notify
+ * zfcp_ccw_notify - ccw notify function
* @ccw_device: pointer to belonging ccw device
* @event: indicates if adapter was detached or attached
*
* This function gets called by the common i/o layer if an adapter has gone
* or reappeared.
*/
-static int
-zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
+static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
{
- struct zfcp_adapter *adapter;
+ struct zfcp_adapter *adapter = dev_get_drvdata(&ccw_device->dev);
- down(&zfcp_data.config_sema);
- adapter = dev_get_drvdata(&ccw_device->dev);
switch (event) {
case CIO_GONE:
- ZFCP_LOG_NORMAL("adapter %s: device gone\n",
- zfcp_get_busid_by_adapter(adapter));
+ dev_warn(&adapter->ccw_device->dev,
+ "The FCP device has been detached\n");
zfcp_erp_adapter_shutdown(adapter, 0, 87, NULL);
break;
case CIO_NO_PATH:
- ZFCP_LOG_NORMAL("adapter %s: no path\n",
- zfcp_get_busid_by_adapter(adapter));
+ dev_warn(&adapter->ccw_device->dev,
+ "The CHPID for the FCP device is offline\n");
zfcp_erp_adapter_shutdown(adapter, 0, 88, NULL);
break;
case CIO_OPER:
- ZFCP_LOG_NORMAL("adapter %s: operational again\n",
- zfcp_get_busid_by_adapter(adapter));
+ dev_info(&adapter->ccw_device->dev,
+ "The FCP device is operational again\n");
zfcp_erp_modify_adapter_status(adapter, 11, NULL,
ZFCP_STATUS_COMMON_RUNNING,
ZFCP_SET);
@@ -241,38 +178,68 @@ zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
89, NULL);
break;
}
+ return 1;
+}
+
+/**
+ * zfcp_ccw_shutdown - handle shutdown from cio
+ * @cdev: device for adapter to shutdown.
+ */
+static void zfcp_ccw_shutdown(struct ccw_device *cdev)
+{
+ struct zfcp_adapter *adapter;
+
+ down(&zfcp_data.config_sema);
+ adapter = dev_get_drvdata(&cdev->dev);
+ zfcp_erp_adapter_shutdown(adapter, 0, 90, NULL);
zfcp_erp_wait(adapter);
up(&zfcp_data.config_sema);
- return 1;
}
+static struct ccw_device_id zfcp_ccw_device_id[] = {
+ { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) },
+ { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x4) }, /* priv. */
+ {},
+};
+
+MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
+
+static struct ccw_driver zfcp_ccw_driver = {
+ .owner = THIS_MODULE,
+ .name = "zfcp",
+ .ids = zfcp_ccw_device_id,
+ .probe = zfcp_ccw_probe,
+ .remove = zfcp_ccw_remove,
+ .set_online = zfcp_ccw_set_online,
+ .set_offline = zfcp_ccw_set_offline,
+ .notify = zfcp_ccw_notify,
+ .shutdown = zfcp_ccw_shutdown,
+};
+
/**
* zfcp_ccw_register - ccw register function
*
* Registers the driver at the common i/o layer. This function will be called
* at module load time/system start.
*/
-int __init
-zfcp_ccw_register(void)
+int __init zfcp_ccw_register(void)
{
return ccw_driver_register(&zfcp_ccw_driver);
}
/**
- * zfcp_ccw_shutdown - gets called on reboot/shutdown
- *
- * Makes sure that QDIO queues are down when the system gets stopped.
+ * zfcp_get_adapter_by_busid - find zfcp_adapter struct
+ * @busid: bus id string of zfcp adapter to find
*/
-static void
-zfcp_ccw_shutdown(struct ccw_device *cdev)
+struct zfcp_adapter *zfcp_get_adapter_by_busid(char *busid)
{
- struct zfcp_adapter *adapter;
+ struct ccw_device *ccw_device;
+ struct zfcp_adapter *adapter = NULL;
- down(&zfcp_data.config_sema);
- adapter = dev_get_drvdata(&cdev->dev);
- zfcp_erp_adapter_shutdown(adapter, 0, 90, NULL);
- zfcp_erp_wait(adapter);
- up(&zfcp_data.config_sema);
+ ccw_device = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
+ if (ccw_device) {
+ adapter = dev_get_drvdata(&ccw_device->dev);
+ put_device(&ccw_device->dev);
+ }
+ return adapter;
}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c
new file mode 100644
index 00000000000..ec2abceca6d
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_cfdc.c
@@ -0,0 +1,259 @@
+/*
+ * zfcp device driver
+ *
+ * Userspace interface for accessing the
+ * Access Control Lists / Control File Data Channel
+ *
+ * Copyright IBM Corporation 2008
+ */
+
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <asm/ccwdev.h>
+#include "zfcp_def.h"
+#include "zfcp_ext.h"
+#include "zfcp_fsf.h"
+
+#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL 0x00010001
+#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE 0x00010101
+#define ZFCP_CFDC_CMND_FULL_ACCESS 0x00000201
+#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS 0x00000401
+#define ZFCP_CFDC_CMND_UPLOAD 0x00010002
+
+#define ZFCP_CFDC_DOWNLOAD 0x00000001
+#define ZFCP_CFDC_UPLOAD 0x00000002
+#define ZFCP_CFDC_WITH_CONTROL_FILE 0x00010000
+
+#define ZFCP_CFDC_IOC_MAGIC 0xDD
+#define ZFCP_CFDC_IOC \
+ _IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_data)
+
+/**
+ * struct zfcp_cfdc_data - data for ioctl cfdc interface
+ * @signature: request signature
+ * @devno: FCP adapter device number
+ * @command: command code
+ * @fsf_status: returns status of FSF command to userspace
+ * @fsf_status_qual: returned to userspace
+ * @payloads: access conflicts list
+ * @control_file: access control table
+ */
+struct zfcp_cfdc_data {
+ u32 signature;
+ u32 devno;
+ u32 command;
+ u32 fsf_status;
+ u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
+ u8 payloads[256];
+ u8 control_file[0];
+};
+
+static int zfcp_cfdc_copy_from_user(struct scatterlist *sg,
+ void __user *user_buffer)
+{
+ unsigned int length;
+ unsigned int size = ZFCP_CFDC_MAX_SIZE;
+
+ while (size) {
+ length = min((unsigned int)size, sg->length);
+ if (copy_from_user(sg_virt(sg++), user_buffer, length))
+ return -EFAULT;
+ user_buffer += length;
+ size -= length;
+ }
+ return 0;
+}
+
+static int zfcp_cfdc_copy_to_user(void __user *user_buffer,
+ struct scatterlist *sg)
+{
+ unsigned int length;
+ unsigned int size = ZFCP_CFDC_MAX_SIZE;
+
+ while (size) {
+ length = min((unsigned int) size, sg->length);
+ if (copy_to_user(user_buffer, sg_virt(sg++), length))
+ return -EFAULT;
+ user_buffer += length;
+ size -= length;
+ }
+ return 0;
+}
+
+static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno)
+{
+ struct zfcp_adapter *adapter = NULL, *cur_adapter;
+ struct ccw_dev_id dev_id;
+
+ read_lock_irq(&zfcp_data.config_lock);
+ list_for_each_entry(cur_adapter, &zfcp_data.adapter_list_head, list) {
+ ccw_device_get_id(cur_adapter->ccw_device, &dev_id);
+ if (dev_id.devno == devno) {
+ adapter = cur_adapter;
+ zfcp_adapter_get(adapter);
+ break;
+ }
+ }
+ read_unlock_irq(&zfcp_data.config_lock);
+ return adapter;
+}
+
+static int zfcp_cfdc_set_fsf(struct zfcp_fsf_cfdc *fsf_cfdc, int command)
+{
+ switch (command) {
+ case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL:
+ fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+ fsf_cfdc->option = FSF_CFDC_OPTION_NORMAL_MODE;
+ break;
+ case ZFCP_CFDC_CMND_DOWNLOAD_FORCE:
+ fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+ fsf_cfdc->option = FSF_CFDC_OPTION_FORCE;
+ break;
+ case ZFCP_CFDC_CMND_FULL_ACCESS:
+ fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+ fsf_cfdc->option = FSF_CFDC_OPTION_FULL_ACCESS;
+ break;
+ case ZFCP_CFDC_CMND_RESTRICTED_ACCESS:
+ fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+ fsf_cfdc->option = FSF_CFDC_OPTION_RESTRICTED_ACCESS;
+ break;
+ case ZFCP_CFDC_CMND_UPLOAD:
+ fsf_cfdc->command = FSF_QTCB_UPLOAD_CONTROL_FILE;
+ fsf_cfdc->option = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int zfcp_cfdc_sg_setup(int command, struct scatterlist *sg,
+ u8 __user *control_file)
+{
+ int retval;
+ retval = zfcp_sg_setup_table(sg, ZFCP_CFDC_PAGES);
+ if (retval)
+ return retval;
+
+ sg[ZFCP_CFDC_PAGES - 1].length = ZFCP_CFDC_MAX_SIZE % PAGE_SIZE;
+
+ if (command & ZFCP_CFDC_WITH_CONTROL_FILE &&
+ command & ZFCP_CFDC_DOWNLOAD) {
+ retval = zfcp_cfdc_copy_from_user(sg, control_file);
+ if (retval) {
+ zfcp_sg_free_table(sg, ZFCP_CFDC_PAGES);
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+static void zfcp_cfdc_req_to_sense(struct zfcp_cfdc_data *data,
+ struct zfcp_fsf_req *req)
+{
+ data->fsf_status = req->qtcb->header.fsf_status;
+ memcpy(&data->fsf_status_qual, &req->qtcb->header.fsf_status_qual,
+ sizeof(union fsf_status_qual));
+ memcpy(&data->payloads, &req->qtcb->bottom.support.els,
+ sizeof(req->qtcb->bottom.support.els));
+}
+
+static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
+ unsigned long buffer)
+{
+ struct zfcp_cfdc_data *data;
+ struct zfcp_cfdc_data __user *data_user;
+ struct zfcp_adapter *adapter;
+ struct zfcp_fsf_req *req;
+ struct zfcp_fsf_cfdc *fsf_cfdc;
+ int retval;
+
+ if (command != ZFCP_CFDC_IOC)
+ return -ENOTTY;
+
+ data_user = (void __user *) buffer;
+ if (!data_user)
+ return -EINVAL;
+
+ fsf_cfdc = kmalloc(sizeof(struct zfcp_fsf_cfdc), GFP_KERNEL);
+ if (!fsf_cfdc)
+ return -ENOMEM;
+
+ data = kmalloc(sizeof(struct zfcp_cfdc_data), GFP_KERNEL);
+ if (!data) {
+ retval = -ENOMEM;
+ goto no_mem_sense;
+ }
+
+ retval = copy_from_user(data, data_user, sizeof(*data));
+ if (retval) {
+ retval = -EFAULT;
+ goto free_buffer;
+ }
+
+ if (data->signature != 0xCFDCACDF) {
+ retval = -EINVAL;
+ goto free_buffer;
+ }
+
+ retval = zfcp_cfdc_set_fsf(fsf_cfdc, data->command);
+
+ adapter = zfcp_cfdc_get_adapter(data->devno);
+ if (!adapter) {
+ retval = -ENXIO;
+ goto free_buffer;
+ }
+
+ retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg,
+ data_user->control_file);
+ if (retval)
+ goto adapter_put;
+ req = zfcp_fsf_control_file(adapter, fsf_cfdc);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
+ goto free_sg;
+ }
+
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+ retval = -ENXIO;
+ goto free_fsf;
+ }
+
+ zfcp_cfdc_req_to_sense(data, req);
+ retval = copy_to_user(data_user, data, sizeof(*data_user));
+ if (retval) {
+ retval = -EFAULT;
+ goto free_fsf;
+ }
+
+ if (data->command & ZFCP_CFDC_UPLOAD)
+ retval = zfcp_cfdc_copy_to_user(&data_user->control_file,
+ fsf_cfdc->sg);
+
+ free_fsf:
+ zfcp_fsf_req_free(req);
+ free_sg:
+ zfcp_sg_free_table(fsf_cfdc->sg, ZFCP_CFDC_PAGES);
+ adapter_put:
+ zfcp_adapter_put(adapter);
+ free_buffer:
+ kfree(data);
+ no_mem_sense:
+ kfree(fsf_cfdc);
+ return retval;
+}
+
+static const struct file_operations zfcp_cfdc_fops = {
+ .unlocked_ioctl = zfcp_cfdc_dev_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = zfcp_cfdc_dev_ioctl
+#endif
+};
+
+struct miscdevice zfcp_cfdc_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "zfcp_cfdc",
+ .fops = &zfcp_cfdc_fops,
+};
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index c8bad675dbd..060f5f2352e 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -1,22 +1,9 @@
/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
*
- * (C) Copyright IBM Corp. 2002, 2006
+ * Debug traces for zfcp.
*
- * 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 Corporation 2002, 2008
*/
#include <linux/ctype.h>
@@ -29,8 +16,6 @@ module_param(dbfsize, uint, 0400);
MODULE_PARM_DESC(dbfsize,
"number of pages for each debug feature area (default 4)");
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER
-
static void zfcp_dbf_hexdump(debug_info_t *dbf, void *to, int to_len,
int level, char *from, int from_len)
{
@@ -186,8 +171,8 @@ void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req)
fsf_status_qual, FSF_STATUS_QUALIFIER_SIZE);
response->fsf_req_status = fsf_req->status;
response->sbal_first = fsf_req->sbal_first;
- response->sbal_curr = fsf_req->sbal_curr;
response->sbal_last = fsf_req->sbal_last;
+ response->sbal_response = fsf_req->sbal_response;
response->pool = fsf_req->pool != NULL;
response->erp_action = (unsigned long)fsf_req->erp_action;
@@ -268,7 +253,7 @@ void zfcp_hba_dbf_event_fsf_unsol(const char *tag, struct zfcp_adapter *adapter,
strncpy(rec->tag, "stat", ZFCP_DBF_TAG_SIZE);
strncpy(rec->tag2, tag, ZFCP_DBF_TAG_SIZE);
- rec->u.status.failed = adapter->status_read_failed;
+ rec->u.status.failed = atomic_read(&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;
@@ -312,15 +297,13 @@ void zfcp_hba_dbf_event_fsf_unsol(const char *tag, struct zfcp_adapter *adapter,
/**
* zfcp_hba_dbf_event_qdio - trace event for QDIO related failure
* @adapter: adapter affected by this QDIO related event
- * @status: as passed by qdio module
* @qdio_error: as passed by qdio module
- * @siga_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
*/
-void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *adapter, unsigned int status,
- unsigned int qdio_error, unsigned int siga_error,
- int sbal_index, int sbal_count)
+void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *adapter,
+ unsigned int qdio_error, int sbal_index,
+ int sbal_count)
{
struct zfcp_hba_dbf_record *r = &adapter->hba_dbf_buf;
unsigned long flags;
@@ -328,15 +311,33 @@ void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *adapter, unsigned int status,
spin_lock_irqsave(&adapter->hba_dbf_lock, flags);
memset(r, 0, sizeof(*r));
strncpy(r->tag, "qdio", ZFCP_DBF_TAG_SIZE);
- r->u.qdio.status = status;
r->u.qdio.qdio_error = qdio_error;
- r->u.qdio.siga_error = siga_error;
r->u.qdio.sbal_index = sbal_index;
r->u.qdio.sbal_count = sbal_count;
debug_event(adapter->hba_dbf, 0, r, sizeof(*r));
spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags);
}
+/**
+ * zfcp_hba_dbf_event_berr - trace event for bit error threshold
+ * @adapter: adapter affected by this QDIO related event
+ * @req: fsf request
+ */
+void zfcp_hba_dbf_event_berr(struct zfcp_adapter *adapter,
+ struct zfcp_fsf_req *req)
+{
+ struct zfcp_hba_dbf_record *r = &adapter->hba_dbf_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(&adapter->hba_dbf_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(adapter->hba_dbf, 0, r, sizeof(*r));
+ spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags);
+}
static void zfcp_hba_dbf_view_response(char **p,
struct zfcp_hba_dbf_record_response *r)
{
@@ -355,8 +356,8 @@ static void zfcp_hba_dbf_view_response(char **p,
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_curr", "0x%02x", r->sbal_curr);
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) {
@@ -413,13 +414,35 @@ static void zfcp_hba_dbf_view_status(char **p,
static void zfcp_hba_dbf_view_qdio(char **p, struct zfcp_hba_dbf_record_qdio *r)
{
- zfcp_dbf_out(p, "status", "0x%08x", r->status);
zfcp_dbf_out(p, "qdio_error", "0x%08x", r->qdio_error);
- zfcp_dbf_out(p, "siga_error", "0x%08x", r->siga_error);
zfcp_dbf_out(p, "sbal_index", "0x%02x", r->sbal_index);
zfcp_dbf_out(p, "sbal_count", "0x%02x", r->sbal_count);
}
+static void zfcp_hba_dbf_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);
+}
+
static int zfcp_hba_dbf_view_format(debug_info_t *id, struct debug_view *view,
char *out_buf, const char *in_buf)
{
@@ -439,6 +462,8 @@ static int zfcp_hba_dbf_view_format(debug_info_t *id, struct debug_view *view,
zfcp_hba_dbf_view_status(&p, &r->u.status);
else if (strncmp(r->tag, "qdio", ZFCP_DBF_TAG_SIZE) == 0)
zfcp_hba_dbf_view_qdio(&p, &r->u.qdio);
+ else if (strncmp(r->tag, "berr", ZFCP_DBF_TAG_SIZE) == 0)
+ zfcp_hba_dbf_view_berr(&p, &r->u.berr);
p += sprintf(p, "\n");
return p - out_buf;
@@ -515,13 +540,13 @@ static const char *zfcp_rec_dbf_ids[] = {
[52] = "port boxed close unit",
[53] = "port boxed fcp",
[54] = "unit boxed fcp",
- [55] = "port access denied ct",
- [56] = "port access denied els",
- [57] = "port access denied open port",
- [58] = "port access denied close physical",
- [59] = "unit access denied open unit",
+ [55] = "port access denied",
+ [56] = "",
+ [57] = "",
+ [58] = "",
+ [59] = "unit access denied",
[60] = "shared unit access denied open unit",
- [61] = "unit access denied fcp",
+ [61] = "",
[62] = "request timeout",
[63] = "adisc link test reject or timeout",
[64] = "adisc link test d_id changed",
@@ -540,14 +565,14 @@ static const char *zfcp_rec_dbf_ids[] = {
[75] = "physical port recovery escalation after failed port "
"recovery",
[76] = "port recovery escalation after failed unit recovery",
- [77] = "recovery opening nameserver port",
+ [77] = "",
[78] = "duplicate request id",
[79] = "link down",
[80] = "exclusive read-only unit access unsupported",
[81] = "shared read-write unit access unsupported",
[82] = "incoming rscn",
- [83] = "incoming plogi",
- [84] = "incoming logo",
+ [83] = "incoming wwpn",
+ [84] = "wka port handle not valid close port",
[85] = "online",
[86] = "offline",
[87] = "ccw device gone",
@@ -586,16 +611,16 @@ static const char *zfcp_rec_dbf_ids[] = {
[120] = "unknown fsf command",
[121] = "no recommendation for status qualifier",
[122] = "status read physical port closed in error",
- [123] = "fc service class not supported ct",
- [124] = "fc service class not supported els",
+ [123] = "fc service class not supported",
+ [124] = "",
[125] = "need newer zfcp",
[126] = "need newer microcode",
[127] = "arbitrated loop not supported",
- [128] = "unknown topology",
+ [128] = "",
[129] = "qtcb size mismatch",
[130] = "unknown fsf status ecd",
[131] = "fcp request too big",
- [132] = "fc service class not supported fcp",
+ [132] = "",
[133] = "data direction not valid fcp",
[134] = "command length not valid fcp",
[135] = "status read act update",
@@ -603,13 +628,18 @@ static const char *zfcp_rec_dbf_ids[] = {
[137] = "hbaapi port open",
[138] = "hbaapi unit open",
[139] = "hbaapi unit shutdown",
- [140] = "qdio error",
+ [140] = "qdio error outbound",
[141] = "scsi host reset",
[142] = "dismissing fsf request for recovery action",
[143] = "recovery action timed out",
[144] = "recovery action gone",
[145] = "recovery action being processed",
[146] = "recovery action ready for next step",
+ [147] = "qdio error inbound",
+ [148] = "nameserver needed for port scan",
+ [149] = "port scan",
+ [150] = "ptp attach",
+ [151] = "port validation failed",
};
static int zfcp_rec_dbf_view_format(debug_info_t *id, struct debug_view *view,
@@ -670,24 +700,20 @@ static struct debug_view zfcp_rec_dbf_view = {
* zfcp_rec_dbf_event_thread - trace event related to recovery thread operation
* @id2: identifier for event
* @adapter: adapter
- * @lock: non-zero value indicates that erp_lock has not yet been acquired
+ * This function assumes that the caller is holding erp_lock.
*/
-void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter, int lock)
+void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter)
{
struct zfcp_rec_dbf_record *r = &adapter->rec_dbf_buf;
unsigned long flags = 0;
struct list_head *entry;
unsigned ready = 0, running = 0, total;
- if (lock)
- read_lock_irqsave(&adapter->erp_lock, flags);
list_for_each(entry, &adapter->erp_ready_head)
ready++;
list_for_each(entry, &adapter->erp_running_head)
running++;
total = adapter->erp_total_count;
- if (lock)
- read_unlock_irqrestore(&adapter->erp_lock, flags);
spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
memset(r, 0, sizeof(*r));
@@ -696,10 +722,25 @@ void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter, int lock)
r->u.thread.total = total;
r->u.thread.ready = ready;
r->u.thread.running = running;
- debug_event(adapter->rec_dbf, 5, r, sizeof(*r));
+ debug_event(adapter->rec_dbf, 6, r, sizeof(*r));
spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
}
+/**
+ * zfcp_rec_dbf_event_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_rec_dbf_event_thread_lock(u8 id2, struct zfcp_adapter *adapter)
+{
+ unsigned long flags;
+
+ read_lock_irqsave(&adapter->erp_lock, flags);
+ zfcp_rec_dbf_event_thread(id2, adapter);
+ read_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
static void zfcp_rec_dbf_event_target(u8 id2, void *ref,
struct zfcp_adapter *adapter,
atomic_t *status, atomic_t *erp_count,
@@ -823,7 +864,7 @@ void zfcp_rec_dbf_event_action(u8 id2, struct zfcp_erp_action *erp_action)
r->u.action.status = erp_action->status;
r->u.action.step = erp_action->step;
r->u.action.fsf_req = (unsigned long)erp_action->fsf_req;
- debug_event(adapter->rec_dbf, 4, r, sizeof(*r));
+ debug_event(adapter->rec_dbf, 5, r, sizeof(*r));
spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
}
@@ -834,9 +875,9 @@ void zfcp_rec_dbf_event_action(u8 id2, struct zfcp_erp_action *erp_action)
void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req)
{
struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data;
- struct zfcp_port *port = ct->port;
- struct zfcp_adapter *adapter = port->adapter;
- struct ct_hdr *hdr = zfcp_sg_to_address(ct->req);
+ struct zfcp_wka_port *wka_port = ct->wka_port;
+ struct zfcp_adapter *adapter = wka_port->adapter;
+ struct ct_hdr *hdr = sg_virt(ct->req);
struct zfcp_san_dbf_record *r = &adapter->san_dbf_buf;
struct zfcp_san_dbf_record_ct_request *oct = &r->u.ct_req;
unsigned long flags;
@@ -847,7 +888,7 @@ void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req)
r->fsf_reqid = (unsigned long)fsf_req;
r->fsf_seqno = fsf_req->seq_no;
r->s_id = fc_host_port_id(adapter->scsi_host);
- r->d_id = port->d_id;
+ r->d_id = wka_port->d_id;
oct->cmd_req_code = hdr->cmd_rsp_code;
oct->revision = hdr->revision;
oct->gs_type = hdr->gs_type;
@@ -868,9 +909,9 @@ void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req)
void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req)
{
struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data;
- struct zfcp_port *port = ct->port;
- struct zfcp_adapter *adapter = port->adapter;
- struct ct_hdr *hdr = zfcp_sg_to_address(ct->resp);
+ struct zfcp_wka_port *wka_port = ct->wka_port;
+ struct zfcp_adapter *adapter = wka_port->adapter;
+ struct ct_hdr *hdr = sg_virt(ct->resp);
struct zfcp_san_dbf_record *r = &adapter->san_dbf_buf;
struct zfcp_san_dbf_record_ct_response *rct = &r->u.ct_resp;
unsigned long flags;
@@ -880,7 +921,7 @@ void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req)
strncpy(r->tag, "rctc", ZFCP_DBF_TAG_SIZE);
r->fsf_reqid = (unsigned long)fsf_req;
r->fsf_seqno = fsf_req->seq_no;
- r->s_id = port->d_id;
+ r->s_id = wka_port->d_id;
r->d_id = fc_host_port_id(adapter->scsi_host);
rct->cmd_rsp_code = hdr->cmd_rsp_code;
rct->revision = hdr->revision;
@@ -927,8 +968,8 @@ void zfcp_san_dbf_event_els_request(struct zfcp_fsf_req *fsf_req)
zfcp_san_dbf_event_els("oels", 2, fsf_req,
fc_host_port_id(els->adapter->scsi_host),
- els->d_id, *(u8 *) zfcp_sg_to_address(els->req),
- zfcp_sg_to_address(els->req), els->req->length);
+ els->d_id, *(u8 *) sg_virt(els->req),
+ sg_virt(els->req), els->req->length);
}
/**
@@ -941,8 +982,7 @@ void zfcp_san_dbf_event_els_response(struct zfcp_fsf_req *fsf_req)
zfcp_san_dbf_event_els("rels", 2, fsf_req, els->d_id,
fc_host_port_id(els->adapter->scsi_host),
- *(u8 *)zfcp_sg_to_address(els->req),
- zfcp_sg_to_address(els->resp),
+ *(u8 *)sg_virt(els->req), sg_virt(els->resp),
els->resp->length);
}
@@ -960,7 +1000,7 @@ void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *fsf_req)
zfcp_san_dbf_event_els("iels", 1, fsf_req, buf->d_id,
fc_host_port_id(adapter->scsi_host),
- *(u8 *)buf->payload, (void *)buf->payload,
+ buf->payload.data[0], (void *)buf->payload.data,
length);
}
@@ -1064,8 +1104,7 @@ static void zfcp_scsi_dbf_event(const char *tag, const char *tag2, int level,
if (fsf_req != NULL) {
fcp_rsp = (struct fcp_rsp_iu *)
&(fsf_req->qtcb->bottom.io.fcp_rsp);
- fcp_rsp_info =
- zfcp_get_fcp_rsp_info_ptr(fcp_rsp);
+ fcp_rsp_info = (unsigned char *) &fcp_rsp[1];
fcp_sns_info =
zfcp_get_fcp_sns_info_ptr(fcp_rsp);
@@ -1279,5 +1318,3 @@ void zfcp_adapter_debug_unregister(struct zfcp_adapter *adapter)
adapter->hba_dbf = NULL;
adapter->rec_dbf = NULL;
}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 54c34e48345..e8f450801fe 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -38,7 +38,7 @@ struct zfcp_rec_dbf_record_thread {
u32 total;
u32 ready;
u32 running;
-} __attribute__ ((packed));
+};
struct zfcp_rec_dbf_record_target {
u64 ref;
@@ -47,7 +47,7 @@ struct zfcp_rec_dbf_record_target {
u64 wwpn;
u64 fcp_lun;
u32 erp_count;
-} __attribute__ ((packed));
+};
struct zfcp_rec_dbf_record_trigger {
u8 want;
@@ -59,14 +59,14 @@ struct zfcp_rec_dbf_record_trigger {
u64 action;
u64 wwpn;
u64 fcp_lun;
-} __attribute__ ((packed));
+};
struct zfcp_rec_dbf_record_action {
u32 status;
u32 step;
u64 action;
u64 fsf_req;
-} __attribute__ ((packed));
+};
struct zfcp_rec_dbf_record {
u8 id;
@@ -77,7 +77,7 @@ struct zfcp_rec_dbf_record {
struct zfcp_rec_dbf_record_target target;
struct zfcp_rec_dbf_record_trigger trigger;
} u;
-} __attribute__ ((packed));
+};
enum {
ZFCP_REC_DBF_ID_ACTION,
@@ -97,8 +97,8 @@ struct zfcp_hba_dbf_record_response {
u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
u32 fsf_req_status;
u8 sbal_first;
- u8 sbal_curr;
u8 sbal_last;
+ u8 sbal_response;
u8 pool;
u64 erp_action;
union {
@@ -139,9 +139,7 @@ struct zfcp_hba_dbf_record_status {
} __attribute__ ((packed));
struct zfcp_hba_dbf_record_qdio {
- u32 status;
u32 qdio_error;
- u32 siga_error;
u8 sbal_index;
u8 sbal_count;
} __attribute__ ((packed));
@@ -153,6 +151,7 @@ struct zfcp_hba_dbf_record {
struct zfcp_hba_dbf_record_response response;
struct zfcp_hba_dbf_record_status status;
struct zfcp_hba_dbf_record_qdio qdio;
+ struct fsf_bit_error_payload berr;
} u;
} __attribute__ ((packed));
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index bda8c77b22d..8a13071c444 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -1,22 +1,9 @@
/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
*
- * (C) Copyright IBM Corp. 2002, 2006
+ * Global definitions for the zfcp device driver.
*
- * 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 Corporation 2002, 2008
*/
#ifndef ZFCP_DEF_H
@@ -26,7 +13,6 @@
#include <linux/init.h>
#include <linux/moduleparam.h>
-#include <linux/miscdevice.h>
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
@@ -53,32 +39,6 @@
/********************* GENERAL DEFINES *********************************/
-/* zfcp version number, it consists of major, minor, and patch-level number */
-#define ZFCP_VERSION "4.8.0"
-
-/**
- * zfcp_sg_to_address - determine kernel address from struct scatterlist
- * @list: struct scatterlist
- * Return: kernel address
- */
-static inline void *
-zfcp_sg_to_address(struct scatterlist *list)
-{
- return sg_virt(list);
-}
-
-/**
- * zfcp_address_to_sg - set up struct scatterlist from kernel address
- * @address: kernel address
- * @list: struct scatterlist
- * @size: buffer size
- */
-static inline void
-zfcp_address_to_sg(void *address, struct scatterlist *list, unsigned int size)
-{
- sg_set_buf(list, address, size);
-}
-
#define REQUEST_LIST_SIZE 128
/********************* SCSI SPECIFIC DEFINES *********************************/
@@ -93,11 +53,6 @@ zfcp_address_to_sg(void *address, struct scatterlist *list, unsigned int size)
#define ZFCP_DEVICE_MODEL 0x03
#define ZFCP_DEVICE_MODEL_PRIV 0x04
-/* allow as many chained SBALs as are supported by hardware */
-#define ZFCP_MAX_SBALS_PER_REQ FSF_MAX_SBALS_PER_REQ
-#define ZFCP_MAX_SBALS_PER_CT_REQ FSF_MAX_SBALS_PER_REQ
-#define ZFCP_MAX_SBALS_PER_ELS_REQ FSF_MAX_SBALS_PER_ELS_REQ
-
/* DMQ bug workaround: don't use last SBALE */
#define ZFCP_MAX_SBALES_PER_SBAL (QDIO_MAX_ELEMENTS_PER_BUFFER - 1)
@@ -106,64 +61,26 @@ zfcp_address_to_sg(void *address, struct scatterlist *list, unsigned int size)
/* max. number of (data buffer) SBALEs in largest SBAL chain */
#define ZFCP_MAX_SBALES_PER_REQ \
- (ZFCP_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL - 2)
+ (FSF_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL - 2)
/* request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */
#define ZFCP_MAX_SECTORS (ZFCP_MAX_SBALES_PER_REQ * 8)
/* max. number of (data buffer) SBALEs in largest SBAL chain
multiplied with number of sectors per 4k block */
-/* FIXME(tune): free space should be one max. SBAL chain plus what? */
-#define ZFCP_QDIO_PCI_INTERVAL (QDIO_MAX_BUFFERS_PER_Q \
- - (ZFCP_MAX_SBALS_PER_REQ + 4))
-
-#define ZFCP_SBAL_TIMEOUT (5*HZ)
-
-#define ZFCP_TYPE2_RECOVERY_TIME 8 /* seconds */
-
-/* queue polling (values in microseconds) */
-#define ZFCP_MAX_INPUT_THRESHOLD 5000 /* FIXME: tune */
-#define ZFCP_MAX_OUTPUT_THRESHOLD 1000 /* FIXME: tune */
-#define ZFCP_MIN_INPUT_THRESHOLD 1 /* ignored by QDIO layer */
-#define ZFCP_MIN_OUTPUT_THRESHOLD 1 /* ignored by QDIO layer */
-
-#define QDIO_SCSI_QFMT 1 /* 1 for FSF */
-#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer))
-
/********************* FSF SPECIFIC DEFINES *********************************/
-#define ZFCP_ULP_INFO_VERSION 26
-#define ZFCP_QTCB_VERSION FSF_QTCB_CURRENT_VERSION
/* ATTENTION: value must not be used by hardware */
#define FSF_QTCB_UNSOLICITED_STATUS 0x6305
-#define ZFCP_STATUS_READ_FAILED_THRESHOLD 3
-#define ZFCP_STATUS_READS_RECOM FSF_STATUS_READS_RECOM
-
-/* Do 1st retry in 1 second, then double the timeout for each following retry */
-#define ZFCP_EXCHANGE_CONFIG_DATA_FIRST_SLEEP 1
-#define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES 7
/* timeout value for "default timer" for fsf requests */
#define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ)
/*************** FIBRE CHANNEL PROTOCOL SPECIFIC DEFINES ********************/
-typedef unsigned long long wwn_t;
-typedef unsigned long long fcp_lun_t;
-/* data length field may be at variable position in FCP-2 FCP_CMND IU */
-typedef unsigned int fcp_dl_t;
-
-#define ZFCP_FC_SERVICE_CLASS_DEFAULT FSF_CLASS_3
-
/* timeout for name-server lookup (in seconds) */
#define ZFCP_NS_GID_PN_TIMEOUT 10
-/* largest SCSI command we can process */
-/* FCP-2 (FCP_CMND IU) allows up to (255-3+16) */
-#define ZFCP_MAX_SCSI_CMND_LENGTH 255
-/* maximum number of commands in LUN queue (tagged queueing) */
-#define ZFCP_CMND_PER_LUN 32
-
/* task attribute values in FCP-2 FCP_CMND IU */
#define SIMPLE_Q 0
#define HEAD_OF_Q 1
@@ -184,7 +101,7 @@ typedef unsigned int fcp_dl_t;
/* FCP(-2) FCP_CMND IU */
struct fcp_cmnd_iu {
- fcp_lun_t fcp_lun; /* FCP logical unit number */
+ u64 fcp_lun; /* FCP logical unit number */
u8 crn; /* command reference number */
u8 reserved0:5; /* reserved */
u8 task_attribute:3; /* task attribute */
@@ -224,9 +141,9 @@ struct fcp_rsp_iu {
#define RSP_CODE_TASKMAN_FAILED 5
/* see fc-fs */
-#define LS_RSCN 0x61040000
-#define LS_LOGO 0x05000000
-#define LS_PLOGI 0x03000000
+#define LS_RSCN 0x61
+#define LS_LOGO 0x05
+#define LS_PLOGI 0x03
struct fcp_rscn_head {
u8 command;
@@ -259,14 +176,13 @@ struct fcp_rscn_element {
struct fcp_logo {
u32 command;
u32 nport_did;
- wwn_t nport_wwpn;
+ u64 nport_wwpn;
} __attribute__((packed));
/*
* FC-FS stuff
*/
#define R_A_TOV 10 /* seconds */
-#define ZFCP_ELS_TIMEOUT (2 * R_A_TOV)
#define ZFCP_LS_RLS 0x0f
#define ZFCP_LS_ADISC 0x52
@@ -274,13 +190,6 @@ struct fcp_logo {
#define ZFCP_LS_RSCN 0x61
#define ZFCP_LS_RNID 0x78
-struct zfcp_ls_rjt_par {
- u8 action;
- u8 reason_code;
- u8 reason_expl;
- u8 vendor_unique;
-} __attribute__ ((packed));
-
struct zfcp_ls_adisc {
u8 code;
u8 field[3];
@@ -290,20 +199,6 @@ struct zfcp_ls_adisc {
u32 nport_id;
} __attribute__ ((packed));
-struct zfcp_ls_adisc_acc {
- u8 code;
- u8 field[3];
- u32 hard_nport_id;
- u64 wwpn;
- u64 wwnn;
- u32 nport_id;
-} __attribute__ ((packed));
-
-struct zfcp_rc_entry {
- u8 code;
- const char *description;
-};
-
/*
* FC-GS-2 stuff
*/
@@ -311,7 +206,10 @@ struct zfcp_rc_entry {
#define ZFCP_CT_DIRECTORY_SERVICE 0xFC
#define ZFCP_CT_NAME_SERVER 0x02
#define ZFCP_CT_SYNCHRONOUS 0x00
+#define ZFCP_CT_SCSI_FCP 0x08
+#define ZFCP_CT_UNABLE_TO_PERFORM_CMD 0x09
#define ZFCP_CT_GID_PN 0x0121
+#define ZFCP_CT_GPN_FT 0x0172
#define ZFCP_CT_MAX_SIZE 0x1020
#define ZFCP_CT_ACCEPT 0x8002
#define ZFCP_CT_REJECT 0x8001
@@ -321,107 +219,6 @@ struct zfcp_rc_entry {
*/
#define ZFCP_CT_TIMEOUT (3 * R_A_TOV)
-/******************** LOGGING MACROS AND DEFINES *****************************/
-
-/*
- * Logging may be applied on certain kinds of driver operations
- * independently. Additionally, different log-levels are supported for
- * each of these areas.
- */
-
-#define ZFCP_NAME "zfcp"
-
-/* independent log areas */
-#define ZFCP_LOG_AREA_OTHER 0
-#define ZFCP_LOG_AREA_SCSI 1
-#define ZFCP_LOG_AREA_FSF 2
-#define ZFCP_LOG_AREA_CONFIG 3
-#define ZFCP_LOG_AREA_CIO 4
-#define ZFCP_LOG_AREA_QDIO 5
-#define ZFCP_LOG_AREA_ERP 6
-#define ZFCP_LOG_AREA_FC 7
-
-/* log level values*/
-#define ZFCP_LOG_LEVEL_NORMAL 0
-#define ZFCP_LOG_LEVEL_INFO 1
-#define ZFCP_LOG_LEVEL_DEBUG 2
-#define ZFCP_LOG_LEVEL_TRACE 3
-
-/*
- * this allows removal of logging code by the preprocessor
- * (the most detailed log level still to be compiled in is specified,
- * higher log levels are removed)
- */
-#define ZFCP_LOG_LEVEL_LIMIT ZFCP_LOG_LEVEL_TRACE
-
-/* get "loglevel" nibble assignment */
-#define ZFCP_GET_LOG_VALUE(zfcp_lognibble) \
- ((atomic_read(&zfcp_data.loglevel) >> (zfcp_lognibble<<2)) & 0xF)
-
-/* set "loglevel" nibble */
-#define ZFCP_SET_LOG_NIBBLE(value, zfcp_lognibble) \
- (value << (zfcp_lognibble << 2))
-
-/* all log-level defaults are combined to generate initial log-level */
-#define ZFCP_LOG_LEVEL_DEFAULTS \
- (ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_OTHER) | \
- ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_SCSI) | \
- ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_FSF) | \
- ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_CONFIG) | \
- ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_CIO) | \
- ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_QDIO) | \
- ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_ERP) | \
- ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_FC))
-
-/* check whether we have the right level for logging */
-#define ZFCP_LOG_CHECK(level) \
- ((ZFCP_GET_LOG_VALUE(ZFCP_LOG_AREA)) >= level)
-
-/* logging routine for zfcp */
-#define _ZFCP_LOG(fmt, args...) \
- printk(KERN_ERR ZFCP_NAME": %s(%d): " fmt, __func__, \
- __LINE__ , ##args)
-
-#define ZFCP_LOG(level, fmt, args...) \
-do { \
- if (ZFCP_LOG_CHECK(level)) \
- _ZFCP_LOG(fmt, ##args); \
-} while (0)
-
-#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL
-# define ZFCP_LOG_NORMAL(fmt, args...) do { } while (0)
-#else
-# define ZFCP_LOG_NORMAL(fmt, args...) \
-do { \
- if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_NORMAL)) \
- printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \
-} while (0)
-#endif
-
-#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO
-# define ZFCP_LOG_INFO(fmt, args...) do { } while (0)
-#else
-# define ZFCP_LOG_INFO(fmt, args...) \
-do { \
- if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_INFO)) \
- printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \
-} while (0)
-#endif
-
-#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG
-# define ZFCP_LOG_DEBUG(fmt, args...) do { } while (0)
-#else
-# define ZFCP_LOG_DEBUG(fmt, args...) \
- ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, fmt , ##args)
-#endif
-
-#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_TRACE
-# define ZFCP_LOG_TRACE(fmt, args...) do { } while (0)
-#else
-# define ZFCP_LOG_TRACE(fmt, args...) \
- ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, fmt , ##args)
-#endif
-
/*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/
/*
@@ -435,25 +232,23 @@ do { \
#define ZFCP_STATUS_COMMON_RUNNING 0x40000000
#define ZFCP_STATUS_COMMON_ERP_FAILED 0x20000000
#define ZFCP_STATUS_COMMON_UNBLOCKED 0x10000000
-#define ZFCP_STATUS_COMMON_OPENING 0x08000000
#define ZFCP_STATUS_COMMON_OPEN 0x04000000
-#define ZFCP_STATUS_COMMON_CLOSING 0x02000000
#define ZFCP_STATUS_COMMON_ERP_INUSE 0x01000000
#define ZFCP_STATUS_COMMON_ACCESS_DENIED 0x00800000
#define ZFCP_STATUS_COMMON_ACCESS_BOXED 0x00400000
+#define ZFCP_STATUS_COMMON_NOESC 0x00200000
/* adapter status */
#define ZFCP_STATUS_ADAPTER_QDIOUP 0x00000002
-#define ZFCP_STATUS_ADAPTER_REGISTERED 0x00000004
#define ZFCP_STATUS_ADAPTER_XCONFIG_OK 0x00000008
#define ZFCP_STATUS_ADAPTER_HOST_CON_INIT 0x00000010
#define ZFCP_STATUS_ADAPTER_ERP_THREAD_UP 0x00000020
#define ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL 0x00000080
#define ZFCP_STATUS_ADAPTER_ERP_PENDING 0x00000100
#define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED 0x00000200
-#define ZFCP_STATUS_ADAPTER_XPORT_OK 0x00000800
/* FC-PH/FC-GS well-known address identifiers for generic services */
+#define ZFCP_DID_WKA 0xFFFFF0
#define ZFCP_DID_MANAGEMENT_SERVICE 0xFFFFFA
#define ZFCP_DID_TIME_SERVICE 0xFFFFFB
#define ZFCP_DID_DIRECTORY_SERVICE 0xFFFFFC
@@ -465,29 +260,27 @@ do { \
#define ZFCP_STATUS_PORT_DID_DID 0x00000002
#define ZFCP_STATUS_PORT_PHYS_CLOSING 0x00000004
#define ZFCP_STATUS_PORT_NO_WWPN 0x00000008
-#define ZFCP_STATUS_PORT_NO_SCSI_ID 0x00000010
#define ZFCP_STATUS_PORT_INVALID_WWPN 0x00000020
-/* for ports with well known addresses */
-#define ZFCP_STATUS_PORT_WKA \
- (ZFCP_STATUS_PORT_NO_WWPN | \
- ZFCP_STATUS_PORT_NO_SCSI_ID)
+/* well known address (WKA) port status*/
+enum zfcp_wka_status {
+ ZFCP_WKA_PORT_OFFLINE,
+ ZFCP_WKA_PORT_CLOSING,
+ ZFCP_WKA_PORT_OPENING,
+ ZFCP_WKA_PORT_ONLINE,
+};
/* logical unit status */
-#define ZFCP_STATUS_UNIT_TEMPORARY 0x00000002
#define ZFCP_STATUS_UNIT_SHARED 0x00000004
#define ZFCP_STATUS_UNIT_READONLY 0x00000008
#define ZFCP_STATUS_UNIT_REGISTERED 0x00000010
#define ZFCP_STATUS_UNIT_SCSI_WORK_PENDING 0x00000020
/* FSF request status (this does not have a common part) */
-#define ZFCP_STATUS_FSFREQ_NOT_INIT 0x00000000
-#define ZFCP_STATUS_FSFREQ_POOL 0x00000001
#define ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT 0x00000002
#define ZFCP_STATUS_FSFREQ_COMPLETED 0x00000004
#define ZFCP_STATUS_FSFREQ_ERROR 0x00000008
#define ZFCP_STATUS_FSFREQ_CLEANUP 0x00000010
-#define ZFCP_STATUS_FSFREQ_ABORTING 0x00000020
#define ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED 0x00000040
#define ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED 0x00000080
#define ZFCP_STATUS_FSFREQ_ABORTED 0x00000100
@@ -496,77 +289,6 @@ do { \
#define ZFCP_STATUS_FSFREQ_RETRY 0x00000800
#define ZFCP_STATUS_FSFREQ_DISMISSED 0x00001000
-/*********************** ERROR RECOVERY PROCEDURE DEFINES ********************/
-
-#define ZFCP_MAX_ERPS 3
-
-#define ZFCP_ERP_FSFREQ_TIMEOUT (30 * HZ)
-#define ZFCP_ERP_MEMWAIT_TIMEOUT HZ
-
-#define ZFCP_STATUS_ERP_TIMEDOUT 0x10000000
-#define ZFCP_STATUS_ERP_CLOSE_ONLY 0x01000000
-#define ZFCP_STATUS_ERP_DISMISSING 0x00100000
-#define ZFCP_STATUS_ERP_DISMISSED 0x00200000
-#define ZFCP_STATUS_ERP_LOWMEM 0x00400000
-
-#define ZFCP_ERP_STEP_UNINITIALIZED 0x00000000
-#define ZFCP_ERP_STEP_FSF_XCONFIG 0x00000001
-#define ZFCP_ERP_STEP_PHYS_PORT_CLOSING 0x00000010
-#define ZFCP_ERP_STEP_PORT_CLOSING 0x00000100
-#define ZFCP_ERP_STEP_NAMESERVER_OPEN 0x00000200
-#define ZFCP_ERP_STEP_NAMESERVER_LOOKUP 0x00000400
-#define ZFCP_ERP_STEP_PORT_OPENING 0x00000800
-#define ZFCP_ERP_STEP_UNIT_CLOSING 0x00001000
-#define ZFCP_ERP_STEP_UNIT_OPENING 0x00002000
-
-/* Ordered by escalation level (necessary for proper erp-code operation) */
-#define ZFCP_ERP_ACTION_REOPEN_ADAPTER 0x4
-#define ZFCP_ERP_ACTION_REOPEN_PORT_FORCED 0x3
-#define ZFCP_ERP_ACTION_REOPEN_PORT 0x2
-#define ZFCP_ERP_ACTION_REOPEN_UNIT 0x1
-
-#define ZFCP_ERP_ACTION_RUNNING 0x1
-#define ZFCP_ERP_ACTION_READY 0x2
-
-#define ZFCP_ERP_SUCCEEDED 0x0
-#define ZFCP_ERP_FAILED 0x1
-#define ZFCP_ERP_CONTINUES 0x2
-#define ZFCP_ERP_EXIT 0x3
-#define ZFCP_ERP_DISMISSED 0x4
-#define ZFCP_ERP_NOMEM 0x5
-
-
-/******************** CFDC SPECIFIC STUFF *****************************/
-
-/* Firewall data channel sense data record */
-struct zfcp_cfdc_sense_data {
- u32 signature; /* Request signature */
- u32 devno; /* FCP adapter device number */
- u32 command; /* Command code */
- u32 fsf_status; /* FSF request status and status qualifier */
- u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
- u8 payloads[256]; /* Access conflicts list */
- u8 control_file[0]; /* Access control table */
-};
-
-#define ZFCP_CFDC_SIGNATURE 0xCFDCACDF
-
-#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL 0x00010001
-#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE 0x00010101
-#define ZFCP_CFDC_CMND_FULL_ACCESS 0x00000201
-#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS 0x00000401
-#define ZFCP_CFDC_CMND_UPLOAD 0x00010002
-
-#define ZFCP_CFDC_DOWNLOAD 0x00000001
-#define ZFCP_CFDC_UPLOAD 0x00000002
-#define ZFCP_CFDC_WITH_CONTROL_FILE 0x00010000
-
-#define ZFCP_CFDC_DEV_NAME "zfcp_cfdc"
-#define ZFCP_CFDC_DEV_MAJOR MISC_MAJOR
-#define ZFCP_CFDC_DEV_MINOR MISC_DYNAMIC_MINOR
-
-#define ZFCP_CFDC_MAX_CONTROL_FILE_SIZE 127 * 1024
-
/************************* STRUCTURE DEFINITIONS *****************************/
struct zfcp_fsf_req;
@@ -603,7 +325,7 @@ struct ct_hdr {
* a port name is required */
struct ct_iu_gid_pn_req {
struct ct_hdr header;
- wwn_t wwpn;
+ u64 wwpn;
} __attribute__ ((packed));
/* FS_ACC IU and data unit for GID_PN nameserver request */
@@ -612,31 +334,27 @@ struct ct_iu_gid_pn_resp {
u32 d_id;
} __attribute__ ((packed));
-typedef void (*zfcp_send_ct_handler_t)(unsigned long);
-
/**
* struct zfcp_send_ct - used to pass parameters to function zfcp_fsf_send_ct
- * @port: port where the request is sent to
+ * @wka_port: port where the request is sent to
* @req: scatter-gather list for request
* @resp: scatter-gather list for response
* @req_count: number of elements in request scatter-gather list
* @resp_count: number of elements in response scatter-gather list
* @handler: handler function (called for response to the request)
* @handler_data: data passed to handler function
- * @pool: pointer to memory pool for ct request structure
* @timeout: FSF timeout for this request
* @completion: completion for synchronization purposes
* @status: used to pass error status to calling function
*/
struct zfcp_send_ct {
- struct zfcp_port *port;
+ struct zfcp_wka_port *wka_port;
struct scatterlist *req;
struct scatterlist *resp;
unsigned int req_count;
unsigned int resp_count;
- zfcp_send_ct_handler_t handler;
+ void (*handler)(unsigned long);
unsigned long handler_data;
- mempool_t *pool;
int timeout;
struct completion *completion;
int status;
@@ -652,8 +370,6 @@ struct zfcp_gid_pn_data {
struct zfcp_port *port;
};
-typedef void (*zfcp_send_els_handler_t)(unsigned long);
-
/**
* struct zfcp_send_els - used to pass parameters to function zfcp_fsf_send_els
* @adapter: adapter where request is sent from
@@ -677,22 +393,28 @@ struct zfcp_send_els {
struct scatterlist *resp;
unsigned int req_count;
unsigned int resp_count;
- zfcp_send_els_handler_t handler;
+ void (*handler)(unsigned long);
unsigned long handler_data;
struct completion *completion;
int ls_code;
int status;
};
+struct zfcp_wka_port {
+ struct zfcp_adapter *adapter;
+ wait_queue_head_t completion_wq;
+ enum zfcp_wka_status status;
+ atomic_t refcount;
+ u32 d_id;
+ u32 handle;
+ struct mutex mutex;
+ struct delayed_work work;
+};
+
struct zfcp_qdio_queue {
- struct qdio_buffer *buffer[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */
- u8 free_index; /* index of next free bfr
- in queue (free_count>0) */
- atomic_t free_count; /* number of free buffers
- in queue */
- rwlock_t queue_lock; /* lock for operations on queue */
- int distance_from_int; /* SBALs used since PCI indication
- was last set */
+ struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q];
+ u8 first; /* index of next free bfr in queue */
+ atomic_t count; /* number of free buffers in queue */
};
struct zfcp_erp_action {
@@ -701,21 +423,39 @@ struct zfcp_erp_action {
struct zfcp_adapter *adapter; /* device which should be recovered */
struct zfcp_port *port;
struct zfcp_unit *unit;
- volatile u32 status; /* recovery status */
+ u32 status; /* recovery status */
u32 step; /* active step of this erp action */
struct zfcp_fsf_req *fsf_req; /* fsf request currently pending
for this action */
struct timer_list timer;
};
+struct fsf_latency_record {
+ u32 min;
+ u32 max;
+ u64 sum;
+};
+
+struct latency_cont {
+ struct fsf_latency_record channel;
+ struct fsf_latency_record fabric;
+ u64 counter;
+};
+
+struct zfcp_latencies {
+ struct latency_cont read;
+ struct latency_cont write;
+ struct latency_cont cmd;
+ spinlock_t lock;
+};
struct zfcp_adapter {
struct list_head list; /* list of adapters */
atomic_t refcount; /* reference count */
wait_queue_head_t remove_wq; /* can be used to wait for
refcount drop to zero */
- wwn_t peer_wwnn; /* P2P peer WWNN */
- wwn_t peer_wwpn; /* P2P peer WWPN */
+ u64 peer_wwnn; /* P2P peer WWNN */
+ u64 peer_wwpn; /* P2P peer WWPN */
u32 peer_d_id; /* P2P peer D_ID */
struct ccw_device *ccw_device; /* S/390 ccw device */
u32 hydra_version; /* Hydra version */
@@ -723,24 +463,25 @@ struct zfcp_adapter {
u32 adapter_features; /* FCP channel features */
u32 connection_features; /* host connection features */
u32 hardware_version; /* of FCP channel */
+ u16 timer_ticks; /* time int for a tick */
struct Scsi_Host *scsi_host; /* Pointer to mid-layer */
struct list_head port_list_head; /* remote port list */
- struct list_head port_remove_lh; /* head of ports to be
- removed */
- u32 ports; /* number of remote ports */
- atomic_t reqs_active; /* # active FSF reqs */
unsigned long req_no; /* unique FSF req number */
struct list_head *req_list; /* list of pending reqs */
spinlock_t req_list_lock; /* request list lock */
- struct zfcp_qdio_queue request_queue; /* request queue */
+ struct zfcp_qdio_queue req_q; /* request queue */
+ spinlock_t req_q_lock; /* for operations on queue */
+ int req_q_pci_batch; /* SBALs since PCI indication
+ was last set */
u32 fsf_req_seq_no; /* FSF cmnd seq number */
wait_queue_head_t request_wq; /* can be used to wait for
more avaliable SBALs */
- struct zfcp_qdio_queue response_queue; /* response queue */
+ struct zfcp_qdio_queue resp_q; /* response queue */
rwlock_t abort_lock; /* Protects against SCSI
stack abort/command
completion races */
- u16 status_read_failed; /* # failed status reads */
+ atomic_t stat_miss; /* # missing status reads*/
+ struct work_struct stat_work;
atomic_t status; /* status of this adapter */
struct list_head erp_ready_head; /* error recovery for this
adapter/devices */
@@ -755,7 +496,7 @@ struct zfcp_adapter {
actions */
u32 erp_low_mem_count; /* nr of erp actions waiting
for memory */
- struct zfcp_port *nameserver_port; /* adapter's nameserver */
+ struct zfcp_wka_port nsp; /* adapter's nameserver */
debug_info_t *rec_dbf;
debug_info_t *hba_dbf;
debug_info_t *san_dbf; /* debug feature areas */
@@ -770,17 +511,13 @@ struct zfcp_adapter {
struct zfcp_scsi_dbf_record scsi_dbf_buf;
struct zfcp_adapter_mempool pool; /* Adapter memory pools */
struct qdio_initialize qdio_init_data; /* for qdio_establish */
- struct device generic_services; /* directory for WKA ports */
struct fc_host_statistics *fc_stats;
struct fsf_qtcb_bottom_port *stats_reset_data;
unsigned long stats_reset;
+ struct work_struct scan_work;
+ atomic_t qdio_outb_full; /* queue full incidents */
};
-/*
- * the struct device sysfs_device must be at the beginning of this structure.
- * pointer to struct device is used to free port structure in release function
- * of the device. don't change!
- */
struct zfcp_port {
struct device sysfs_device; /* sysfs device */
struct fc_rport *rport; /* rport of fc transport class */
@@ -790,24 +527,18 @@ struct zfcp_port {
refcount drop to zero */
struct zfcp_adapter *adapter; /* adapter used to access port */
struct list_head unit_list_head; /* head of logical unit list */
- struct list_head unit_remove_lh; /* head of luns to be removed
- list */
- u32 units; /* # of logical units in list */
atomic_t status; /* status of this remote port */
- wwn_t wwnn; /* WWNN if known */
- wwn_t wwpn; /* WWPN */
+ u64 wwnn; /* WWNN if known */
+ u64 wwpn; /* WWPN */
u32 d_id; /* D_ID */
u32 handle; /* handle assigned by FSF */
struct zfcp_erp_action erp_action; /* pending error recovery */
atomic_t erp_counter;
u32 maxframe_size;
u32 supported_classes;
+ struct work_struct gid_pn_work;
};
-/* the struct device sysfs_device must be at the beginning of this structure.
- * pointer to struct device is used to free unit structure in release function
- * of the device. don't change!
- */
struct zfcp_unit {
struct device sysfs_device; /* sysfs device */
struct list_head list; /* list of logical units */
@@ -816,12 +547,12 @@ struct zfcp_unit {
refcount drop to zero */
struct zfcp_port *port; /* remote port of unit */
atomic_t status; /* status of this logical unit */
- unsigned int scsi_lun; /* own SCSI LUN */
- fcp_lun_t fcp_lun; /* own FCP_LUN */
+ u64 fcp_lun; /* own FCP_LUN */
u32 handle; /* handle assigned by FSF */
struct scsi_device *device; /* scsi device struct pointer */
struct zfcp_erp_action erp_action; /* pending error recovery */
atomic_t erp_counter;
+ struct zfcp_latencies latencies;
};
/* FSF request */
@@ -831,19 +562,19 @@ struct zfcp_fsf_req {
struct zfcp_adapter *adapter; /* adapter request belongs to */
u8 sbal_number; /* nr of SBALs free for use */
u8 sbal_first; /* first SBAL for this request */
- u8 sbal_last; /* last possible SBAL for
+ u8 sbal_last; /* last SBAL for this request */
+ u8 sbal_limit; /* last possible SBAL for
this reuest */
- u8 sbal_curr; /* current SBAL during creation
- of request */
u8 sbale_curr; /* current SBALE during creation
of request */
+ u8 sbal_response; /* SBAL used in interrupt */
wait_queue_head_t completion_wq; /* can be used by a routine
to wait for completion */
- volatile u32 status; /* status of this request */
+ u32 status; /* status of this request */
u32 fsf_command; /* FSF Command copy */
struct fsf_qtcb *qtcb; /* address of associated QTCB */
u32 seq_no; /* Sequence number of request */
- unsigned long data; /* private data of request */
+ void *data; /* private data of request */
struct timer_list timer; /* used for erp or scsi er */
struct zfcp_erp_action *erp_action; /* used if this request is
issued on behalf of erp */
@@ -851,51 +582,29 @@ struct zfcp_fsf_req {
from emergency pool */
unsigned long long issued; /* request sent time (STCK) */
struct zfcp_unit *unit;
+ void (*handler)(struct zfcp_fsf_req *);
};
-typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*);
-
/* driver data */
struct zfcp_data {
struct scsi_host_template scsi_host_template;
struct scsi_transport_template *scsi_transport_template;
- atomic_t status; /* Module status flags */
struct list_head adapter_list_head; /* head of adapter list */
- struct list_head adapter_remove_lh; /* head of adapters to be
- removed */
- u32 adapters; /* # of adapters in list */
rwlock_t config_lock; /* serialises changes
to adapter/port/unit
lists */
struct semaphore config_sema; /* serialises configuration
changes */
atomic_t loglevel; /* current loglevel */
- char init_busid[BUS_ID_SIZE];
- wwn_t init_wwpn;
- fcp_lun_t init_fcp_lun;
- char *driver_version;
- struct kmem_cache *fsf_req_qtcb_cache;
- struct kmem_cache *sr_buffer_cache;
- struct kmem_cache *gid_pn_cache;
-};
-
-/**
- * struct zfcp_sg_list - struct describing a scatter-gather list
- * @sg: pointer to array of (struct scatterlist)
- * @count: number of elements in scatter-gather list
- */
-struct zfcp_sg_list {
- struct scatterlist *sg;
- unsigned int count;
+ char init_busid[20];
+ u64 init_wwpn;
+ u64 init_fcp_lun;
+ struct kmem_cache *fsf_req_qtcb_cache;
+ struct kmem_cache *sr_buffer_cache;
+ struct kmem_cache *gid_pn_cache;
+ struct workqueue_struct *work_queue;
};
-/* number of elements for various memory pools */
-#define ZFCP_POOL_FSF_REQ_ERP_NR 1
-#define ZFCP_POOL_FSF_REQ_SCSI_NR 1
-#define ZFCP_POOL_FSF_REQ_ABORT_NR 1
-#define ZFCP_POOL_STATUS_READ_NR ZFCP_STATUS_READS_RECOM
-#define ZFCP_POOL_DATA_GID_PN_NR 1
-
/* struct used by memory pools for fsf_requests */
struct zfcp_fsf_req_qtcb {
struct zfcp_fsf_req fsf_req;
@@ -905,26 +614,12 @@ struct zfcp_fsf_req_qtcb {
/********************** ZFCP SPECIFIC DEFINES ********************************/
#define ZFCP_REQ_AUTO_CLEANUP 0x00000002
-#define ZFCP_WAIT_FOR_SBAL 0x00000004
#define ZFCP_REQ_NO_QTCB 0x00000008
#define ZFCP_SET 0x00000100
#define ZFCP_CLEAR 0x00000200
-#ifndef atomic_test_mask
-#define atomic_test_mask(mask, target) \
- ((atomic_read(target) & mask) == mask)
-#endif
-
-extern void _zfcp_hex_dump(char *, int);
-#define ZFCP_HEX_DUMP(level, addr, count) \
- if (ZFCP_LOG_CHECK(level)) { \
- _zfcp_hex_dump(addr, count); \
- }
-
-#define zfcp_get_busid_by_adapter(adapter) (adapter->ccw_device->dev.bus_id)
-#define zfcp_get_busid_by_port(port) (zfcp_get_busid_by_adapter(port->adapter))
-#define zfcp_get_busid_by_unit(unit) (zfcp_get_busid_by_port(unit->port))
+#define zfcp_get_busid_by_adapter(adapter) (dev_name(&adapter->ccw_device->dev))
/*
* Helper functions for request ID management.
@@ -934,15 +629,6 @@ static inline int zfcp_reqlist_hash(unsigned long req_id)
return req_id % REQUEST_LIST_SIZE;
}
-static inline void zfcp_reqlist_add(struct zfcp_adapter *adapter,
- struct zfcp_fsf_req *fsf_req)
-{
- unsigned int idx;
-
- idx = zfcp_reqlist_hash(fsf_req->req_id);
- list_add_tail(&fsf_req->list, &adapter->req_list[idx]);
-}
-
static inline void zfcp_reqlist_remove(struct zfcp_adapter *adapter,
struct zfcp_fsf_req *fsf_req)
{
@@ -994,12 +680,6 @@ zfcp_unit_put(struct zfcp_unit *unit)
}
static inline void
-zfcp_unit_wait(struct zfcp_unit *unit)
-{
- wait_event(unit->remove_wq, atomic_read(&unit->refcount) == 0);
-}
-
-static inline void
zfcp_port_get(struct zfcp_port *port)
{
atomic_inc(&port->refcount);
@@ -1013,12 +693,6 @@ zfcp_port_put(struct zfcp_port *port)
}
static inline void
-zfcp_port_wait(struct zfcp_port *port)
-{
- wait_event(port->remove_wq, atomic_read(&port->refcount) == 0);
-}
-
-static inline void
zfcp_adapter_get(struct zfcp_adapter *adapter)
{
atomic_inc(&adapter->refcount);
@@ -1031,10 +705,4 @@ zfcp_adapter_put(struct zfcp_adapter *adapter)
wake_up(&adapter->remove_wq);
}
-static inline void
-zfcp_adapter_wait(struct zfcp_adapter *adapter)
-{
- wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0);
-}
-
#endif /* ZFCP_DEF_H */
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 805484658dd..9040f738ff3 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -1,641 +1,405 @@
/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
*
- * (C) Copyright IBM Corp. 2002, 2006
+ * Error Recovery Procedures (ERP).
*
- * 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 Corporation 2002, 2008
*/
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_ERP
-
#include "zfcp_ext.h"
-static int zfcp_erp_adisc(struct zfcp_port *);
-static void zfcp_erp_adisc_handler(unsigned long);
-
-static int zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *, int, u8,
- void *);
-static int zfcp_erp_port_forced_reopen_internal(struct zfcp_port *, int, u8,
- void *);
-static int zfcp_erp_port_reopen_internal(struct zfcp_port *, int, u8, void *);
-static int zfcp_erp_unit_reopen_internal(struct zfcp_unit *, int, u8, void *);
-
-static int zfcp_erp_port_reopen_all_internal(struct zfcp_adapter *, int, u8,
- void *);
-static int zfcp_erp_unit_reopen_all_internal(struct zfcp_port *, int, u8,
- void *);
-
-static void zfcp_erp_adapter_block(struct zfcp_adapter *, int);
-static void zfcp_erp_adapter_unblock(struct zfcp_adapter *);
-static void zfcp_erp_port_block(struct zfcp_port *, int);
-static void zfcp_erp_port_unblock(struct zfcp_port *);
-static void zfcp_erp_unit_block(struct zfcp_unit *, int);
-static void zfcp_erp_unit_unblock(struct zfcp_unit *);
-
-static int zfcp_erp_thread(void *);
-
-static int zfcp_erp_strategy(struct zfcp_erp_action *);
-
-static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *);
-static int zfcp_erp_strategy_memwait(struct zfcp_erp_action *);
-static int zfcp_erp_strategy_check_target(struct zfcp_erp_action *, int);
-static int zfcp_erp_strategy_check_unit(struct zfcp_unit *, int);
-static int zfcp_erp_strategy_check_port(struct zfcp_port *, int);
-static int zfcp_erp_strategy_check_adapter(struct zfcp_adapter *, int);
-static int zfcp_erp_strategy_statechange(int, u32, struct zfcp_adapter *,
- struct zfcp_port *,
- struct zfcp_unit *, int);
-static int zfcp_erp_strategy_statechange_detected(atomic_t *, u32);
-static int zfcp_erp_strategy_followup_actions(int, struct zfcp_adapter *,
- struct zfcp_port *,
- struct zfcp_unit *, int);
-static int zfcp_erp_strategy_check_queues(struct zfcp_adapter *);
-static int zfcp_erp_strategy_check_action(struct zfcp_erp_action *, int);
-
-static int zfcp_erp_adapter_strategy(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *, int);
-static int zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open_fsf_statusread(
- struct zfcp_erp_action *);
-
-static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *);
-static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *);
-
-static int zfcp_erp_port_strategy(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_clearstati(struct zfcp_port *);
-static int zfcp_erp_port_strategy_close(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open_nameserver_wakeup(
- struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open_common_lookup(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *);
-
-static int zfcp_erp_unit_strategy(struct zfcp_erp_action *);
-static int zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *);
-static int zfcp_erp_unit_strategy_close(struct zfcp_erp_action *);
-static int zfcp_erp_unit_strategy_open(struct zfcp_erp_action *);
-
-static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *);
-static void zfcp_erp_action_dismiss_port(struct zfcp_port *);
-static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *);
-static void zfcp_erp_action_dismiss(struct zfcp_erp_action *);
-
-static int zfcp_erp_action_enqueue(int, struct zfcp_adapter *,
- struct zfcp_port *, struct zfcp_unit *,
- u8 id, void *ref);
-static int zfcp_erp_action_dequeue(struct zfcp_erp_action *);
-static void zfcp_erp_action_cleanup(int, struct zfcp_adapter *,
- struct zfcp_port *, struct zfcp_unit *,
- int);
-
-static void zfcp_erp_action_ready(struct zfcp_erp_action *);
-static int zfcp_erp_action_exists(struct zfcp_erp_action *);
-
-static void zfcp_erp_action_to_ready(struct zfcp_erp_action *);
-static void zfcp_erp_action_to_running(struct zfcp_erp_action *);
-
-static void zfcp_erp_memwait_handler(unsigned long);
+#define ZFCP_MAX_ERPS 3
-/**
- * zfcp_close_qdio - close qdio queues for an adapter
- */
-static void zfcp_close_qdio(struct zfcp_adapter *adapter)
-{
- struct zfcp_qdio_queue *req_queue;
- int first, count;
+enum zfcp_erp_act_flags {
+ ZFCP_STATUS_ERP_TIMEDOUT = 0x10000000,
+ ZFCP_STATUS_ERP_CLOSE_ONLY = 0x01000000,
+ ZFCP_STATUS_ERP_DISMISSING = 0x00100000,
+ ZFCP_STATUS_ERP_DISMISSED = 0x00200000,
+ ZFCP_STATUS_ERP_LOWMEM = 0x00400000,
+};
- if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status))
- return;
+enum zfcp_erp_steps {
+ ZFCP_ERP_STEP_UNINITIALIZED = 0x0000,
+ ZFCP_ERP_STEP_FSF_XCONFIG = 0x0001,
+ ZFCP_ERP_STEP_PHYS_PORT_CLOSING = 0x0010,
+ ZFCP_ERP_STEP_PORT_CLOSING = 0x0100,
+ ZFCP_ERP_STEP_NAMESERVER_LOOKUP = 0x0400,
+ ZFCP_ERP_STEP_PORT_OPENING = 0x0800,
+ ZFCP_ERP_STEP_UNIT_CLOSING = 0x1000,
+ ZFCP_ERP_STEP_UNIT_OPENING = 0x2000,
+};
- /* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */
- req_queue = &adapter->request_queue;
- write_lock_irq(&req_queue->queue_lock);
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
- write_unlock_irq(&req_queue->queue_lock);
-
- while (qdio_shutdown(adapter->ccw_device,
- QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS)
- ssleep(1);
-
- /* cleanup used outbound sbals */
- count = atomic_read(&req_queue->free_count);
- if (count < QDIO_MAX_BUFFERS_PER_Q) {
- first = (req_queue->free_index+count) % QDIO_MAX_BUFFERS_PER_Q;
- count = QDIO_MAX_BUFFERS_PER_Q - count;
- zfcp_qdio_zero_sbals(req_queue->buffer, first, count);
- }
- req_queue->free_index = 0;
- atomic_set(&req_queue->free_count, 0);
- req_queue->distance_from_int = 0;
- adapter->response_queue.free_index = 0;
- atomic_set(&adapter->response_queue.free_count, 0);
+enum zfcp_erp_act_type {
+ ZFCP_ERP_ACTION_REOPEN_UNIT = 1,
+ ZFCP_ERP_ACTION_REOPEN_PORT = 2,
+ ZFCP_ERP_ACTION_REOPEN_PORT_FORCED = 3,
+ ZFCP_ERP_ACTION_REOPEN_ADAPTER = 4,
+};
+
+enum zfcp_erp_act_state {
+ ZFCP_ERP_ACTION_RUNNING = 1,
+ ZFCP_ERP_ACTION_READY = 2,
+};
+
+enum zfcp_erp_act_result {
+ ZFCP_ERP_SUCCEEDED = 0,
+ ZFCP_ERP_FAILED = 1,
+ ZFCP_ERP_CONTINUES = 2,
+ ZFCP_ERP_EXIT = 3,
+ ZFCP_ERP_DISMISSED = 4,
+ ZFCP_ERP_NOMEM = 5,
+};
+
+static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int mask)
+{
+ zfcp_erp_modify_adapter_status(adapter, 15, NULL,
+ ZFCP_STATUS_COMMON_UNBLOCKED | mask,
+ ZFCP_CLEAR);
}
-/**
- * zfcp_close_fsf - stop FSF operations for an adapter
- *
- * Dismiss and cleanup all pending fsf_reqs (this wakes up all initiators of
- * requests waiting for completion; especially this returns SCSI commands
- * with error state).
- */
-static void zfcp_close_fsf(struct zfcp_adapter *adapter)
+static int zfcp_erp_action_exists(struct zfcp_erp_action *act)
{
- /* close queues to ensure that buffers are not accessed by adapter */
- zfcp_close_qdio(adapter);
- zfcp_fsf_req_dismiss_all(adapter);
- /* reset FSF request sequence number */
- adapter->fsf_req_seq_no = 0;
- /* all ports and units are closed */
- zfcp_erp_modify_adapter_status(adapter, 24, NULL,
- ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
+ struct zfcp_erp_action *curr_act;
+
+ list_for_each_entry(curr_act, &act->adapter->erp_running_head, list)
+ if (act == curr_act)
+ return ZFCP_ERP_ACTION_RUNNING;
+ return 0;
}
-/**
- * zfcp_fsf_request_timeout_handler - called if a request timed out
- * @data: pointer to adapter for handler function
- *
- * This function needs to be called if requests (ELS, Generic Service,
- * or SCSI commands) exceed a certain time limit. The assumption is
- * that after the time limit the adapter get stuck. So we trigger a reopen of
- * the adapter.
- */
-static void zfcp_fsf_request_timeout_handler(unsigned long data)
+static void zfcp_erp_action_ready(struct zfcp_erp_action *act)
{
- struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
- zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 62,
- NULL);
+ struct zfcp_adapter *adapter = act->adapter;
+
+ list_move(&act->list, &act->adapter->erp_ready_head);
+ zfcp_rec_dbf_event_action(146, act);
+ up(&adapter->erp_ready_sem);
+ zfcp_rec_dbf_event_thread(2, adapter);
}
-void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req, unsigned long timeout)
+static void zfcp_erp_action_dismiss(struct zfcp_erp_action *act)
{
- fsf_req->timer.function = zfcp_fsf_request_timeout_handler;
- fsf_req->timer.data = (unsigned long) fsf_req->adapter;
- fsf_req->timer.expires = jiffies + timeout;
- add_timer(&fsf_req->timer);
+ act->status |= ZFCP_STATUS_ERP_DISMISSED;
+ if (zfcp_erp_action_exists(act) == ZFCP_ERP_ACTION_RUNNING)
+ zfcp_erp_action_ready(act);
}
-/*
- * function:
- *
- * purpose: called if an adapter failed,
- * initiates adapter recovery which is done
- * asynchronously
- *
- * returns: 0 - initiated action successfully
- * <0 - failed to initiate action
- */
-static int zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *adapter,
- int clear_mask, u8 id, void *ref)
+static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit)
{
- int retval;
+ if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+ zfcp_erp_action_dismiss(&unit->erp_action);
+}
- ZFCP_LOG_DEBUG("reopen adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
+static void zfcp_erp_action_dismiss_port(struct zfcp_port *port)
+{
+ struct zfcp_unit *unit;
- zfcp_erp_adapter_block(adapter, clear_mask);
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+ zfcp_erp_action_dismiss(&port->erp_action);
+ else
+ list_for_each_entry(unit, &port->unit_list_head, list)
+ zfcp_erp_action_dismiss_unit(unit);
+}
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
- ZFCP_LOG_DEBUG("skipped reopen of failed adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- /* ensure propagation of failed status to new devices */
- zfcp_erp_adapter_failed(adapter, 13, NULL);
- retval = -EIO;
- goto out;
- }
- retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER,
- adapter, NULL, NULL, id, ref);
+static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
+{
+ struct zfcp_port *port;
- out:
- return retval;
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+ zfcp_erp_action_dismiss(&adapter->erp_action);
+ else
+ list_for_each_entry(port, &adapter->port_list_head, list)
+ zfcp_erp_action_dismiss_port(port);
}
-/*
- * function:
- *
- * purpose: Wrappper for zfcp_erp_adapter_reopen_internal
- * used to ensure the correct locking
- *
- * returns: 0 - initiated action successfully
- * <0 - failed to initiate action
- */
-int zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear_mask,
- u8 id, void *ref)
+static int zfcp_erp_required_act(int want, struct zfcp_adapter *adapter,
+ struct zfcp_port *port,
+ struct zfcp_unit *unit)
{
- int retval;
- unsigned long flags;
+ int need = want;
+ int u_status, p_status, a_status;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- write_lock(&adapter->erp_lock);
- retval = zfcp_erp_adapter_reopen_internal(adapter, clear_mask, id, ref);
- write_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ switch (want) {
+ case ZFCP_ERP_ACTION_REOPEN_UNIT:
+ u_status = atomic_read(&unit->status);
+ if (u_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+ return 0;
+ p_status = atomic_read(&port->status);
+ if (!(p_status & ZFCP_STATUS_COMMON_RUNNING) ||
+ p_status & ZFCP_STATUS_COMMON_ERP_FAILED)
+ return 0;
+ if (!(p_status & ZFCP_STATUS_COMMON_UNBLOCKED))
+ need = ZFCP_ERP_ACTION_REOPEN_PORT;
+ /* fall through */
+ case ZFCP_ERP_ACTION_REOPEN_PORT:
+ case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+ p_status = atomic_read(&port->status);
+ if (p_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+ return 0;
+ a_status = atomic_read(&adapter->status);
+ if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) ||
+ a_status & ZFCP_STATUS_COMMON_ERP_FAILED)
+ return 0;
+ if (!(a_status & ZFCP_STATUS_COMMON_UNBLOCKED))
+ need = ZFCP_ERP_ACTION_REOPEN_ADAPTER;
+ /* fall through */
+ case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+ a_status = atomic_read(&adapter->status);
+ if (a_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+ return 0;
+ }
- return retval;
+ return need;
}
-int zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear_mask,
- u8 id, void *ref)
+static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
+ struct zfcp_adapter *adapter,
+ struct zfcp_port *port,
+ struct zfcp_unit *unit)
{
- int retval;
+ struct zfcp_erp_action *erp_action;
+ u32 status = 0;
- retval = zfcp_erp_adapter_reopen(adapter,
- ZFCP_STATUS_COMMON_RUNNING |
- ZFCP_STATUS_COMMON_ERP_FAILED |
- clear_mask, id, ref);
+ switch (need) {
+ case ZFCP_ERP_ACTION_REOPEN_UNIT:
+ zfcp_unit_get(unit);
+ atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status);
+ erp_action = &unit->erp_action;
+ if (!(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_RUNNING))
+ status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+ break;
- return retval;
-}
+ case ZFCP_ERP_ACTION_REOPEN_PORT:
+ case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+ zfcp_port_get(port);
+ zfcp_erp_action_dismiss_port(port);
+ atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
+ erp_action = &port->erp_action;
+ if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING))
+ status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+ break;
-int zfcp_erp_port_shutdown(struct zfcp_port *port, int clear_mask, u8 id,
- void *ref)
-{
- int retval;
+ case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+ zfcp_adapter_get(adapter);
+ zfcp_erp_action_dismiss_adapter(adapter);
+ atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
+ erp_action = &adapter->erp_action;
+ if (!(atomic_read(&adapter->status) &
+ ZFCP_STATUS_COMMON_RUNNING))
+ status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+ break;
- retval = zfcp_erp_port_reopen(port,
- ZFCP_STATUS_COMMON_RUNNING |
- ZFCP_STATUS_COMMON_ERP_FAILED |
- clear_mask, id, ref);
+ default:
+ return NULL;
+ }
- return retval;
+ memset(erp_action, 0, sizeof(struct zfcp_erp_action));
+ erp_action->adapter = adapter;
+ erp_action->port = port;
+ erp_action->unit = unit;
+ erp_action->action = need;
+ erp_action->status = status;
+
+ return erp_action;
}
-int zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear_mask, u8 id,
- void *ref)
+static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
+ struct zfcp_port *port,
+ struct zfcp_unit *unit, u8 id, void *ref)
{
- int retval;
+ int retval = 1, need;
+ struct zfcp_erp_action *act = NULL;
+
+ if (!(atomic_read(&adapter->status) &
+ ZFCP_STATUS_ADAPTER_ERP_THREAD_UP))
+ return -EIO;
- retval = zfcp_erp_unit_reopen(unit,
- ZFCP_STATUS_COMMON_RUNNING |
- ZFCP_STATUS_COMMON_ERP_FAILED |
- clear_mask, id, ref);
+ need = zfcp_erp_required_act(want, adapter, port, unit);
+ if (!need)
+ goto out;
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
+ act = zfcp_erp_setup_act(need, adapter, port, unit);
+ if (!act)
+ goto out;
+ ++adapter->erp_total_count;
+ list_add_tail(&act->list, &adapter->erp_ready_head);
+ up(&adapter->erp_ready_sem);
+ zfcp_rec_dbf_event_thread(1, adapter);
+ retval = 0;
+ out:
+ zfcp_rec_dbf_event_trigger(id, ref, want, need, act,
+ adapter, port, unit);
return retval;
}
-
-/**
- * zfcp_erp_adisc - send ADISC ELS command
- * @port: port structure
- */
-static int
-zfcp_erp_adisc(struct zfcp_port *port)
+static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter,
+ int clear_mask, u8 id, void *ref)
{
- struct zfcp_adapter *adapter = port->adapter;
- struct zfcp_send_els *send_els;
- struct zfcp_ls_adisc *adisc;
- void *address = NULL;
- int retval = 0;
-
- send_els = kzalloc(sizeof(struct zfcp_send_els), GFP_ATOMIC);
- if (send_els == NULL)
- goto nomem;
-
- send_els->req = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC);
- if (send_els->req == NULL)
- goto nomem;
- sg_init_table(send_els->req, 1);
-
- send_els->resp = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC);
- if (send_els->resp == NULL)
- goto nomem;
- sg_init_table(send_els->resp, 1);
-
- address = (void *) get_zeroed_page(GFP_ATOMIC);
- if (address == NULL)
- goto nomem;
-
- zfcp_address_to_sg(address, send_els->req, sizeof(struct zfcp_ls_adisc));
- address += PAGE_SIZE >> 1;
- zfcp_address_to_sg(address, send_els->resp, sizeof(struct zfcp_ls_adisc_acc));
- send_els->req_count = send_els->resp_count = 1;
-
- send_els->adapter = adapter;
- send_els->port = port;
- send_els->d_id = port->d_id;
- send_els->handler = zfcp_erp_adisc_handler;
- send_els->handler_data = (unsigned long) send_els;
-
- adisc = zfcp_sg_to_address(send_els->req);
- send_els->ls_code = adisc->code = ZFCP_LS_ADISC;
-
- /* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
- without FC-AL-2 capability, so we don't set it */
- adisc->wwpn = fc_host_port_name(adapter->scsi_host);
- adisc->wwnn = fc_host_node_name(adapter->scsi_host);
- adisc->nport_id = fc_host_port_id(adapter->scsi_host);
- ZFCP_LOG_INFO("ADISC request from s_id 0x%06x to d_id 0x%06x "
- "(wwpn=0x%016Lx, wwnn=0x%016Lx, "
- "hard_nport_id=0x%06x, nport_id=0x%06x)\n",
- adisc->nport_id, send_els->d_id, (wwn_t) adisc->wwpn,
- (wwn_t) adisc->wwnn, adisc->hard_nport_id,
- adisc->nport_id);
-
- retval = zfcp_fsf_send_els(send_els);
- if (retval != 0) {
- ZFCP_LOG_NORMAL("error: initiation of Send ELS failed for port "
- "0x%06x on adapter %s\n", send_els->d_id,
- zfcp_get_busid_by_adapter(adapter));
- goto freemem;
- }
+ zfcp_erp_adapter_block(adapter, clear_mask);
- goto out;
-
- nomem:
- retval = -ENOMEM;
- freemem:
- if (address != NULL)
- __free_pages(sg_page(send_els->req), 0);
- if (send_els != NULL) {
- kfree(send_els->req);
- kfree(send_els->resp);
- kfree(send_els);
+ /* ensure propagation of failed status to new devices */
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
+ zfcp_erp_adapter_failed(adapter, 13, NULL);
+ return -EIO;
}
- out:
- return retval;
+ return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER,
+ adapter, NULL, NULL, id, ref);
}
-
/**
- * zfcp_erp_adisc_handler - handler for ADISC ELS command
- * @data: pointer to struct zfcp_send_els
- *
- * If ADISC failed (LS_RJT or timed out) forced reopen of the port is triggered.
+ * zfcp_erp_adapter_reopen - Reopen adapter.
+ * @adapter: Adapter to reopen.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
*/
-static void
-zfcp_erp_adisc_handler(unsigned long data)
+void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear,
+ u8 id, void *ref)
{
- struct zfcp_send_els *send_els;
- struct zfcp_port *port;
- struct zfcp_adapter *adapter;
- u32 d_id;
- struct zfcp_ls_adisc_acc *adisc;
-
- send_els = (struct zfcp_send_els *) data;
- adapter = send_els->adapter;
- port = send_els->port;
- d_id = send_els->d_id;
-
- /* request rejected or timed out */
- if (send_els->status != 0) {
- ZFCP_LOG_NORMAL("ELS request rejected/timed out, "
- "force physical port reopen "
- "(adapter %s, port d_id=0x%06x)\n",
- zfcp_get_busid_by_adapter(adapter), d_id);
- if (zfcp_erp_port_forced_reopen(port, 0, 63, NULL))
- ZFCP_LOG_NORMAL("failed reopen of port "
- "(adapter %s, wwpn=0x%016Lx)\n",
- zfcp_get_busid_by_port(port),
- port->wwpn);
- goto out;
- }
-
- adisc = zfcp_sg_to_address(send_els->resp);
-
- ZFCP_LOG_INFO("ADISC response from d_id 0x%06x to s_id "
- "0x%06x (wwpn=0x%016Lx, wwnn=0x%016Lx, "
- "hard_nport_id=0x%06x, nport_id=0x%06x)\n",
- d_id, fc_host_port_id(adapter->scsi_host),
- (wwn_t) adisc->wwpn, (wwn_t) adisc->wwnn,
- adisc->hard_nport_id, adisc->nport_id);
-
- /* set wwnn for port */
- if (port->wwnn == 0)
- port->wwnn = adisc->wwnn;
-
- if (port->wwpn != adisc->wwpn) {
- ZFCP_LOG_NORMAL("d_id assignment changed, reopening "
- "port (adapter %s, wwpn=0x%016Lx, "
- "adisc_resp_wwpn=0x%016Lx)\n",
- zfcp_get_busid_by_port(port),
- port->wwpn, (wwn_t) adisc->wwpn);
- if (zfcp_erp_port_reopen(port, 0, 64, NULL))
- ZFCP_LOG_NORMAL("failed reopen of port "
- "(adapter %s, wwpn=0x%016Lx)\n",
- zfcp_get_busid_by_port(port),
- port->wwpn);
- }
+ unsigned long flags;
- out:
- zfcp_port_put(port);
- __free_pages(sg_page(send_els->req), 0);
- kfree(send_els->req);
- kfree(send_els->resp);
- kfree(send_els);
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+ _zfcp_erp_adapter_reopen(adapter, clear, id, ref);
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
-
/**
- * zfcp_test_link - lightweight link test procedure
- * @port: port to be tested
- *
- * Test status of a link to a remote port using the ELS command ADISC.
+ * zfcp_erp_adapter_shutdown - Shutdown adapter.
+ * @adapter: Adapter to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
*/
-int
-zfcp_test_link(struct zfcp_port *port)
+void zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear,
+ u8 id, void *ref)
{
- int retval;
-
- zfcp_port_get(port);
- retval = zfcp_erp_adisc(port);
- if (retval != 0 && retval != -EBUSY) {
- zfcp_port_put(port);
- ZFCP_LOG_NORMAL("reopen needed for port 0x%016Lx "
- "on adapter %s\n ", port->wwpn,
- zfcp_get_busid_by_port(port));
- retval = zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
- if (retval != 0) {
- ZFCP_LOG_NORMAL("reopen of remote port 0x%016Lx "
- "on adapter %s failed\n", port->wwpn,
- zfcp_get_busid_by_port(port));
- retval = -EPERM;
- }
- }
-
- return retval;
+ int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+ zfcp_erp_adapter_reopen(adapter, clear | flags, id, ref);
}
-
-/*
- * function:
- *
- * purpose: called if a port failed to be opened normally
- * initiates Forced Reopen recovery which is done
- * asynchronously
- *
- * returns: 0 - initiated action successfully
- * <0 - failed to initiate action
+/**
+ * zfcp_erp_port_shutdown - Shutdown port
+ * @port: Port to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
*/
-static int zfcp_erp_port_forced_reopen_internal(struct zfcp_port *port,
- int clear_mask, u8 id,
- void *ref)
+void zfcp_erp_port_shutdown(struct zfcp_port *port, int clear, u8 id, void *ref)
{
- int retval;
+ int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+ zfcp_erp_port_reopen(port, clear | flags, id, ref);
+}
- ZFCP_LOG_DEBUG("forced reopen of port 0x%016Lx on adapter %s\n",
- port->wwpn, zfcp_get_busid_by_port(port));
+/**
+ * zfcp_erp_unit_shutdown - Shutdown unit
+ * @unit: Unit to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
+ */
+void zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear, u8 id, void *ref)
+{
+ int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+ zfcp_erp_unit_reopen(unit, clear | flags, id, ref);
+}
- zfcp_erp_port_block(port, clear_mask);
+static void zfcp_erp_port_block(struct zfcp_port *port, int clear)
+{
+ zfcp_erp_modify_port_status(port, 17, NULL,
+ ZFCP_STATUS_COMMON_UNBLOCKED | clear,
+ ZFCP_CLEAR);
+}
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
- ZFCP_LOG_DEBUG("skipped forced reopen of failed port 0x%016Lx "
- "on adapter %s\n", port->wwpn,
- zfcp_get_busid_by_port(port));
- retval = -EIO;
- goto out;
- }
+static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port,
+ int clear, u8 id, void *ref)
+{
+ zfcp_erp_port_block(port, clear);
- retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
- port->adapter, port, NULL, id, ref);
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+ return;
- out:
- return retval;
+ zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
+ port->adapter, port, NULL, id, ref);
}
-/*
- * function:
- *
- * purpose: Wrappper for zfcp_erp_port_forced_reopen_internal
- * used to ensure the correct locking
- *
- * returns: 0 - initiated action successfully
- * <0 - failed to initiate action
+/**
+ * zfcp_erp_port_forced_reopen - Forced close of port and open again
+ * @port: Port to force close and to reopen.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
*/
-int zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear_mask, u8 id,
- void *ref)
+void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, u8 id,
+ void *ref)
{
- int retval;
unsigned long flags;
- struct zfcp_adapter *adapter;
+ struct zfcp_adapter *adapter = port->adapter;
- adapter = port->adapter;
read_lock_irqsave(&zfcp_data.config_lock, flags);
write_lock(&adapter->erp_lock);
- retval = zfcp_erp_port_forced_reopen_internal(port, clear_mask, id,
- ref);
+ _zfcp_erp_port_forced_reopen(port, clear, id, ref);
write_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
- return retval;
}
-/*
- * function:
- *
- * purpose: called if a port is to be opened
- * initiates Reopen recovery which is done
- * asynchronously
- *
- * returns: 0 - initiated action successfully
- * <0 - failed to initiate action
- */
-static int zfcp_erp_port_reopen_internal(struct zfcp_port *port, int clear_mask,
- u8 id, void *ref)
+static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, u8 id,
+ void *ref)
{
- int retval;
+ zfcp_erp_port_block(port, clear);
- ZFCP_LOG_DEBUG("reopen of port 0x%016Lx on adapter %s\n",
- port->wwpn, zfcp_get_busid_by_port(port));
-
- zfcp_erp_port_block(port, clear_mask);
-
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
- ZFCP_LOG_DEBUG("skipped reopen of failed port 0x%016Lx "
- "on adapter %s\n", port->wwpn,
- zfcp_get_busid_by_port(port));
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
/* ensure propagation of failed status to new devices */
zfcp_erp_port_failed(port, 14, NULL);
- retval = -EIO;
- goto out;
+ return -EIO;
}
- retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT,
- port->adapter, port, NULL, id, ref);
-
- out:
- return retval;
+ return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT,
+ port->adapter, port, NULL, id, ref);
}
/**
- * zfcp_erp_port_reopen - initiate reopen of a remote port
- * @port: port to be reopened
- * @clear_mask: specifies flags in port status to be cleared
- * Return: 0 on success, < 0 on error
+ * zfcp_erp_port_reopen - trigger remote port recovery
+ * @port: port to recover
+ * @clear_mask: flags in port status to be cleared
*
- * This is a wrappper function for zfcp_erp_port_reopen_internal. It ensures
- * correct locking. An error recovery task is initiated to do the reopen.
- * To wait for the completion of the reopen zfcp_erp_wait should be used.
+ * Returns 0 if recovery has been triggered, < 0 if not.
*/
-int zfcp_erp_port_reopen(struct zfcp_port *port, int clear_mask, u8 id,
- void *ref)
+int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, u8 id, void *ref)
{
- int retval;
unsigned long flags;
+ int retval;
struct zfcp_adapter *adapter = port->adapter;
read_lock_irqsave(&zfcp_data.config_lock, flags);
write_lock(&adapter->erp_lock);
- retval = zfcp_erp_port_reopen_internal(port, clear_mask, id, ref);
+ retval = _zfcp_erp_port_reopen(port, clear, id, ref);
write_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
return retval;
}
-/*
- * function:
- *
- * purpose: called if a unit is to be opened
- * initiates Reopen recovery which is done
- * asynchronously
- *
- * returns: 0 - initiated action successfully
- * <0 - failed to initiate action
- */
-static int zfcp_erp_unit_reopen_internal(struct zfcp_unit *unit, int clear_mask,
- u8 id, void *ref)
+static void zfcp_erp_unit_block(struct zfcp_unit *unit, int clear_mask)
{
- int retval;
- struct zfcp_adapter *adapter = unit->port->adapter;
+ zfcp_erp_modify_unit_status(unit, 19, NULL,
+ ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
+ ZFCP_CLEAR);
+}
- ZFCP_LOG_DEBUG("reopen of unit 0x%016Lx on port 0x%016Lx "
- "on adapter %s\n", unit->fcp_lun,
- unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+static void _zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, u8 id,
+ void *ref)
+{
+ struct zfcp_adapter *adapter = unit->port->adapter;
- zfcp_erp_unit_block(unit, clear_mask);
+ zfcp_erp_unit_block(unit, clear);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
- ZFCP_LOG_DEBUG("skipped reopen of failed unit 0x%016Lx "
- "on port 0x%016Lx on adapter %s\n",
- unit->fcp_lun, unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- retval = -EIO;
- goto out;
- }
+ if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+ return;
- retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_UNIT,
- adapter, unit->port, unit, id, ref);
- out:
- return retval;
+ zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_UNIT,
+ adapter, unit->port, unit, id, ref);
}
/**
@@ -643,987 +407,181 @@ static int zfcp_erp_unit_reopen_internal(struct zfcp_unit *unit, int clear_mask,
* @unit: unit to be reopened
* @clear_mask: specifies flags in unit status to be cleared
* Return: 0 on success, < 0 on error
- *
- * This is a wrappper for zfcp_erp_unit_reopen_internal. It ensures correct
- * locking. An error recovery task is initiated to do the reopen.
- * To wait for the completion of the reopen zfcp_erp_wait should be used.
*/
-int zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear_mask, u8 id,
- void *ref)
+void zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, u8 id, void *ref)
{
- int retval;
unsigned long flags;
- struct zfcp_adapter *adapter;
- struct zfcp_port *port;
-
- port = unit->port;
- adapter = port->adapter;
+ struct zfcp_port *port = unit->port;
+ struct zfcp_adapter *adapter = port->adapter;
read_lock_irqsave(&zfcp_data.config_lock, flags);
write_lock(&adapter->erp_lock);
- retval = zfcp_erp_unit_reopen_internal(unit, clear_mask, id, ref);
+ _zfcp_erp_unit_reopen(unit, clear, id, ref);
write_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
- return retval;
}
-/**
- * zfcp_erp_adapter_block - mark adapter as blocked, block scsi requests
- */
-static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int clear_mask)
+static int status_change_set(unsigned long mask, atomic_t *status)
{
- zfcp_erp_modify_adapter_status(adapter, 15, NULL,
- ZFCP_STATUS_COMMON_UNBLOCKED |
- clear_mask, ZFCP_CLEAR);
+ return (atomic_read(status) ^ mask) & mask;
}
-/* FIXME: isn't really atomic */
-/*
- * returns the mask which has not been set so far, i.e.
- * 0 if no bit has been changed, !0 if some bit has been changed
- */
-static int atomic_test_and_set_mask(unsigned long mask, atomic_t *v)
+static int status_change_clear(unsigned long mask, atomic_t *status)
{
- int changed_bits = (atomic_read(v) /*XOR*/^ mask) & mask;
- atomic_set_mask(mask, v);
- return changed_bits;
+ return atomic_read(status) & mask;
}
-/* FIXME: isn't really atomic */
-/*
- * returns the mask which has not been cleared so far, i.e.
- * 0 if no bit has been changed, !0 if some bit has been changed
- */
-static int atomic_test_and_clear_mask(unsigned long mask, atomic_t *v)
-{
- int changed_bits = atomic_read(v) & mask;
- atomic_clear_mask(mask, v);
- return changed_bits;
-}
-
-/**
- * zfcp_erp_adapter_unblock - mark adapter as unblocked, allow scsi requests
- */
static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
{
- if (atomic_test_and_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &adapter->status))
+ if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status))
zfcp_rec_dbf_event_adapter(16, NULL, adapter);
+ atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
}
-/*
- * function:
- *
- * purpose: disable I/O,
- * return any open requests and clean them up,
- * aim: no pending and incoming I/O
- *
- * returns:
- */
-static void
-zfcp_erp_port_block(struct zfcp_port *port, int clear_mask)
-{
- zfcp_erp_modify_port_status(port, 17, NULL,
- ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
- ZFCP_CLEAR);
-}
-
-/*
- * function:
- *
- * purpose: enable I/O
- *
- * returns:
- */
-static void
-zfcp_erp_port_unblock(struct zfcp_port *port)
+static void zfcp_erp_port_unblock(struct zfcp_port *port)
{
- if (atomic_test_and_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &port->status))
+ if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status))
zfcp_rec_dbf_event_port(18, NULL, port);
+ atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
}
-/*
- * function:
- *
- * purpose: disable I/O,
- * return any open requests and clean them up,
- * aim: no pending and incoming I/O
- *
- * returns:
- */
-static void
-zfcp_erp_unit_block(struct zfcp_unit *unit, int clear_mask)
-{
- zfcp_erp_modify_unit_status(unit, 19, NULL,
- ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
- ZFCP_CLEAR);
-}
-
-/*
- * function:
- *
- * purpose: enable I/O
- *
- * returns:
- */
-static void
-zfcp_erp_unit_unblock(struct zfcp_unit *unit)
+static void zfcp_erp_unit_unblock(struct zfcp_unit *unit)
{
- if (atomic_test_and_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &unit->status))
+ if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status))
zfcp_rec_dbf_event_unit(20, NULL, unit);
+ atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status);
}
-static void
-zfcp_erp_action_ready(struct zfcp_erp_action *erp_action)
+static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
{
- struct zfcp_adapter *adapter = erp_action->adapter;
-
- zfcp_erp_action_to_ready(erp_action);
- up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(2, adapter, 0);
+ list_move(&erp_action->list, &erp_action->adapter->erp_running_head);
+ zfcp_rec_dbf_event_action(145, erp_action);
}
-/*
- * function:
- *
- * purpose:
- *
- * returns: <0 erp_action not found in any list
- * ZFCP_ERP_ACTION_READY erp_action is in ready list
- * ZFCP_ERP_ACTION_RUNNING erp_action is in running list
- *
- * locks: erp_lock must be held
- */
-static int
-zfcp_erp_action_exists(struct zfcp_erp_action *erp_action)
+static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act)
{
- int retval = -EINVAL;
- struct list_head *entry;
- struct zfcp_erp_action *entry_erp_action;
- struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_adapter *adapter = act->adapter;
- /* search in running list */
- list_for_each(entry, &adapter->erp_running_head) {
- entry_erp_action =
- list_entry(entry, struct zfcp_erp_action, list);
- if (entry_erp_action == erp_action) {
- retval = ZFCP_ERP_ACTION_RUNNING;
- goto out;
- }
- }
- /* search in ready list */
- list_for_each(entry, &adapter->erp_ready_head) {
- entry_erp_action =
- list_entry(entry, struct zfcp_erp_action, list);
- if (entry_erp_action == erp_action) {
- retval = ZFCP_ERP_ACTION_READY;
- goto out;
- }
- }
-
- out:
- return retval;
-}
-
-/*
- * purpose: checks current status of action (timed out, dismissed, ...)
- * and does appropriate preparations (dismiss fsf request, ...)
- *
- * locks: called under erp_lock (disabled interrupts)
- */
-static void
-zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action)
-{
- struct zfcp_adapter *adapter = erp_action->adapter;
+ if (!act->fsf_req)
+ return;
- if (erp_action->fsf_req) {
- /* take lock to ensure that request is not deleted meanwhile */
- spin_lock(&adapter->req_list_lock);
- if (zfcp_reqlist_find_safe(adapter, erp_action->fsf_req) &&
- erp_action->fsf_req->erp_action == erp_action) {
- /* fsf_req still exists */
- /* dismiss fsf_req of timed out/dismissed erp_action */
- if (erp_action->status & (ZFCP_STATUS_ERP_DISMISSED |
- ZFCP_STATUS_ERP_TIMEDOUT)) {
- erp_action->fsf_req->status |=
- ZFCP_STATUS_FSFREQ_DISMISSED;
- zfcp_rec_dbf_event_action(142, erp_action);
- }
- if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
- zfcp_rec_dbf_event_action(143, erp_action);
- ZFCP_LOG_NORMAL("error: erp step timed out "
- "(action=%d, fsf_req=%p)\n ",
- erp_action->action,
- erp_action->fsf_req);
- }
- /*
- * If fsf_req is neither dismissed nor completed
- * then keep it running asynchronously and don't mess
- * with the association of erp_action and fsf_req.
- */
- if (erp_action->fsf_req->status &
- (ZFCP_STATUS_FSFREQ_COMPLETED |
- ZFCP_STATUS_FSFREQ_DISMISSED)) {
- /* forget about association between fsf_req
- and erp_action */
- erp_action->fsf_req = NULL;
- }
- } else {
- /*
- * even if this fsf_req has gone, forget about
- * association between erp_action and fsf_req
- */
- erp_action->fsf_req = NULL;
+ spin_lock(&adapter->req_list_lock);
+ if (zfcp_reqlist_find_safe(adapter, act->fsf_req) &&
+ act->fsf_req->erp_action == act) {
+ if (act->status & (ZFCP_STATUS_ERP_DISMISSED |
+ ZFCP_STATUS_ERP_TIMEDOUT)) {
+ act->fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
+ zfcp_rec_dbf_event_action(142, act);
}
- spin_unlock(&adapter->req_list_lock);
- }
-}
-
-/**
- * zfcp_erp_async_handler_nolock - complete erp_action
- *
- * Used for normal completion, time-out, dismissal and failure after
- * low memory condition.
- */
-static void zfcp_erp_async_handler_nolock(struct zfcp_erp_action *erp_action,
- unsigned long set_mask)
-{
- if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) {
- erp_action->status |= set_mask;
- zfcp_erp_action_ready(erp_action);
- } else {
- /* action is ready or gone - nothing to do */
- }
+ if (act->status & ZFCP_STATUS_ERP_TIMEDOUT)
+ zfcp_rec_dbf_event_action(143, act);
+ if (act->fsf_req->status & (ZFCP_STATUS_FSFREQ_COMPLETED |
+ ZFCP_STATUS_FSFREQ_DISMISSED))
+ act->fsf_req = NULL;
+ } else
+ act->fsf_req = NULL;
+ spin_unlock(&adapter->req_list_lock);
}
/**
- * zfcp_erp_async_handler - wrapper for erp_async_handler_nolock w/ locking
+ * zfcp_erp_notify - Trigger ERP action.
+ * @erp_action: ERP action to continue.
+ * @set_mask: ERP action status flags to set.
*/
-void zfcp_erp_async_handler(struct zfcp_erp_action *erp_action,
- unsigned long set_mask)
+void zfcp_erp_notify(struct zfcp_erp_action *erp_action, unsigned long set_mask)
{
struct zfcp_adapter *adapter = erp_action->adapter;
unsigned long flags;
write_lock_irqsave(&adapter->erp_lock, flags);
- zfcp_erp_async_handler_nolock(erp_action, set_mask);
- write_unlock_irqrestore(&adapter->erp_lock, flags);
-}
-
-/*
- * purpose: is called for erp_action which was slept waiting for
- * memory becoming avaliable,
- * will trigger that this action will be continued
- */
-static void
-zfcp_erp_memwait_handler(unsigned long data)
-{
- struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data;
-
- zfcp_erp_async_handler(erp_action, 0);
-}
-
-/*
- * purpose: is called if an asynchronous erp step timed out,
- * action gets an appropriate flag and will be processed
- * accordingly
- */
-static void zfcp_erp_timeout_handler(unsigned long data)
-{
- struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data;
-
- zfcp_erp_async_handler(erp_action, ZFCP_STATUS_ERP_TIMEDOUT);
-}
-
-/**
- * zfcp_erp_action_dismiss - dismiss an erp_action
- *
- * adapter->erp_lock must be held
- *
- * Dismissal of an erp_action is usually required if an erp_action of
- * higher priority is generated.
- */
-static void zfcp_erp_action_dismiss(struct zfcp_erp_action *erp_action)
-{
- erp_action->status |= ZFCP_STATUS_ERP_DISMISSED;
- if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING)
+ if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) {
+ erp_action->status |= set_mask;
zfcp_erp_action_ready(erp_action);
-}
-
-int
-zfcp_erp_thread_setup(struct zfcp_adapter *adapter)
-{
- int retval = 0;
-
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
-
- retval = kernel_thread(zfcp_erp_thread, adapter, SIGCHLD);
- if (retval < 0) {
- ZFCP_LOG_NORMAL("error: creation of erp thread failed for "
- "adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- } else {
- wait_event(adapter->erp_thread_wqh,
- atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
- &adapter->status));
- }
-
- return (retval < 0);
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:
- *
- * context: process (i.e. proc-fs or rmmod/insmod)
- *
- * note: The caller of this routine ensures that the specified
- * adapter has been shut down and that this operation
- * has been completed. Thus, there are no pending erp_actions
- * which would need to be handled here.
- */
-int
-zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
-{
- int retval = 0;
-
- atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
- up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(2, adapter, 1);
-
- wait_event(adapter->erp_thread_wqh,
- !atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
- &adapter->status));
-
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
- &adapter->status);
-
- return retval;
-}
-
-/*
- * purpose: is run as a kernel thread,
- * goes through list of error recovery actions of associated adapter
- * and delegates single action to execution
- *
- * returns: 0
- */
-static int
-zfcp_erp_thread(void *data)
-{
- struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
- struct list_head *next;
- struct zfcp_erp_action *erp_action;
- unsigned long flags;
-
- daemonize("zfcperp%s", zfcp_get_busid_by_adapter(adapter));
- /* Block all signals */
- siginitsetinv(&current->blocked, 0);
- atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
- wake_up(&adapter->erp_thread_wqh);
-
- while (!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
- &adapter->status)) {
-
- write_lock_irqsave(&adapter->erp_lock, flags);
- next = adapter->erp_ready_head.next;
- write_unlock_irqrestore(&adapter->erp_lock, flags);
-
- if (next != &adapter->erp_ready_head) {
- erp_action =
- list_entry(next, struct zfcp_erp_action, list);
- /*
- * process action (incl. [re]moving it
- * from 'ready' queue)
- */
- zfcp_erp_strategy(erp_action);
- }
-
- /*
- * sleep as long as there is nothing to do, i.e.
- * no action in 'ready' queue to be processed and
- * thread is not to be killed
- */
- zfcp_rec_dbf_event_thread(4, adapter, 1);
- down_interruptible(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(5, adapter, 1);
}
-
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
- wake_up(&adapter->erp_thread_wqh);
-
- return 0;
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
}
-/*
- * function:
- *
- * purpose: drives single error recovery action and schedules higher and
- * subordinate actions, if necessary
- *
- * returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
- * ZFCP_ERP_SUCCEEDED - action finished successfully (deqd)
- * ZFCP_ERP_FAILED - action finished unsuccessfully (deqd)
- * ZFCP_ERP_EXIT - action finished (dequeued), offline
- * ZFCP_ERP_DISMISSED - action canceled (dequeued)
+/**
+ * zfcp_erp_timeout_handler - Trigger ERP action from timed out ERP request
+ * @data: ERP action (from timer data)
*/
-static int
-zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
+void zfcp_erp_timeout_handler(unsigned long data)
{
- int retval = 0;
- struct zfcp_adapter *adapter = erp_action->adapter;
- struct zfcp_port *port = erp_action->port;
- struct zfcp_unit *unit = erp_action->unit;
- int action = erp_action->action;
- u32 status = erp_action->status;
- unsigned long flags;
-
- /* serialise dismissing, timing out, moving, enqueueing */
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- write_lock(&adapter->erp_lock);
-
- /* dequeue dismissed action and leave, if required */
- retval = zfcp_erp_strategy_check_action(erp_action, retval);
- if (retval == ZFCP_ERP_DISMISSED) {
- goto unlock;
- }
-
- /*
- * move action to 'running' queue before processing it
- * (to avoid a race condition regarding moving the
- * action to the 'running' queue and back)
- */
- zfcp_erp_action_to_running(erp_action);
-
- /*
- * try to process action as far as possible,
- * no lock to allow for blocking operations (kmalloc, qdio, ...),
- * afterwards the lock is required again for the following reasons:
- * - dequeueing of finished action and enqueueing of
- * follow-up actions must be atomic so that any other
- * reopen-routine does not believe there is nothing to do
- * and that it is safe to enqueue something else,
- * - we want to force any control thread which is dismissing
- * actions to finish this before we decide about
- * necessary steps to be taken here further
- */
- write_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
- retval = zfcp_erp_strategy_do_action(erp_action);
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- write_lock(&adapter->erp_lock);
-
- /*
- * check for dismissed status again to avoid follow-up actions,
- * failing of targets and so on for dismissed actions,
- * we go through down() here because there has been an up()
- */
- if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED)
- retval = ZFCP_ERP_CONTINUES;
-
- switch (retval) {
- case ZFCP_ERP_NOMEM:
- /* no memory to continue immediately, let it sleep */
- if (!(erp_action->status & ZFCP_STATUS_ERP_LOWMEM)) {
- ++adapter->erp_low_mem_count;
- erp_action->status |= ZFCP_STATUS_ERP_LOWMEM;
- }
- /* This condition is true if there is no memory available
- for any erp_action on this adapter. This implies that there
- are no elements in the memory pool(s) left for erp_actions.
- This might happen if an erp_action that used a memory pool
- element was timed out.
- */
- if (adapter->erp_total_count == adapter->erp_low_mem_count) {
- ZFCP_LOG_NORMAL("error: no mempool elements available, "
- "restarting I/O on adapter %s "
- "to free mempool\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_erp_adapter_reopen_internal(adapter, 0, 66, NULL);
- } else {
- retval = zfcp_erp_strategy_memwait(erp_action);
- }
- goto unlock;
- case ZFCP_ERP_CONTINUES:
- /* leave since this action runs asynchronously */
- if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) {
- --adapter->erp_low_mem_count;
- erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM;
- }
- goto unlock;
- }
- /* ok, finished action (whatever its result is) */
-
- /* check for unrecoverable targets */
- retval = zfcp_erp_strategy_check_target(erp_action, retval);
-
- /* action must be dequeued (here to allow for further ones) */
- zfcp_erp_action_dequeue(erp_action);
-
- /*
- * put this target through the erp mill again if someone has
- * requested to change the status of a target being online
- * to offline or the other way around
- * (old retval is preserved if nothing has to be done here)
- */
- retval = zfcp_erp_strategy_statechange(action, status, adapter,
- port, unit, retval);
-
- /*
- * leave if target is in permanent error state or if
- * action is repeated in order to process state change
- */
- if (retval == ZFCP_ERP_EXIT) {
- goto unlock;
- }
-
- /* trigger follow up actions */
- zfcp_erp_strategy_followup_actions(action, adapter, port, unit, retval);
-
- unlock:
- write_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
- if (retval != ZFCP_ERP_CONTINUES)
- zfcp_erp_action_cleanup(action, adapter, port, unit, retval);
-
- /*
- * a few tasks remain when the erp queues are empty
- * (don't do that if the last action evaluated was dismissed
- * since this clearly indicates that there is more to come) :
- * - close the name server port if it is open yet
- * (enqueues another [probably] final action)
- * - otherwise, wake up whoever wants to be woken when we are
- * done with erp
- */
- if (retval != ZFCP_ERP_DISMISSED)
- zfcp_erp_strategy_check_queues(adapter);
-
- return retval;
+ struct zfcp_erp_action *act = (struct zfcp_erp_action *) data;
+ zfcp_erp_notify(act, ZFCP_STATUS_ERP_TIMEDOUT);
}
-/*
- * function:
- *
- * purpose:
- *
- * returns: ZFCP_ERP_DISMISSED - if action has been dismissed
- * retval - otherwise
- */
-static int
-zfcp_erp_strategy_check_action(struct zfcp_erp_action *erp_action, int retval)
+static void zfcp_erp_memwait_handler(unsigned long data)
{
- zfcp_erp_strategy_check_fsfreq(erp_action);
-
- if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) {
- zfcp_erp_action_dequeue(erp_action);
- retval = ZFCP_ERP_DISMISSED;
- }
-
- return retval;
+ zfcp_erp_notify((struct zfcp_erp_action *)data, 0);
}
-static int
-zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
+static void zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action)
{
- int retval = ZFCP_ERP_FAILED;
-
- /*
- * try to execute/continue action as far as possible,
- * note: no lock in subsequent strategy routines
- * (this allows these routine to call schedule, e.g.
- * kmalloc with such flags or qdio_initialize & friends)
- * Note: in case of timeout, the separate strategies will fail
- * anyhow. No need for a special action. Even worse, a nameserver
- * failure would not wake up waiting ports without the call.
- */
- switch (erp_action->action) {
-
- case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- retval = zfcp_erp_adapter_strategy(erp_action);
- break;
-
- case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
- retval = zfcp_erp_port_forced_strategy(erp_action);
- break;
-
- case ZFCP_ERP_ACTION_REOPEN_PORT:
- retval = zfcp_erp_port_strategy(erp_action);
- break;
-
- case ZFCP_ERP_ACTION_REOPEN_UNIT:
- retval = zfcp_erp_unit_strategy(erp_action);
- break;
-
- default:
- ZFCP_LOG_NORMAL("bug: unknown erp action requested on "
- "adapter %s (action=%d)\n",
- zfcp_get_busid_by_adapter(erp_action->adapter),
- erp_action->action);
- }
-
- return retval;
-}
-
-/*
- * function:
- *
- * purpose: triggers retry of this action after a certain amount of time
- * by means of timer provided by erp_action
- *
- * returns: ZFCP_ERP_CONTINUES - erp_action sleeps in erp running queue
- */
-static int
-zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action)
-{
- int retval = ZFCP_ERP_CONTINUES;
-
init_timer(&erp_action->timer);
erp_action->timer.function = zfcp_erp_memwait_handler;
erp_action->timer.data = (unsigned long) erp_action;
- erp_action->timer.expires = jiffies + ZFCP_ERP_MEMWAIT_TIMEOUT;
+ erp_action->timer.expires = jiffies + HZ;
add_timer(&erp_action->timer);
-
- return retval;
-}
-
-/*
- * function: zfcp_erp_adapter_failed
- *
- * purpose: sets the adapter and all underlying devices to ERP_FAILED
- *
- */
-void
-zfcp_erp_adapter_failed(struct zfcp_adapter *adapter, u8 id, void *ref)
-{
- zfcp_erp_modify_adapter_status(adapter, id, ref,
- ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
- ZFCP_LOG_NORMAL("adapter erp failed on adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
-}
-
-/*
- * function: zfcp_erp_port_failed
- *
- * purpose: sets the port and all underlying devices to ERP_FAILED
- *
- */
-void
-zfcp_erp_port_failed(struct zfcp_port *port, u8 id, void *ref)
-{
- zfcp_erp_modify_port_status(port, id, ref,
- ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
-
- if (atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
- ZFCP_LOG_NORMAL("port erp failed (adapter %s, "
- "port d_id=0x%06x)\n",
- zfcp_get_busid_by_port(port), port->d_id);
- else
- ZFCP_LOG_NORMAL("port erp failed (adapter %s, wwpn=0x%016Lx)\n",
- zfcp_get_busid_by_port(port), port->wwpn);
-}
-
-/*
- * function: zfcp_erp_unit_failed
- *
- * purpose: sets the unit to ERP_FAILED
- *
- */
-void
-zfcp_erp_unit_failed(struct zfcp_unit *unit, u8 id, void *ref)
-{
- zfcp_erp_modify_unit_status(unit, id, ref,
- ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
-
- ZFCP_LOG_NORMAL("unit erp failed on unit 0x%016Lx on port 0x%016Lx "
- " on adapter %s\n", unit->fcp_lun,
- unit->port->wwpn, zfcp_get_busid_by_unit(unit));
-}
-
-/*
- * function: zfcp_erp_strategy_check_target
- *
- * purpose: increments the erp action count on the device currently in
- * recovery if the action failed or resets the count in case of
- * success. If a maximum count is exceeded the device is marked
- * as ERP_FAILED.
- * The 'blocked' state of a target which has been recovered
- * successfully is reset.
- *
- * returns: ZFCP_ERP_CONTINUES - action continues (not considered)
- * ZFCP_ERP_SUCCEEDED - action finished successfully
- * ZFCP_ERP_EXIT - action failed and will not continue
- */
-static int
-zfcp_erp_strategy_check_target(struct zfcp_erp_action *erp_action, int result)
-{
- struct zfcp_adapter *adapter = erp_action->adapter;
- struct zfcp_port *port = erp_action->port;
- struct zfcp_unit *unit = erp_action->unit;
-
- switch (erp_action->action) {
-
- case ZFCP_ERP_ACTION_REOPEN_UNIT:
- result = zfcp_erp_strategy_check_unit(unit, result);
- break;
-
- case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
- case ZFCP_ERP_ACTION_REOPEN_PORT:
- result = zfcp_erp_strategy_check_port(port, result);
- break;
-
- case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- result = zfcp_erp_strategy_check_adapter(adapter, result);
- break;
- }
-
- return result;
-}
-
-static int
-zfcp_erp_strategy_statechange(int action,
- u32 status,
- struct zfcp_adapter *adapter,
- struct zfcp_port *port,
- struct zfcp_unit *unit, int retval)
-{
- switch (action) {
-
- case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- if (zfcp_erp_strategy_statechange_detected(&adapter->status,
- status)) {
- zfcp_erp_adapter_reopen_internal(adapter,
- ZFCP_STATUS_COMMON_ERP_FAILED,
- 67, NULL);
- retval = ZFCP_ERP_EXIT;
- }
- break;
-
- case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
- case ZFCP_ERP_ACTION_REOPEN_PORT:
- if (zfcp_erp_strategy_statechange_detected(&port->status,
- status)) {
- zfcp_erp_port_reopen_internal(port,
- ZFCP_STATUS_COMMON_ERP_FAILED,
- 68, NULL);
- retval = ZFCP_ERP_EXIT;
- }
- break;
-
- case ZFCP_ERP_ACTION_REOPEN_UNIT:
- if (zfcp_erp_strategy_statechange_detected(&unit->status,
- status)) {
- zfcp_erp_unit_reopen_internal(unit,
- ZFCP_STATUS_COMMON_ERP_FAILED,
- 69, NULL);
- retval = ZFCP_ERP_EXIT;
- }
- break;
- }
-
- return retval;
-}
-
-static int
-zfcp_erp_strategy_statechange_detected(atomic_t * target_status, u32 erp_status)
-{
- return
- /* take it online */
- (atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
- (ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status)) ||
- /* take it offline */
- (!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
- !(ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status));
}
-static int
-zfcp_erp_strategy_check_unit(struct zfcp_unit *unit, int result)
+static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter,
+ int clear, u8 id, void *ref)
{
- switch (result) {
- case ZFCP_ERP_SUCCEEDED :
- atomic_set(&unit->erp_counter, 0);
- zfcp_erp_unit_unblock(unit);
- break;
- case ZFCP_ERP_FAILED :
- atomic_inc(&unit->erp_counter);
- if (atomic_read(&unit->erp_counter) > ZFCP_MAX_ERPS)
- zfcp_erp_unit_failed(unit, 21, NULL);
- break;
- case ZFCP_ERP_EXIT :
- /* nothing */
- break;
- }
-
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
- zfcp_erp_unit_block(unit, 0); /* for ZFCP_ERP_SUCCEEDED */
- result = ZFCP_ERP_EXIT;
- }
-
- return result;
-}
-
-static int
-zfcp_erp_strategy_check_port(struct zfcp_port *port, int result)
-{
- switch (result) {
- case ZFCP_ERP_SUCCEEDED :
- atomic_set(&port->erp_counter, 0);
- zfcp_erp_port_unblock(port);
- break;
- case ZFCP_ERP_FAILED :
- atomic_inc(&port->erp_counter);
- if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS)
- zfcp_erp_port_failed(port, 22, NULL);
- break;
- case ZFCP_ERP_EXIT :
- /* nothing */
- break;
- }
-
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
- zfcp_erp_port_block(port, 0); /* for ZFCP_ERP_SUCCEEDED */
- result = ZFCP_ERP_EXIT;
- }
+ struct zfcp_port *port;
- return result;
+ list_for_each_entry(port, &adapter->port_list_head, list)
+ _zfcp_erp_port_reopen(port, clear, id, ref);
}
-static int
-zfcp_erp_strategy_check_adapter(struct zfcp_adapter *adapter, int result)
+static void _zfcp_erp_unit_reopen_all(struct zfcp_port *port, int clear, u8 id,
+ void *ref)
{
- switch (result) {
- case ZFCP_ERP_SUCCEEDED :
- atomic_set(&adapter->erp_counter, 0);
- zfcp_erp_adapter_unblock(adapter);
- break;
- case ZFCP_ERP_FAILED :
- atomic_inc(&adapter->erp_counter);
- if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS)
- zfcp_erp_adapter_failed(adapter, 23, NULL);
- break;
- case ZFCP_ERP_EXIT :
- /* nothing */
- break;
- }
-
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
- zfcp_erp_adapter_block(adapter, 0); /* for ZFCP_ERP_SUCCEEDED */
- result = ZFCP_ERP_EXIT;
- }
-
- return result;
-}
-
-struct zfcp_erp_add_work {
- struct zfcp_unit *unit;
- struct work_struct work;
-};
+ struct zfcp_unit *unit;
-/**
- * zfcp_erp_scsi_scan
- * @data: pointer to a struct zfcp_erp_add_work
- *
- * Registers a logical unit with the SCSI stack.
- */
-static void zfcp_erp_scsi_scan(struct work_struct *work)
-{
- struct zfcp_erp_add_work *p =
- container_of(work, struct zfcp_erp_add_work, work);
- struct zfcp_unit *unit = p->unit;
- struct fc_rport *rport = unit->port->rport;
- scsi_scan_target(&rport->dev, 0, rport->scsi_target_id,
- unit->scsi_lun, 0);
- atomic_clear_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status);
- zfcp_unit_put(unit);
- kfree(p);
+ list_for_each_entry(unit, &port->unit_list_head, list)
+ _zfcp_erp_unit_reopen(unit, clear, id, ref);
}
-/**
- * zfcp_erp_schedule_work
- * @unit: pointer to unit which should be registered with SCSI stack
- *
- * Schedules work which registers a unit with the SCSI stack
- */
-static void
-zfcp_erp_schedule_work(struct zfcp_unit *unit)
+static void zfcp_erp_strategy_followup_actions(struct zfcp_erp_action *act)
{
- struct zfcp_erp_add_work *p;
-
- p = kzalloc(sizeof(*p), GFP_KERNEL);
- if (!p) {
- ZFCP_LOG_NORMAL("error: Out of resources. Could not register "
- "the FCP-LUN 0x%Lx connected to "
- "the port with WWPN 0x%Lx connected to "
- "the adapter %s with the SCSI stack.\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- return;
- }
-
- zfcp_unit_get(unit);
- atomic_set_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status);
- INIT_WORK(&p->work, zfcp_erp_scsi_scan);
- p->unit = unit;
- schedule_work(&p->work);
-}
+ struct zfcp_adapter *adapter = act->adapter;
+ struct zfcp_port *port = act->port;
+ struct zfcp_unit *unit = act->unit;
+ u32 status = act->status;
-/*
- * function:
- *
- * purpose: remaining things in good cases,
- * escalation in bad cases
- *
- * returns:
- */
-static int
-zfcp_erp_strategy_followup_actions(int action,
- struct zfcp_adapter *adapter,
- struct zfcp_port *port,
- struct zfcp_unit *unit, int status)
-{
/* initiate follow-up actions depending on success of finished action */
- switch (action) {
+ switch (act->action) {
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
if (status == ZFCP_ERP_SUCCEEDED)
- zfcp_erp_port_reopen_all_internal(adapter, 0, 70, NULL);
+ _zfcp_erp_port_reopen_all(adapter, 0, 70, NULL);
else
- zfcp_erp_adapter_reopen_internal(adapter, 0, 71, NULL);
+ _zfcp_erp_adapter_reopen(adapter, 0, 71, NULL);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
if (status == ZFCP_ERP_SUCCEEDED)
- zfcp_erp_port_reopen_internal(port, 0, 72, NULL);
+ _zfcp_erp_port_reopen(port, 0, 72, NULL);
else
- zfcp_erp_adapter_reopen_internal(adapter, 0, 73, NULL);
+ _zfcp_erp_adapter_reopen(adapter, 0, 73, NULL);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT:
if (status == ZFCP_ERP_SUCCEEDED)
- zfcp_erp_unit_reopen_all_internal(port, 0, 74, NULL);
+ _zfcp_erp_unit_reopen_all(port, 0, 74, NULL);
else
- zfcp_erp_port_forced_reopen_internal(port, 0, 75, NULL);
+ _zfcp_erp_port_forced_reopen(port, 0, 75, NULL);
break;
case ZFCP_ERP_ACTION_REOPEN_UNIT:
- /* Nothing to do if status == ZFCP_ERP_SUCCEEDED */
if (status != ZFCP_ERP_SUCCEEDED)
- zfcp_erp_port_reopen_internal(unit->port, 0, 76, NULL);
+ _zfcp_erp_port_reopen(unit->port, 0, 76, NULL);
break;
}
-
- return 0;
}
-static int
-zfcp_erp_strategy_check_queues(struct zfcp_adapter *adapter)
+static void zfcp_erp_wakeup(struct zfcp_adapter *adapter)
{
unsigned long flags;
@@ -1637,1277 +595,560 @@ zfcp_erp_strategy_check_queues(struct zfcp_adapter *adapter)
}
read_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
- return 0;
}
-/**
- * zfcp_erp_wait - wait for completion of error recovery on an adapter
- * @adapter: adapter for which to wait for completion of its error recovery
- * Return: 0
- */
-int
-zfcp_erp_wait(struct zfcp_adapter *adapter)
+static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *act)
{
- int retval = 0;
-
- wait_event(adapter->erp_done_wqh,
- !atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
- &adapter->status));
-
- return retval;
+ if (zfcp_qdio_open(act->adapter))
+ return ZFCP_ERP_FAILED;
+ init_waitqueue_head(&act->adapter->request_wq);
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &act->adapter->status);
+ return ZFCP_ERP_SUCCEEDED;
}
-void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, u8 id,
- void *ref, u32 mask, int set_or_clear)
+static void zfcp_erp_enqueue_ptp_port(struct zfcp_adapter *adapter)
{
struct zfcp_port *port;
- u32 changed, common_mask = mask & ZFCP_COMMON_FLAGS;
-
- if (set_or_clear == ZFCP_SET) {
- changed = atomic_test_and_set_mask(mask, &adapter->status);
- } else {
- changed = atomic_test_and_clear_mask(mask, &adapter->status);
- if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
- atomic_set(&adapter->erp_counter, 0);
- }
- if (changed)
- zfcp_rec_dbf_event_adapter(id, ref, adapter);
-
- /* Deal with all underlying devices, only pass common_mask */
- if (common_mask)
- list_for_each_entry(port, &adapter->port_list_head, list)
- zfcp_erp_modify_port_status(port, id, ref, common_mask,
- set_or_clear);
+ port = zfcp_port_enqueue(adapter, adapter->peer_wwpn, 0,
+ adapter->peer_d_id);
+ if (IS_ERR(port)) /* error or port already attached */
+ return;
+ _zfcp_erp_port_reopen(port, 0, 150, NULL);
}
-/*
- * function: zfcp_erp_modify_port_status
- *
- * purpose: sets the port and all underlying devices to ERP_FAILED
- *
- */
-void zfcp_erp_modify_port_status(struct zfcp_port *port, u8 id, void *ref,
- u32 mask, int set_or_clear)
+static int zfcp_erp_adapter_strat_fsf_xconf(struct zfcp_erp_action *erp_action)
{
- struct zfcp_unit *unit;
- u32 changed, common_mask = mask & ZFCP_COMMON_FLAGS;
-
- if (set_or_clear == ZFCP_SET) {
- changed = atomic_test_and_set_mask(mask, &port->status);
- } else {
- changed = atomic_test_and_clear_mask(mask, &port->status);
- if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
- atomic_set(&port->erp_counter, 0);
- }
- if (changed)
- zfcp_rec_dbf_event_port(id, ref, port);
-
- /* Modify status of all underlying devices, only pass common mask */
- if (common_mask)
- list_for_each_entry(unit, &port->unit_list_head, list)
- zfcp_erp_modify_unit_status(unit, id, ref, common_mask,
- set_or_clear);
-}
+ int retries;
+ int sleep = 1;
+ struct zfcp_adapter *adapter = erp_action->adapter;
-/*
- * function: zfcp_erp_modify_unit_status
- *
- * purpose: sets the unit to ERP_FAILED
- *
- */
-void zfcp_erp_modify_unit_status(struct zfcp_unit *unit, u8 id, void *ref,
- u32 mask, int set_or_clear)
-{
- u32 changed;
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
- if (set_or_clear == ZFCP_SET) {
- changed = atomic_test_and_set_mask(mask, &unit->status);
- } else {
- changed = atomic_test_and_clear_mask(mask, &unit->status);
- if (mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
- atomic_set(&unit->erp_counter, 0);
+ for (retries = 7; retries; retries--) {
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+ &adapter->status);
+ write_lock_irq(&adapter->erp_lock);
+ zfcp_erp_action_to_running(erp_action);
+ write_unlock_irq(&adapter->erp_lock);
+ if (zfcp_fsf_exchange_config_data(erp_action)) {
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+ &adapter->status);
+ return ZFCP_ERP_FAILED;
}
- }
- if (changed)
- zfcp_rec_dbf_event_unit(id, ref, unit);
-}
-
-/*
- * function:
- *
- * purpose: Wrappper for zfcp_erp_port_reopen_all_internal
- * used to ensure the correct locking
- *
- * returns: 0 - initiated action successfully
- * <0 - failed to initiate action
- */
-int zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter, int clear_mask,
- u8 id, void *ref)
-{
- int retval;
- unsigned long flags;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- write_lock(&adapter->erp_lock);
- retval = zfcp_erp_port_reopen_all_internal(adapter, clear_mask, id,
- ref);
- write_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
- return retval;
-}
+ zfcp_rec_dbf_event_thread_lock(6, adapter);
+ down(&adapter->erp_ready_sem);
+ zfcp_rec_dbf_event_thread_lock(7, adapter);
+ if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT)
+ break;
-static int zfcp_erp_port_reopen_all_internal(struct zfcp_adapter *adapter,
- int clear_mask, u8 id, void *ref)
-{
- int retval = 0;
- struct zfcp_port *port;
+ if (!(atomic_read(&adapter->status) &
+ ZFCP_STATUS_ADAPTER_HOST_CON_INIT))
+ break;
- list_for_each_entry(port, &adapter->port_list_head, list)
- if (!atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
- zfcp_erp_port_reopen_internal(port, clear_mask, id,
- ref);
+ ssleep(sleep);
+ sleep *= 2;
+ }
- return retval;
-}
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+ &adapter->status);
-/*
- * function:
- *
- * purpose:
- *
- * returns: FIXME
- */
-static int zfcp_erp_unit_reopen_all_internal(struct zfcp_port *port,
- int clear_mask, u8 id, void *ref)
-{
- int retval = 0;
- struct zfcp_unit *unit;
+ if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_XCONFIG_OK))
+ return ZFCP_ERP_FAILED;
- list_for_each_entry(unit, &port->unit_list_head, list)
- zfcp_erp_unit_reopen_internal(unit, clear_mask, id, ref);
+ if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP)
+ zfcp_erp_enqueue_ptp_port(adapter);
- return retval;
+ return ZFCP_ERP_SUCCEEDED;
}
-/*
- * function:
- *
- * purpose: this routine executes the 'Reopen Adapter' action
- * (the entire action is processed synchronously, since
- * there are no actions which might be run concurrently
- * per definition)
- *
- * returns: ZFCP_ERP_SUCCEEDED - action finished successfully
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_adapter_strategy(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *act)
{
- int retval;
- struct zfcp_adapter *adapter = erp_action->adapter;
-
- retval = zfcp_erp_adapter_strategy_close(erp_action);
- if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
- retval = ZFCP_ERP_EXIT;
- else
- retval = zfcp_erp_adapter_strategy_open(erp_action);
-
- if (retval == ZFCP_ERP_FAILED) {
- ZFCP_LOG_INFO("Waiting to allow the adapter %s "
- "to recover itself\n",
- zfcp_get_busid_by_adapter(adapter));
- ssleep(ZFCP_TYPE2_RECOVERY_TIME);
- }
+ int ret;
+ struct zfcp_adapter *adapter = act->adapter;
- return retval;
-}
+ write_lock_irq(&adapter->erp_lock);
+ zfcp_erp_action_to_running(act);
+ write_unlock_irq(&adapter->erp_lock);
-/*
- * function:
- *
- * purpose:
- *
- * returns: ZFCP_ERP_SUCCEEDED - action finished successfully
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *erp_action)
-{
- int retval;
+ ret = zfcp_fsf_exchange_port_data(act);
+ if (ret == -EOPNOTSUPP)
+ return ZFCP_ERP_SUCCEEDED;
+ if (ret)
+ return ZFCP_ERP_FAILED;
- atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING,
- &erp_action->adapter->status);
- retval = zfcp_erp_adapter_strategy_generic(erp_action, 1);
- atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING,
- &erp_action->adapter->status);
+ zfcp_rec_dbf_event_thread_lock(8, adapter);
+ down(&adapter->erp_ready_sem);
+ zfcp_rec_dbf_event_thread_lock(9, adapter);
+ if (act->status & ZFCP_STATUS_ERP_TIMEDOUT)
+ return ZFCP_ERP_FAILED;
- return retval;
+ return ZFCP_ERP_SUCCEEDED;
}
-/*
- * function:
- *
- * purpose:
- *
- * returns: ZFCP_ERP_SUCCEEDED - action finished successfully
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *act)
{
- int retval;
+ if (zfcp_erp_adapter_strat_fsf_xconf(act) == ZFCP_ERP_FAILED)
+ return ZFCP_ERP_FAILED;
- atomic_set_mask(ZFCP_STATUS_COMMON_OPENING,
- &erp_action->adapter->status);
- retval = zfcp_erp_adapter_strategy_generic(erp_action, 0);
- atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING,
- &erp_action->adapter->status);
+ if (zfcp_erp_adapter_strategy_open_fsf_xport(act) == ZFCP_ERP_FAILED)
+ return ZFCP_ERP_FAILED;
- return retval;
+ atomic_set(&act->adapter->stat_miss, 16);
+ if (zfcp_status_read_refill(act->adapter))
+ return ZFCP_ERP_FAILED;
+
+ return ZFCP_ERP_SUCCEEDED;
}
-/*
- * function: zfcp_register_adapter
- *
- * purpose: allocate the irq associated with this devno and register
- * the FSF adapter with the SCSI stack
- *
- * returns:
- */
-static int
-zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *erp_action, int close)
+static int zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *act,
+ int close)
{
int retval = ZFCP_ERP_SUCCEEDED;
+ struct zfcp_adapter *adapter = act->adapter;
if (close)
goto close_only;
- retval = zfcp_erp_adapter_strategy_open_qdio(erp_action);
+ retval = zfcp_erp_adapter_strategy_open_qdio(act);
if (retval != ZFCP_ERP_SUCCEEDED)
goto failed_qdio;
- retval = zfcp_erp_adapter_strategy_open_fsf(erp_action);
+ retval = zfcp_erp_adapter_strategy_open_fsf(act);
if (retval != ZFCP_ERP_SUCCEEDED)
goto failed_openfcp;
- atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &erp_action->adapter->status);
- goto out;
+ atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &act->adapter->status);
+ schedule_work(&act->adapter->scan_work);
+
+ return ZFCP_ERP_SUCCEEDED;
close_only:
atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
- &erp_action->adapter->status);
+ &act->adapter->status);
failed_openfcp:
- zfcp_close_fsf(erp_action->adapter);
+ /* close queues to ensure that buffers are not accessed by adapter */
+ zfcp_qdio_close(adapter);
+ zfcp_fsf_req_dismiss_all(adapter);
+ adapter->fsf_req_seq_no = 0;
+ /* all ports and units are closed */
+ zfcp_erp_modify_adapter_status(adapter, 24, NULL,
+ ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
failed_qdio:
atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
- ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
- ZFCP_STATUS_ADAPTER_XPORT_OK,
- &erp_action->adapter->status);
- out:
+ ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
+ &act->adapter->status);
return retval;
}
-/*
- * function: zfcp_qdio_init
- *
- * purpose: setup QDIO operation for specified adapter
- *
- * returns: 0 - successful setup
- * !0 - failed setup
- */
-static int
-zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_adapter_strategy(struct zfcp_erp_action *act)
{
int retval;
- int i;
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_adapter *adapter = erp_action->adapter;
-
- if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
- ZFCP_LOG_NORMAL("bug: second attempt to set up QDIO on "
- "adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- goto failed_sanity;
- }
-
- if (qdio_establish(&adapter->qdio_init_data) != 0) {
- ZFCP_LOG_INFO("error: establishment of QDIO queues failed "
- "on adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- goto failed_qdio_establish;
- }
-
- if (qdio_activate(adapter->ccw_device, 0) != 0) {
- ZFCP_LOG_INFO("error: activation of QDIO queues failed "
- "on adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- goto failed_qdio_activate;
- }
-
- /*
- * put buffers into response queue,
- */
- for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
- sbale = &(adapter->response_queue.buffer[i]->element[0]);
- sbale->length = 0;
- sbale->flags = SBAL_FLAGS_LAST_ENTRY;
- sbale->addr = NULL;
- }
- ZFCP_LOG_TRACE("calling do_QDIO on adapter %s (flags=0x%x, "
- "queue_no=%i, index_in_queue=%i, count=%i)\n",
- zfcp_get_busid_by_adapter(adapter),
- QDIO_FLAG_SYNC_INPUT, 0, 0, QDIO_MAX_BUFFERS_PER_Q);
+ zfcp_erp_adapter_strategy_generic(act, 1); /* close */
+ if (act->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+ return ZFCP_ERP_EXIT;
- retval = do_QDIO(adapter->ccw_device,
- QDIO_FLAG_SYNC_INPUT,
- 0, 0, QDIO_MAX_BUFFERS_PER_Q, NULL);
+ retval = zfcp_erp_adapter_strategy_generic(act, 0); /* open */
- if (retval) {
- ZFCP_LOG_NORMAL("bug: setup of QDIO failed (retval=%d)\n",
- retval);
- goto failed_do_qdio;
- } else {
- adapter->response_queue.free_index = 0;
- atomic_set(&adapter->response_queue.free_count, 0);
- ZFCP_LOG_DEBUG("%i buffers successfully enqueued to "
- "response queue\n", QDIO_MAX_BUFFERS_PER_Q);
- }
- /* set index of first avalable SBALS / number of available SBALS */
- adapter->request_queue.free_index = 0;
- atomic_set(&adapter->request_queue.free_count, QDIO_MAX_BUFFERS_PER_Q);
- adapter->request_queue.distance_from_int = 0;
-
- /* initialize waitqueue used to wait for free SBALs in requests queue */
- init_waitqueue_head(&adapter->request_wq);
-
- /* ok, we did it - skip all cleanups for different failures */
- atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
- retval = ZFCP_ERP_SUCCEEDED;
- goto out;
-
- failed_do_qdio:
- /* NOP */
-
- failed_qdio_activate:
- while (qdio_shutdown(adapter->ccw_device,
- QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS)
- ssleep(1);
-
- failed_qdio_establish:
- failed_sanity:
- retval = ZFCP_ERP_FAILED;
-
- out:
- return retval;
-}
-
-
-static int
-zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *erp_action)
-{
- int retval;
-
- retval = zfcp_erp_adapter_strategy_open_fsf_xconfig(erp_action);
- if (retval == ZFCP_ERP_FAILED)
- return ZFCP_ERP_FAILED;
-
- retval = zfcp_erp_adapter_strategy_open_fsf_xport(erp_action);
if (retval == ZFCP_ERP_FAILED)
- return ZFCP_ERP_FAILED;
-
- return zfcp_erp_adapter_strategy_open_fsf_statusread(erp_action);
-}
-
-static int
-zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *erp_action)
-{
- int retval = ZFCP_ERP_SUCCEEDED;
- int retries;
- int sleep = ZFCP_EXCHANGE_CONFIG_DATA_FIRST_SLEEP;
- struct zfcp_adapter *adapter = erp_action->adapter;
-
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
-
- for (retries = ZFCP_EXCHANGE_CONFIG_DATA_RETRIES; retries; retries--) {
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
- &adapter->status);
- ZFCP_LOG_DEBUG("Doing exchange config data\n");
- write_lock_irq(&adapter->erp_lock);
- zfcp_erp_action_to_running(erp_action);
- write_unlock_irq(&adapter->erp_lock);
- if (zfcp_fsf_exchange_config_data(erp_action)) {
- retval = ZFCP_ERP_FAILED;
- ZFCP_LOG_INFO("error: initiation of exchange of "
- "configuration data failed for "
- "adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
- }
- ZFCP_LOG_DEBUG("Xchange underway\n");
-
- /*
- * Why this works:
- * Both the normal completion handler as well as the timeout
- * handler will do an 'up' when the 'exchange config data'
- * request completes or times out. Thus, the signal to go on
- * won't be lost utilizing this semaphore.
- * Furthermore, this 'adapter_reopen' action is
- * guaranteed to be the only action being there (highest action
- * which prevents other actions from being created).
- * Resulting from that, the wake signal recognized here
- * _must_ be the one belonging to the 'exchange config
- * data' request.
- */
- zfcp_rec_dbf_event_thread(6, adapter, 1);
- down(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(7, adapter, 1);
- if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
- ZFCP_LOG_INFO("error: exchange of configuration data "
- "for adapter %s timed out\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
- }
-
- if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
- &adapter->status))
- break;
-
- ZFCP_LOG_DEBUG("host connection still initialising... "
- "waiting and retrying...\n");
- /* sleep a little bit before retry */
- ssleep(sleep);
- sleep *= 2;
- }
-
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
- &adapter->status);
-
- if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
- &adapter->status)) {
- ZFCP_LOG_INFO("error: exchange of configuration data for "
- "adapter %s failed\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = ZFCP_ERP_FAILED;
- }
+ ssleep(8);
return retval;
}
-static int
-zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *act)
{
- int ret;
- struct zfcp_adapter *adapter;
-
- adapter = erp_action->adapter;
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
-
- write_lock_irq(&adapter->erp_lock);
- zfcp_erp_action_to_running(erp_action);
- write_unlock_irq(&adapter->erp_lock);
+ int retval;
- ret = zfcp_fsf_exchange_port_data(erp_action);
- if (ret == -EOPNOTSUPP) {
- return ZFCP_ERP_SUCCEEDED;
- } else if (ret) {
+ retval = zfcp_fsf_close_physical_port(act);
+ if (retval == -ENOMEM)
+ return ZFCP_ERP_NOMEM;
+ act->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING;
+ if (retval)
return ZFCP_ERP_FAILED;
- }
-
- ret = ZFCP_ERP_SUCCEEDED;
- zfcp_rec_dbf_event_thread(8, adapter, 1);
- down(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(9, adapter, 1);
- if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
- ZFCP_LOG_INFO("error: exchange port data timed out (adapter "
- "%s)\n", zfcp_get_busid_by_adapter(adapter));
- ret = ZFCP_ERP_FAILED;
- }
-
- /* don't treat as error for the sake of compatibility */
- if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status))
- ZFCP_LOG_INFO("warning: exchange port data failed (adapter "
- "%s\n", zfcp_get_busid_by_adapter(adapter));
- return ret;
+ return ZFCP_ERP_CONTINUES;
}
-static int
-zfcp_erp_adapter_strategy_open_fsf_statusread(struct zfcp_erp_action
- *erp_action)
+static void zfcp_erp_port_strategy_clearstati(struct zfcp_port *port)
{
- int retval = ZFCP_ERP_SUCCEEDED;
- int temp_ret;
- struct zfcp_adapter *adapter = erp_action->adapter;
- int i;
-
- adapter->status_read_failed = 0;
- for (i = 0; i < ZFCP_STATUS_READS_RECOM; i++) {
- temp_ret = zfcp_fsf_status_read(adapter, ZFCP_WAIT_FOR_SBAL);
- if (temp_ret < 0) {
- ZFCP_LOG_INFO("error: set-up of unsolicited status "
- "notification failed on adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = ZFCP_ERP_FAILED;
- i--;
- break;
- }
- }
-
- return retval;
+ atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
+ ZFCP_STATUS_PORT_PHYS_CLOSING |
+ ZFCP_STATUS_PORT_INVALID_WWPN,
+ &port->status);
}
-/*
- * function:
- *
- * purpose: this routine executes the 'Reopen Physical Port' action
- *
- * returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
- * ZFCP_ERP_SUCCEEDED - action finished successfully
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action)
{
- int retval = ZFCP_ERP_FAILED;
struct zfcp_port *port = erp_action->port;
+ int status = atomic_read(&port->status);
switch (erp_action->step) {
-
- /*
- * FIXME:
- * the ULP spec. begs for waiting for oustanding commands
- */
case ZFCP_ERP_STEP_UNINITIALIZED:
zfcp_erp_port_strategy_clearstati(port);
- /*
- * it would be sufficient to test only the normal open flag
- * since the phys. open flag cannot be set if the normal
- * open flag is unset - however, this is for readabilty ...
- */
- if (atomic_test_mask((ZFCP_STATUS_PORT_PHYS_OPEN |
- ZFCP_STATUS_COMMON_OPEN),
- &port->status)) {
- ZFCP_LOG_DEBUG("port 0x%016Lx is open -> trying "
- "close physical\n", port->wwpn);
- retval =
- zfcp_erp_port_forced_strategy_close(erp_action);
- } else
- retval = ZFCP_ERP_FAILED;
- break;
+ if ((status & ZFCP_STATUS_PORT_PHYS_OPEN) &&
+ (status & ZFCP_STATUS_COMMON_OPEN))
+ return zfcp_erp_port_forced_strategy_close(erp_action);
+ else
+ return ZFCP_ERP_FAILED;
case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
- if (atomic_test_mask(ZFCP_STATUS_PORT_PHYS_OPEN,
- &port->status)) {
- ZFCP_LOG_DEBUG("close physical failed for port "
- "0x%016Lx\n", port->wwpn);
- retval = ZFCP_ERP_FAILED;
- } else
- retval = ZFCP_ERP_SUCCEEDED;
- break;
+ if (status & ZFCP_STATUS_PORT_PHYS_OPEN)
+ return ZFCP_ERP_SUCCEEDED;
}
+ return ZFCP_ERP_FAILED;
+}
- return retval;
+static int zfcp_erp_port_strategy_close(struct zfcp_erp_action *erp_action)
+{
+ int retval;
+
+ retval = zfcp_fsf_close_port(erp_action);
+ if (retval == -ENOMEM)
+ return ZFCP_ERP_NOMEM;
+ erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING;
+ if (retval)
+ return ZFCP_ERP_FAILED;
+ return ZFCP_ERP_CONTINUES;
}
-/*
- * function:
- *
- * purpose: this routine executes the 'Reopen Port' action
- *
- * returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
- * ZFCP_ERP_SUCCEEDED - action finished successfully
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_port_strategy(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *erp_action)
{
- int retval = ZFCP_ERP_FAILED;
- struct zfcp_port *port = erp_action->port;
+ int retval;
- switch (erp_action->step) {
+ retval = zfcp_fsf_open_port(erp_action);
+ if (retval == -ENOMEM)
+ return ZFCP_ERP_NOMEM;
+ erp_action->step = ZFCP_ERP_STEP_PORT_OPENING;
+ if (retval)
+ return ZFCP_ERP_FAILED;
+ return ZFCP_ERP_CONTINUES;
+}
- /*
- * FIXME:
- * the ULP spec. begs for waiting for oustanding commands
- */
- case ZFCP_ERP_STEP_UNINITIALIZED:
- zfcp_erp_port_strategy_clearstati(port);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
- ZFCP_LOG_DEBUG("port 0x%016Lx is open -> trying "
- "close\n", port->wwpn);
- retval = zfcp_erp_port_strategy_close(erp_action);
- goto out;
- } /* else it's already closed, open it */
- break;
+static int zfcp_erp_open_ptp_port(struct zfcp_erp_action *act)
+{
+ struct zfcp_adapter *adapter = act->adapter;
+ struct zfcp_port *port = act->port;
- case ZFCP_ERP_STEP_PORT_CLOSING:
- if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
- ZFCP_LOG_DEBUG("close failed for port 0x%016Lx\n",
- port->wwpn);
- retval = ZFCP_ERP_FAILED;
- goto out;
- } /* else it's closed now, open it */
- break;
+ if (port->wwpn != adapter->peer_wwpn) {
+ zfcp_erp_port_failed(port, 25, NULL);
+ return ZFCP_ERP_FAILED;
}
- if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
- retval = ZFCP_ERP_EXIT;
- else
- retval = zfcp_erp_port_strategy_open(erp_action);
-
- out:
- return retval;
+ port->d_id = adapter->peer_d_id;
+ atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
+ return zfcp_erp_port_strategy_open_port(act);
}
-static int
-zfcp_erp_port_strategy_open(struct zfcp_erp_action *erp_action)
+void zfcp_erp_port_strategy_open_lookup(struct work_struct *work)
{
int retval;
+ struct zfcp_port *port = container_of(work, struct zfcp_port,
+ gid_pn_work);
- if (atomic_test_mask(ZFCP_STATUS_PORT_WKA,
- &erp_action->port->status))
- retval = zfcp_erp_port_strategy_open_nameserver(erp_action);
- else
- retval = zfcp_erp_port_strategy_open_common(erp_action);
+ retval = zfcp_fc_ns_gid_pn(&port->erp_action);
+ if (retval == -ENOMEM)
+ zfcp_erp_notify(&port->erp_action, ZFCP_ERP_NOMEM);
+ port->erp_action.step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
+ if (retval)
+ zfcp_erp_notify(&port->erp_action, ZFCP_ERP_FAILED);
- return retval;
}
-static int
-zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
{
- int retval = 0;
- struct zfcp_adapter *adapter = erp_action->adapter;
- struct zfcp_port *port = erp_action->port;
-
- switch (erp_action->step) {
+ struct zfcp_adapter *adapter = act->adapter;
+ struct zfcp_port *port = act->port;
+ int p_status = atomic_read(&port->status);
+ switch (act->step) {
case ZFCP_ERP_STEP_UNINITIALIZED:
case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
case ZFCP_ERP_STEP_PORT_CLOSING:
- if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP) {
- if (port->wwpn != adapter->peer_wwpn) {
- ZFCP_LOG_NORMAL("Failed to open port 0x%016Lx "
- "on adapter %s.\nPeer WWPN "
- "0x%016Lx does not match\n",
- port->wwpn,
- zfcp_get_busid_by_adapter(adapter),
- adapter->peer_wwpn);
- zfcp_erp_port_failed(port, 25, NULL);
- retval = ZFCP_ERP_FAILED;
- break;
- }
- port->d_id = adapter->peer_d_id;
- atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
- retval = zfcp_erp_port_strategy_open_port(erp_action);
- break;
- }
- if (!(adapter->nameserver_port)) {
- retval = zfcp_nameserver_enqueue(adapter);
- if (retval != 0) {
- ZFCP_LOG_NORMAL("error: nameserver port "
- "unavailable for adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = ZFCP_ERP_FAILED;
- break;
- }
- }
- if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &adapter->nameserver_port->status)) {
- ZFCP_LOG_DEBUG("nameserver port is not open -> open "
- "nameserver port\n");
- /* nameserver port may live again */
- atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING,
- &adapter->nameserver_port->status);
- if (zfcp_erp_port_reopen(adapter->nameserver_port, 0,
- 77, erp_action) >= 0) {
- erp_action->step =
- ZFCP_ERP_STEP_NAMESERVER_OPEN;
- retval = ZFCP_ERP_CONTINUES;
- } else
- retval = ZFCP_ERP_FAILED;
- break;
+ if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP)
+ return zfcp_erp_open_ptp_port(act);
+ if (!(p_status & ZFCP_STATUS_PORT_DID_DID)) {
+ queue_work(zfcp_data.work_queue, &port->gid_pn_work);
+ return ZFCP_ERP_CONTINUES;
}
- /* else nameserver port is already open, fall through */
- case ZFCP_ERP_STEP_NAMESERVER_OPEN:
- if (!atomic_test_mask(ZFCP_STATUS_COMMON_OPEN,
- &adapter->nameserver_port->status)) {
- ZFCP_LOG_DEBUG("open failed for nameserver port\n");
- retval = ZFCP_ERP_FAILED;
- } else {
- ZFCP_LOG_DEBUG("nameserver port is open -> "
- "nameserver look-up for port 0x%016Lx\n",
- port->wwpn);
- retval = zfcp_erp_port_strategy_open_common_lookup
- (erp_action);
- }
- break;
-
case ZFCP_ERP_STEP_NAMESERVER_LOOKUP:
- if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status)) {
- if (atomic_test_mask
- (ZFCP_STATUS_PORT_INVALID_WWPN, &port->status)) {
- ZFCP_LOG_DEBUG("nameserver look-up failed "
- "for port 0x%016Lx "
- "(misconfigured WWPN?)\n",
- port->wwpn);
+ if (!(p_status & ZFCP_STATUS_PORT_DID_DID)) {
+ if (p_status & (ZFCP_STATUS_PORT_INVALID_WWPN)) {
zfcp_erp_port_failed(port, 26, NULL);
- retval = ZFCP_ERP_EXIT;
- } else {
- ZFCP_LOG_DEBUG("nameserver look-up failed for "
- "port 0x%016Lx\n", port->wwpn);
- retval = ZFCP_ERP_FAILED;
+ return ZFCP_ERP_EXIT;
}
- } else {
- ZFCP_LOG_DEBUG("port 0x%016Lx has d_id=0x%06x -> "
- "trying open\n", port->wwpn, port->d_id);
- retval = zfcp_erp_port_strategy_open_port(erp_action);
+ return ZFCP_ERP_FAILED;
}
- break;
+ return zfcp_erp_port_strategy_open_port(act);
case ZFCP_ERP_STEP_PORT_OPENING:
/* D_ID might have changed during open */
- if (atomic_test_mask((ZFCP_STATUS_COMMON_OPEN |
- ZFCP_STATUS_PORT_DID_DID),
- &port->status)) {
- ZFCP_LOG_DEBUG("port 0x%016Lx is open\n", port->wwpn);
- retval = ZFCP_ERP_SUCCEEDED;
- } else {
- ZFCP_LOG_DEBUG("open failed for port 0x%016Lx\n",
- port->wwpn);
- retval = ZFCP_ERP_FAILED;
+ if (p_status & ZFCP_STATUS_COMMON_OPEN) {
+ if (p_status & ZFCP_STATUS_PORT_DID_DID)
+ return ZFCP_ERP_SUCCEEDED;
+ else {
+ act->step = ZFCP_ERP_STEP_PORT_CLOSING;
+ return ZFCP_ERP_CONTINUES;
+ }
+ /* fall through otherwise */
}
- break;
-
- default:
- ZFCP_LOG_NORMAL("bug: unknown erp step 0x%08x\n",
- erp_action->step);
- retval = ZFCP_ERP_FAILED;
}
-
- return retval;
+ return ZFCP_ERP_FAILED;
}
-static int
-zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_port_strategy(struct zfcp_erp_action *erp_action)
{
- int retval;
struct zfcp_port *port = erp_action->port;
- switch (erp_action->step) {
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC)
+ goto close_init_done;
+ switch (erp_action->step) {
case ZFCP_ERP_STEP_UNINITIALIZED:
- case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
- case ZFCP_ERP_STEP_PORT_CLOSING:
- ZFCP_LOG_DEBUG("port 0x%016Lx has d_id=0x%06x -> trying open\n",
- port->wwpn, port->d_id);
- retval = zfcp_erp_port_strategy_open_port(erp_action);
+ zfcp_erp_port_strategy_clearstati(port);
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)
+ return zfcp_erp_port_strategy_close(erp_action);
break;
- case ZFCP_ERP_STEP_PORT_OPENING:
- if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
- ZFCP_LOG_DEBUG("WKA port is open\n");
- retval = ZFCP_ERP_SUCCEEDED;
- } else {
- ZFCP_LOG_DEBUG("open failed for WKA port\n");
- retval = ZFCP_ERP_FAILED;
- }
- /* this is needed anyway (dont care for retval of wakeup) */
- ZFCP_LOG_DEBUG("continue other open port operations\n");
- zfcp_erp_port_strategy_open_nameserver_wakeup(erp_action);
+ case ZFCP_ERP_STEP_PORT_CLOSING:
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)
+ return ZFCP_ERP_FAILED;
break;
-
- default:
- ZFCP_LOG_NORMAL("bug: unknown erp step 0x%08x\n",
- erp_action->step);
- retval = ZFCP_ERP_FAILED;
- }
-
- return retval;
-}
-
-/*
- * function:
- *
- * purpose: makes the erp thread continue with reopen (physical) port
- * actions which have been paused until the name server port
- * is opened (or failed)
- *
- * returns: 0 (a kind of void retval, its not used)
- */
-static int
-zfcp_erp_port_strategy_open_nameserver_wakeup(struct zfcp_erp_action
- *ns_erp_action)
-{
- int retval = 0;
- unsigned long flags;
- struct zfcp_adapter *adapter = ns_erp_action->adapter;
- struct zfcp_erp_action *erp_action, *tmp;
-
- read_lock_irqsave(&adapter->erp_lock, flags);
- list_for_each_entry_safe(erp_action, tmp, &adapter->erp_running_head,
- list) {
- if (erp_action->step == ZFCP_ERP_STEP_NAMESERVER_OPEN) {
- if (atomic_test_mask(
- ZFCP_STATUS_COMMON_ERP_FAILED,
- &adapter->nameserver_port->status))
- zfcp_erp_port_failed(erp_action->port, 27,
- NULL);
- zfcp_erp_action_ready(erp_action);
- }
}
- read_unlock_irqrestore(&adapter->erp_lock, flags);
-
- return retval;
-}
-/*
- * function:
- *
- * purpose:
- *
- * returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *erp_action)
-{
- int retval;
-
- retval = zfcp_fsf_close_physical_port(erp_action);
- if (retval == -ENOMEM) {
- retval = ZFCP_ERP_NOMEM;
- goto out;
- }
- erp_action->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING;
- if (retval != 0) {
- /* could not send 'open', fail */
- retval = ZFCP_ERP_FAILED;
- goto out;
- }
- retval = ZFCP_ERP_CONTINUES;
- out:
- return retval;
-}
-
-static int
-zfcp_erp_port_strategy_clearstati(struct zfcp_port *port)
-{
- int retval = 0;
+close_init_done:
+ if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+ return ZFCP_ERP_EXIT;
- atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING |
- ZFCP_STATUS_COMMON_CLOSING |
- ZFCP_STATUS_COMMON_ACCESS_DENIED |
- ZFCP_STATUS_PORT_DID_DID |
- ZFCP_STATUS_PORT_PHYS_CLOSING |
- ZFCP_STATUS_PORT_INVALID_WWPN,
- &port->status);
- return retval;
+ return zfcp_erp_port_strategy_open_common(erp_action);
}
-/*
- * function:
- *
- * purpose:
- *
- * returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_port_strategy_close(struct zfcp_erp_action *erp_action)
+static void zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *unit)
{
- int retval;
-
- retval = zfcp_fsf_close_port(erp_action);
- if (retval == -ENOMEM) {
- retval = ZFCP_ERP_NOMEM;
- goto out;
- }
- erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING;
- if (retval != 0) {
- /* could not send 'close', fail */
- retval = ZFCP_ERP_FAILED;
- goto out;
- }
- retval = ZFCP_ERP_CONTINUES;
- out:
- return retval;
+ atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
+ ZFCP_STATUS_UNIT_SHARED |
+ ZFCP_STATUS_UNIT_READONLY,
+ &unit->status);
}
-/*
- * function:
- *
- * purpose:
- *
- * returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_unit_strategy_close(struct zfcp_erp_action *erp_action)
{
- int retval;
-
- retval = zfcp_fsf_open_port(erp_action);
- if (retval == -ENOMEM) {
- retval = ZFCP_ERP_NOMEM;
- goto out;
- }
- erp_action->step = ZFCP_ERP_STEP_PORT_OPENING;
- if (retval != 0) {
- /* could not send 'open', fail */
- retval = ZFCP_ERP_FAILED;
- goto out;
- }
- retval = ZFCP_ERP_CONTINUES;
- out:
- return retval;
+ int retval = zfcp_fsf_close_unit(erp_action);
+ if (retval == -ENOMEM)
+ return ZFCP_ERP_NOMEM;
+ erp_action->step = ZFCP_ERP_STEP_UNIT_CLOSING;
+ if (retval)
+ return ZFCP_ERP_FAILED;
+ return ZFCP_ERP_CONTINUES;
}
-/*
- * function:
- *
- * purpose:
- *
- * returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_port_strategy_open_common_lookup(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_unit_strategy_open(struct zfcp_erp_action *erp_action)
{
- int retval;
-
- retval = zfcp_ns_gid_pn_request(erp_action);
- if (retval == -ENOMEM) {
- retval = ZFCP_ERP_NOMEM;
- goto out;
- }
- erp_action->step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
- if (retval != 0) {
- /* could not send nameserver request, fail */
- retval = ZFCP_ERP_FAILED;
- goto out;
- }
- retval = ZFCP_ERP_CONTINUES;
- out:
- return retval;
+ int retval = zfcp_fsf_open_unit(erp_action);
+ if (retval == -ENOMEM)
+ return ZFCP_ERP_NOMEM;
+ erp_action->step = ZFCP_ERP_STEP_UNIT_OPENING;
+ if (retval)
+ return ZFCP_ERP_FAILED;
+ return ZFCP_ERP_CONTINUES;
}
-/*
- * function:
- *
- * purpose: this routine executes the 'Reopen Unit' action
- * currently no retries
- *
- * returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
- * ZFCP_ERP_SUCCEEDED - action finished successfully
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_unit_strategy(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_unit_strategy(struct zfcp_erp_action *erp_action)
{
- int retval = ZFCP_ERP_FAILED;
struct zfcp_unit *unit = erp_action->unit;
switch (erp_action->step) {
-
- /*
- * FIXME:
- * the ULP spec. begs for waiting for oustanding commands
- */
case ZFCP_ERP_STEP_UNINITIALIZED:
zfcp_erp_unit_strategy_clearstati(unit);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
- ZFCP_LOG_DEBUG("unit 0x%016Lx is open -> "
- "trying close\n", unit->fcp_lun);
- retval = zfcp_erp_unit_strategy_close(erp_action);
- break;
- }
- /* else it's already closed, fall through */
+ if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_OPEN)
+ return zfcp_erp_unit_strategy_close(erp_action);
+ /* already closed, fall through */
case ZFCP_ERP_STEP_UNIT_CLOSING:
- if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
- ZFCP_LOG_DEBUG("close failed for unit 0x%016Lx\n",
- unit->fcp_lun);
- retval = ZFCP_ERP_FAILED;
- } else {
- if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
- retval = ZFCP_ERP_EXIT;
- else {
- ZFCP_LOG_DEBUG("unit 0x%016Lx is not open -> "
- "trying open\n", unit->fcp_lun);
- retval =
- zfcp_erp_unit_strategy_open(erp_action);
- }
- }
- break;
+ if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_OPEN)
+ return ZFCP_ERP_FAILED;
+ if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+ return ZFCP_ERP_EXIT;
+ return zfcp_erp_unit_strategy_open(erp_action);
case ZFCP_ERP_STEP_UNIT_OPENING:
- if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
- ZFCP_LOG_DEBUG("unit 0x%016Lx is open\n",
- unit->fcp_lun);
- retval = ZFCP_ERP_SUCCEEDED;
- } else {
- ZFCP_LOG_DEBUG("open failed for unit 0x%016Lx\n",
- unit->fcp_lun);
- retval = ZFCP_ERP_FAILED;
- }
- break;
+ if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_OPEN)
+ return ZFCP_ERP_SUCCEEDED;
}
-
- return retval;
+ return ZFCP_ERP_FAILED;
}
-static int
-zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *unit)
+static int zfcp_erp_strategy_check_unit(struct zfcp_unit *unit, int result)
{
- int retval = 0;
-
- atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING |
- ZFCP_STATUS_COMMON_CLOSING |
- ZFCP_STATUS_COMMON_ACCESS_DENIED |
- ZFCP_STATUS_UNIT_SHARED |
- ZFCP_STATUS_UNIT_READONLY,
- &unit->status);
+ switch (result) {
+ case ZFCP_ERP_SUCCEEDED :
+ atomic_set(&unit->erp_counter, 0);
+ zfcp_erp_unit_unblock(unit);
+ break;
+ case ZFCP_ERP_FAILED :
+ atomic_inc(&unit->erp_counter);
+ if (atomic_read(&unit->erp_counter) > ZFCP_MAX_ERPS) {
+ dev_err(&unit->port->adapter->ccw_device->dev,
+ "ERP failed for unit 0x%016Lx on "
+ "port 0x%016Lx\n",
+ (unsigned long long)unit->fcp_lun,
+ (unsigned long long)unit->port->wwpn);
+ zfcp_erp_unit_failed(unit, 21, NULL);
+ }
+ break;
+ }
- return retval;
+ if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
+ zfcp_erp_unit_block(unit, 0);
+ result = ZFCP_ERP_EXIT;
+ }
+ return result;
}
-/*
- * function:
- *
- * purpose:
- *
- * returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_unit_strategy_close(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_strategy_check_port(struct zfcp_port *port, int result)
{
- int retval;
+ switch (result) {
+ case ZFCP_ERP_SUCCEEDED :
+ atomic_set(&port->erp_counter, 0);
+ zfcp_erp_port_unblock(port);
+ break;
- retval = zfcp_fsf_close_unit(erp_action);
- if (retval == -ENOMEM) {
- retval = ZFCP_ERP_NOMEM;
- goto out;
- }
- erp_action->step = ZFCP_ERP_STEP_UNIT_CLOSING;
- if (retval != 0) {
- /* could not send 'close', fail */
- retval = ZFCP_ERP_FAILED;
- goto out;
+ case ZFCP_ERP_FAILED :
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC) {
+ zfcp_erp_port_block(port, 0);
+ result = ZFCP_ERP_EXIT;
+ }
+ atomic_inc(&port->erp_counter);
+ if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS) {
+ dev_err(&port->adapter->ccw_device->dev,
+ "ERP failed for remote port 0x%016Lx\n",
+ (unsigned long long)port->wwpn);
+ zfcp_erp_port_failed(port, 22, NULL);
+ }
+ break;
}
- retval = ZFCP_ERP_CONTINUES;
- out:
- return retval;
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
+ zfcp_erp_port_block(port, 0);
+ result = ZFCP_ERP_EXIT;
+ }
+ return result;
}
-/*
- * function:
- *
- * purpose:
- *
- * returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
- * ZFCP_ERP_FAILED - action finished unsuccessfully
- */
-static int
-zfcp_erp_unit_strategy_open(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_strategy_check_adapter(struct zfcp_adapter *adapter,
+ int result)
{
- int retval;
+ switch (result) {
+ case ZFCP_ERP_SUCCEEDED :
+ atomic_set(&adapter->erp_counter, 0);
+ zfcp_erp_adapter_unblock(adapter);
+ break;
- retval = zfcp_fsf_open_unit(erp_action);
- if (retval == -ENOMEM) {
- retval = ZFCP_ERP_NOMEM;
- goto out;
- }
- erp_action->step = ZFCP_ERP_STEP_UNIT_OPENING;
- if (retval != 0) {
- /* could not send 'open', fail */
- retval = ZFCP_ERP_FAILED;
- goto out;
+ case ZFCP_ERP_FAILED :
+ atomic_inc(&adapter->erp_counter);
+ if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS) {
+ dev_err(&adapter->ccw_device->dev,
+ "ERP cannot recover an error "
+ "on the FCP device\n");
+ zfcp_erp_adapter_failed(adapter, 23, NULL);
+ }
+ break;
}
- retval = ZFCP_ERP_CONTINUES;
- out:
- return retval;
-}
-void zfcp_erp_start_timer(struct zfcp_fsf_req *fsf_req)
-{
- BUG_ON(!fsf_req->erp_action);
- fsf_req->timer.function = zfcp_erp_timeout_handler;
- fsf_req->timer.data = (unsigned long) fsf_req->erp_action;
- fsf_req->timer.expires = jiffies + ZFCP_ERP_FSFREQ_TIMEOUT;
- add_timer(&fsf_req->timer);
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
+ zfcp_erp_adapter_block(adapter, 0);
+ result = ZFCP_ERP_EXIT;
+ }
+ return result;
}
-/*
- * function:
- *
- * purpose: enqueue the specified error recovery action, if needed
- *
- * returns:
- */
-static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
- struct zfcp_port *port,
- struct zfcp_unit *unit, u8 id, void *ref)
+static int zfcp_erp_strategy_check_target(struct zfcp_erp_action *erp_action,
+ int result)
{
- int retval = 1, need = want;
- struct zfcp_erp_action *erp_action = NULL;
- u32 status = 0;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_port *port = erp_action->port;
+ struct zfcp_unit *unit = erp_action->unit;
- /*
- * We need some rules here which check whether we really need
- * this action or whether we should just drop it.
- * E.g. if there is a unfinished 'Reopen Port' request then we drop a
- * 'Reopen Unit' request for an associated unit since we can't
- * satisfy this request now. A 'Reopen Port' action will trigger
- * 'Reopen Unit' actions when it completes.
- * Thus, there are only actions in the queue which can immediately be
- * executed. This makes the processing of the action queue more
- * efficient.
- */
-
- if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
- &adapter->status))
- return -EIO;
+ switch (erp_action->action) {
- /* check whether we really need this */
- switch (want) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
- if (atomic_test_mask
- (ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status)) {
- goto out;
- }
- if (!atomic_test_mask
- (ZFCP_STATUS_COMMON_RUNNING, &port->status) ||
- atomic_test_mask
- (ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
- goto out;
- }
- if (!atomic_test_mask
- (ZFCP_STATUS_COMMON_UNBLOCKED, &port->status))
- need = ZFCP_ERP_ACTION_REOPEN_PORT;
- /* fall through !!! */
-
- case ZFCP_ERP_ACTION_REOPEN_PORT:
- if (atomic_test_mask
- (ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)) {
- goto out;
- }
- /* fall through !!! */
+ result = zfcp_erp_strategy_check_unit(unit, result);
+ break;
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
- &port->status)) {
- if (port->erp_action.action !=
- ZFCP_ERP_ACTION_REOPEN_PORT_FORCED) {
- ZFCP_LOG_INFO("dropped erp action %i (port "
- "0x%016Lx, action in use: %i)\n",
- want, port->wwpn,
- port->erp_action.action);
- }
- goto out;
- }
- if (!atomic_test_mask
- (ZFCP_STATUS_COMMON_RUNNING, &adapter->status) ||
- atomic_test_mask
- (ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
- goto out;
- }
- if (!atomic_test_mask
- (ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status))
- need = ZFCP_ERP_ACTION_REOPEN_ADAPTER;
- /* fall through !!! */
+ case ZFCP_ERP_ACTION_REOPEN_PORT:
+ result = zfcp_erp_strategy_check_port(port, result);
+ break;
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- if (atomic_test_mask
- (ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status)) {
- goto out;
- }
+ result = zfcp_erp_strategy_check_adapter(adapter, result);
break;
-
- default:
- ZFCP_LOG_NORMAL("bug: unknown erp action requested "
- "on adapter %s (action=%d)\n",
- zfcp_get_busid_by_adapter(adapter), want);
- goto out;
}
+ return result;
+}
- /* check whether we need something stronger first */
- if (need) {
- ZFCP_LOG_DEBUG("stronger erp action %d needed before "
- "erp action %d on adapter %s\n",
- need, want, zfcp_get_busid_by_adapter(adapter));
- }
+static int zfcp_erp_strat_change_det(atomic_t *target_status, u32 erp_status)
+{
+ int status = atomic_read(target_status);
- /* mark adapter to have some error recovery pending */
- atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
+ if ((status & ZFCP_STATUS_COMMON_RUNNING) &&
+ (erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY))
+ return 1; /* take it online */
- /* setup error recovery action */
- switch (need) {
+ if (!(status & ZFCP_STATUS_COMMON_RUNNING) &&
+ !(erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY))
+ return 1; /* take it offline */
- case ZFCP_ERP_ACTION_REOPEN_UNIT:
- zfcp_unit_get(unit);
- atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status);
- erp_action = &unit->erp_action;
- if (!atomic_test_mask
- (ZFCP_STATUS_COMMON_RUNNING, &unit->status))
- status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+ return 0;
+}
+
+static int zfcp_erp_strategy_statechange(struct zfcp_erp_action *act, int ret)
+{
+ int action = act->action;
+ struct zfcp_adapter *adapter = act->adapter;
+ struct zfcp_port *port = act->port;
+ struct zfcp_unit *unit = act->unit;
+ u32 erp_status = act->status;
+
+ switch (action) {
+ case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+ if (zfcp_erp_strat_change_det(&adapter->status, erp_status)) {
+ _zfcp_erp_adapter_reopen(adapter,
+ ZFCP_STATUS_COMMON_ERP_FAILED,
+ 67, NULL);
+ return ZFCP_ERP_EXIT;
+ }
break;
- case ZFCP_ERP_ACTION_REOPEN_PORT:
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
- zfcp_port_get(port);
- zfcp_erp_action_dismiss_port(port);
- atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
- erp_action = &port->erp_action;
- if (!atomic_test_mask
- (ZFCP_STATUS_COMMON_RUNNING, &port->status))
- status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+ case ZFCP_ERP_ACTION_REOPEN_PORT:
+ if (zfcp_erp_strat_change_det(&port->status, erp_status)) {
+ _zfcp_erp_port_reopen(port,
+ ZFCP_STATUS_COMMON_ERP_FAILED,
+ 68, NULL);
+ return ZFCP_ERP_EXIT;
+ }
break;
- case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- zfcp_adapter_get(adapter);
- zfcp_erp_action_dismiss_adapter(adapter);
- atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
- erp_action = &adapter->erp_action;
- if (!atomic_test_mask
- (ZFCP_STATUS_COMMON_RUNNING, &adapter->status))
- status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+ case ZFCP_ERP_ACTION_REOPEN_UNIT:
+ if (zfcp_erp_strat_change_det(&unit->status, erp_status)) {
+ _zfcp_erp_unit_reopen(unit,
+ ZFCP_STATUS_COMMON_ERP_FAILED,
+ 69, NULL);
+ return ZFCP_ERP_EXIT;
+ }
break;
}
-
- memset(erp_action, 0, sizeof (struct zfcp_erp_action));
- erp_action->adapter = adapter;
- erp_action->port = port;
- erp_action->unit = unit;
- erp_action->action = need;
- erp_action->status = status;
-
- ++adapter->erp_total_count;
-
- /* finally put it into 'ready' queue and kick erp thread */
- list_add_tail(&erp_action->list, &adapter->erp_ready_head);
- up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(1, adapter, 0);
- retval = 0;
- out:
- zfcp_rec_dbf_event_trigger(id, ref, want, need, erp_action,
- adapter, port, unit);
- return retval;
+ return ret;
}
-static int
-zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
+static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
{
- int retval = 0;
struct zfcp_adapter *adapter = erp_action->adapter;
- --adapter->erp_total_count;
+ adapter->erp_total_count--;
if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) {
- --adapter->erp_low_mem_count;
+ adapter->erp_low_mem_count--;
erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM;
}
@@ -2919,141 +1160,445 @@ zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
&erp_action->unit->status);
break;
+
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
case ZFCP_ERP_ACTION_REOPEN_PORT:
atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
&erp_action->port->status);
break;
+
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
&erp_action->adapter->status);
break;
- default:
- /* bug */
- break;
}
- return retval;
}
-/**
- * zfcp_erp_action_cleanup
- *
- * Register unit with scsi stack if appropriate and fix reference counts.
- * Note: Temporary units are not registered with scsi stack.
- */
-static void
-zfcp_erp_action_cleanup(int action, struct zfcp_adapter *adapter,
- struct zfcp_port *port, struct zfcp_unit *unit,
- int result)
+struct zfcp_erp_add_work {
+ struct zfcp_unit *unit;
+ struct work_struct work;
+};
+
+static void zfcp_erp_scsi_scan(struct work_struct *work)
{
- switch (action) {
+ struct zfcp_erp_add_work *p =
+ container_of(work, struct zfcp_erp_add_work, work);
+ struct zfcp_unit *unit = p->unit;
+ struct fc_rport *rport = unit->port->rport;
+ scsi_scan_target(&rport->dev, 0, rport->scsi_target_id,
+ scsilun_to_int((struct scsi_lun *)&unit->fcp_lun), 0);
+ atomic_clear_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status);
+ zfcp_unit_put(unit);
+ wake_up(&unit->port->adapter->erp_done_wqh);
+ kfree(p);
+}
+
+static void zfcp_erp_schedule_work(struct zfcp_unit *unit)
+{
+ struct zfcp_erp_add_work *p;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ dev_err(&unit->port->adapter->ccw_device->dev,
+ "Registering unit 0x%016Lx on port 0x%016Lx failed\n",
+ (unsigned long long)unit->fcp_lun,
+ (unsigned long long)unit->port->wwpn);
+ return;
+ }
+
+ zfcp_unit_get(unit);
+ atomic_set_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status);
+ INIT_WORK(&p->work, zfcp_erp_scsi_scan);
+ p->unit = unit;
+ queue_work(zfcp_data.work_queue, &p->work);
+}
+
+static void zfcp_erp_rport_register(struct zfcp_port *port)
+{
+ struct fc_rport_identifiers ids;
+ ids.node_name = port->wwnn;
+ ids.port_name = port->wwpn;
+ ids.port_id = port->d_id;
+ ids.roles = FC_RPORT_ROLE_FCP_TARGET;
+ port->rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
+ if (!port->rport) {
+ dev_err(&port->adapter->ccw_device->dev,
+ "Registering port 0x%016Lx failed\n",
+ (unsigned long long)port->wwpn);
+ return;
+ }
+
+ scsi_target_unblock(&port->rport->dev);
+ port->rport->maxframe_size = port->maxframe_size;
+ port->rport->supported_classes = port->supported_classes;
+}
+
+static void zfcp_erp_rports_del(struct zfcp_adapter *adapter)
+{
+ struct zfcp_port *port;
+ list_for_each_entry(port, &adapter->port_list_head, list) {
+ if (!port->rport)
+ continue;
+ fc_remote_port_delete(port->rport);
+ port->rport = NULL;
+ }
+}
+
+static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
+{
+ struct zfcp_adapter *adapter = act->adapter;
+ struct zfcp_port *port = act->port;
+ struct zfcp_unit *unit = act->unit;
+
+ switch (act->action) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
- if ((result == ZFCP_ERP_SUCCEEDED)
- && (!atomic_test_mask(ZFCP_STATUS_UNIT_TEMPORARY,
- &unit->status))
- && !unit->device
- && port->rport) {
+ if ((result == ZFCP_ERP_SUCCEEDED) &&
+ !unit->device && port->rport) {
atomic_set_mask(ZFCP_STATUS_UNIT_REGISTERED,
&unit->status);
- if (atomic_test_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING,
- &unit->status) == 0)
+ if (!(atomic_read(&unit->status) &
+ ZFCP_STATUS_UNIT_SCSI_WORK_PENDING))
zfcp_erp_schedule_work(unit);
}
zfcp_unit_put(unit);
break;
+
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
case ZFCP_ERP_ACTION_REOPEN_PORT:
- if (atomic_test_mask(ZFCP_STATUS_PORT_NO_WWPN,
- &port->status)) {
+ if (atomic_read(&port->status) & ZFCP_STATUS_PORT_NO_WWPN) {
zfcp_port_put(port);
- break;
- }
-
- if ((result == ZFCP_ERP_SUCCEEDED)
- && !port->rport) {
- struct fc_rport_identifiers ids;
- ids.node_name = port->wwnn;
- ids.port_name = port->wwpn;
- ids.port_id = port->d_id;
- ids.roles = FC_RPORT_ROLE_FCP_TARGET;
- port->rport =
- fc_remote_port_add(adapter->scsi_host, 0, &ids);
- if (!port->rport)
- ZFCP_LOG_NORMAL("failed registration of rport"
- "(adapter %s, wwpn=0x%016Lx)\n",
- zfcp_get_busid_by_port(port),
- port->wwpn);
- else {
- scsi_target_unblock(&port->rport->dev);
- port->rport->maxframe_size = port->maxframe_size;
- port->rport->supported_classes =
- port->supported_classes;
- }
+ return;
}
+ if ((result == ZFCP_ERP_SUCCEEDED) && !port->rport)
+ zfcp_erp_rport_register(port);
if ((result != ZFCP_ERP_SUCCEEDED) && port->rport) {
fc_remote_port_delete(port->rport);
port->rport = NULL;
}
zfcp_port_put(port);
break;
+
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- if (result != ZFCP_ERP_SUCCEEDED) {
- list_for_each_entry(port, &adapter->port_list_head, list)
- if (port->rport &&
- !atomic_test_mask(ZFCP_STATUS_PORT_WKA,
- &port->status)) {
- fc_remote_port_delete(port->rport);
- port->rport = NULL;
- }
- }
+ if (result != ZFCP_ERP_SUCCEEDED)
+ zfcp_erp_rports_del(adapter);
zfcp_adapter_put(adapter);
break;
- default:
- break;
}
}
+static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
+{
+ switch (erp_action->action) {
+ case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+ return zfcp_erp_adapter_strategy(erp_action);
+ case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+ return zfcp_erp_port_forced_strategy(erp_action);
+ case ZFCP_ERP_ACTION_REOPEN_PORT:
+ return zfcp_erp_port_strategy(erp_action);
+ case ZFCP_ERP_ACTION_REOPEN_UNIT:
+ return zfcp_erp_unit_strategy(erp_action);
+ }
+ return ZFCP_ERP_FAILED;
+}
-static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
+static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
{
- struct zfcp_port *port;
+ int retval;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+ unsigned long flags;
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status))
- zfcp_erp_action_dismiss(&adapter->erp_action);
- else
- list_for_each_entry(port, &adapter->port_list_head, list)
- zfcp_erp_action_dismiss_port(port);
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+
+ zfcp_erp_strategy_check_fsfreq(erp_action);
+
+ if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) {
+ zfcp_erp_action_dequeue(erp_action);
+ retval = ZFCP_ERP_DISMISSED;
+ goto unlock;
+ }
+
+ zfcp_erp_action_to_running(erp_action);
+
+ /* no lock to allow for blocking operations */
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ retval = zfcp_erp_strategy_do_action(erp_action);
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+
+ if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED)
+ retval = ZFCP_ERP_CONTINUES;
+
+ switch (retval) {
+ case ZFCP_ERP_NOMEM:
+ if (!(erp_action->status & ZFCP_STATUS_ERP_LOWMEM)) {
+ ++adapter->erp_low_mem_count;
+ erp_action->status |= ZFCP_STATUS_ERP_LOWMEM;
+ }
+ if (adapter->erp_total_count == adapter->erp_low_mem_count)
+ _zfcp_erp_adapter_reopen(adapter, 0, 66, NULL);
+ else {
+ zfcp_erp_strategy_memwait(erp_action);
+ retval = ZFCP_ERP_CONTINUES;
+ }
+ goto unlock;
+
+ case ZFCP_ERP_CONTINUES:
+ if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) {
+ --adapter->erp_low_mem_count;
+ erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM;
+ }
+ goto unlock;
+ }
+
+ retval = zfcp_erp_strategy_check_target(erp_action, retval);
+ zfcp_erp_action_dequeue(erp_action);
+ retval = zfcp_erp_strategy_statechange(erp_action, retval);
+ if (retval == ZFCP_ERP_EXIT)
+ goto unlock;
+ zfcp_erp_strategy_followup_actions(erp_action);
+
+ unlock:
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+ if (retval != ZFCP_ERP_CONTINUES)
+ zfcp_erp_action_cleanup(erp_action, retval);
+
+ return retval;
}
-static void zfcp_erp_action_dismiss_port(struct zfcp_port *port)
+static int zfcp_erp_thread(void *data)
{
- struct zfcp_unit *unit;
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
+ struct list_head *next;
+ struct zfcp_erp_action *act;
+ unsigned long flags;
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status))
- zfcp_erp_action_dismiss(&port->erp_action);
- else
- list_for_each_entry(unit, &port->unit_list_head, list)
- zfcp_erp_action_dismiss_unit(unit);
+ daemonize("zfcperp%s", dev_name(&adapter->ccw_device->dev));
+ /* Block all signals */
+ siginitsetinv(&current->blocked, 0);
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+ wake_up(&adapter->erp_thread_wqh);
+
+ while (!(atomic_read(&adapter->status) &
+ ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL)) {
+ write_lock_irqsave(&adapter->erp_lock, flags);
+ next = adapter->erp_ready_head.next;
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+
+ if (next != &adapter->erp_ready_head) {
+ act = list_entry(next, struct zfcp_erp_action, list);
+
+ /* there is more to come after dismission, no notify */
+ if (zfcp_erp_strategy(act) != ZFCP_ERP_DISMISSED)
+ zfcp_erp_wakeup(adapter);
+ }
+
+ zfcp_rec_dbf_event_thread_lock(4, adapter);
+ down_interruptible(&adapter->erp_ready_sem);
+ zfcp_rec_dbf_event_thread_lock(5, adapter);
+ }
+
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+ wake_up(&adapter->erp_thread_wqh);
+
+ return 0;
}
-static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit)
+/**
+ * zfcp_erp_thread_setup - Start ERP thread for adapter
+ * @adapter: Adapter to start the ERP thread for
+ *
+ * Returns 0 on success or error code from kernel_thread()
+ */
+int zfcp_erp_thread_setup(struct zfcp_adapter *adapter)
{
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status))
- zfcp_erp_action_dismiss(&unit->erp_action);
+ int retval;
+
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+ retval = kernel_thread(zfcp_erp_thread, adapter, SIGCHLD);
+ if (retval < 0) {
+ dev_err(&adapter->ccw_device->dev,
+ "Creating an ERP thread for the FCP device failed.\n");
+ return retval;
+ }
+ wait_event(adapter->erp_thread_wqh,
+ atomic_read(&adapter->status) &
+ ZFCP_STATUS_ADAPTER_ERP_THREAD_UP);
+ return 0;
}
-static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
+/**
+ * zfcp_erp_thread_kill - Stop ERP thread.
+ * @adapter: Adapter where the ERP thread should be stopped.
+ *
+ * The caller of this routine ensures that the specified adapter has
+ * been shut down and that this operation has been completed. Thus,
+ * there are no pending erp_actions which would need to be handled
+ * here.
+ */
+void zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
{
- list_move(&erp_action->list, &erp_action->adapter->erp_running_head);
- zfcp_rec_dbf_event_action(145, erp_action);
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
+ up(&adapter->erp_ready_sem);
+ zfcp_rec_dbf_event_thread_lock(3, adapter);
+
+ wait_event(adapter->erp_thread_wqh,
+ !(atomic_read(&adapter->status) &
+ ZFCP_STATUS_ADAPTER_ERP_THREAD_UP));
+
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
+ &adapter->status);
+}
+
+/**
+ * zfcp_erp_adapter_failed - Set adapter status to failed.
+ * @adapter: Failed adapter.
+ * @id: Event id for debug trace.
+ * @ref: Reference for debug trace.
+ */
+void zfcp_erp_adapter_failed(struct zfcp_adapter *adapter, u8 id, void *ref)
+{
+ zfcp_erp_modify_adapter_status(adapter, id, ref,
+ ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
+}
+
+/**
+ * zfcp_erp_port_failed - Set port status to failed.
+ * @port: Failed port.
+ * @id: Event id for debug trace.
+ * @ref: Reference for debug trace.
+ */
+void zfcp_erp_port_failed(struct zfcp_port *port, u8 id, void *ref)
+{
+ zfcp_erp_modify_port_status(port, id, ref,
+ ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
+}
+
+/**
+ * zfcp_erp_unit_failed - Set unit status to failed.
+ * @unit: Failed unit.
+ * @id: Event id for debug trace.
+ * @ref: Reference for debug trace.
+ */
+void zfcp_erp_unit_failed(struct zfcp_unit *unit, u8 id, void *ref)
+{
+ zfcp_erp_modify_unit_status(unit, id, ref,
+ ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
+}
+
+/**
+ * zfcp_erp_wait - wait for completion of error recovery on an adapter
+ * @adapter: adapter for which to wait for completion of its error recovery
+ */
+void zfcp_erp_wait(struct zfcp_adapter *adapter)
+{
+ wait_event(adapter->erp_done_wqh,
+ !(atomic_read(&adapter->status) &
+ ZFCP_STATUS_ADAPTER_ERP_PENDING));
+}
+
+/**
+ * zfcp_erp_modify_adapter_status - change adapter status bits
+ * @adapter: adapter to change the status
+ * @id: id for the debug trace
+ * @ref: reference for the debug trace
+ * @mask: status bits to change
+ * @set_or_clear: ZFCP_SET or ZFCP_CLEAR
+ *
+ * Changes in common status bits are propagated to attached ports and units.
+ */
+void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, u8 id,
+ void *ref, u32 mask, int set_or_clear)
+{
+ struct zfcp_port *port;
+ u32 common_mask = mask & ZFCP_COMMON_FLAGS;
+
+ if (set_or_clear == ZFCP_SET) {
+ if (status_change_set(mask, &adapter->status))
+ zfcp_rec_dbf_event_adapter(id, ref, adapter);
+ atomic_set_mask(mask, &adapter->status);
+ } else {
+ if (status_change_clear(mask, &adapter->status))
+ zfcp_rec_dbf_event_adapter(id, ref, adapter);
+ atomic_clear_mask(mask, &adapter->status);
+ if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
+ atomic_set(&adapter->erp_counter, 0);
+ }
+
+ if (common_mask)
+ list_for_each_entry(port, &adapter->port_list_head, list)
+ zfcp_erp_modify_port_status(port, id, ref, common_mask,
+ set_or_clear);
+}
+
+/**
+ * zfcp_erp_modify_port_status - change port status bits
+ * @port: port to change the status bits
+ * @id: id for the debug trace
+ * @ref: reference for the debug trace
+ * @mask: status bits to change
+ * @set_or_clear: ZFCP_SET or ZFCP_CLEAR
+ *
+ * Changes in common status bits are propagated to attached units.
+ */
+void zfcp_erp_modify_port_status(struct zfcp_port *port, u8 id, void *ref,
+ u32 mask, int set_or_clear)
+{
+ struct zfcp_unit *unit;
+ u32 common_mask = mask & ZFCP_COMMON_FLAGS;
+
+ if (set_or_clear == ZFCP_SET) {
+ if (status_change_set(mask, &port->status))
+ zfcp_rec_dbf_event_port(id, ref, port);
+ atomic_set_mask(mask, &port->status);
+ } else {
+ if (status_change_clear(mask, &port->status))
+ zfcp_rec_dbf_event_port(id, ref, port);
+ atomic_clear_mask(mask, &port->status);
+ if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
+ atomic_set(&port->erp_counter, 0);
+ }
+
+ if (common_mask)
+ list_for_each_entry(unit, &port->unit_list_head, list)
+ zfcp_erp_modify_unit_status(unit, id, ref, common_mask,
+ set_or_clear);
}
-static void zfcp_erp_action_to_ready(struct zfcp_erp_action *erp_action)
+/**
+ * zfcp_erp_modify_unit_status - change unit status bits
+ * @unit: unit to change the status bits
+ * @id: id for the debug trace
+ * @ref: reference for the debug trace
+ * @mask: status bits to change
+ * @set_or_clear: ZFCP_SET or ZFCP_CLEAR
+ */
+void zfcp_erp_modify_unit_status(struct zfcp_unit *unit, u8 id, void *ref,
+ u32 mask, int set_or_clear)
{
- list_move(&erp_action->list, &erp_action->adapter->erp_ready_head);
- zfcp_rec_dbf_event_action(146, erp_action);
+ if (set_or_clear == ZFCP_SET) {
+ if (status_change_set(mask, &unit->status))
+ zfcp_rec_dbf_event_unit(id, ref, unit);
+ atomic_set_mask(mask, &unit->status);
+ } else {
+ if (status_change_clear(mask, &unit->status))
+ zfcp_rec_dbf_event_unit(id, ref, unit);
+ atomic_clear_mask(mask, &unit->status);
+ if (mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
+ atomic_set(&unit->erp_counter, 0);
+ }
+ }
}
+/**
+ * zfcp_erp_port_boxed - Mark port as "boxed" and start ERP
+ * @port: The "boxed" port.
+ * @id: The debug trace id.
+ * @id: Reference for the debug trace.
+ */
void zfcp_erp_port_boxed(struct zfcp_port *port, u8 id, void *ref)
{
unsigned long flags;
@@ -3065,6 +1610,12 @@ void zfcp_erp_port_boxed(struct zfcp_port *port, u8 id, void *ref)
zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref);
}
+/**
+ * zfcp_erp_unit_boxed - Mark unit as "boxed" and start ERP
+ * @port: The "boxed" unit.
+ * @id: The debug trace id.
+ * @id: Reference for the debug trace.
+ */
void zfcp_erp_unit_boxed(struct zfcp_unit *unit, u8 id, void *ref)
{
zfcp_erp_modify_unit_status(unit, id, ref,
@@ -3072,6 +1623,15 @@ void zfcp_erp_unit_boxed(struct zfcp_unit *unit, u8 id, void *ref)
zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref);
}
+/**
+ * zfcp_erp_port_access_denied - Adapter denied access to port.
+ * @port: port where access has been denied
+ * @id: id for debug trace
+ * @ref: reference for debug trace
+ *
+ * Since the adapter has denied access, stop using the port and the
+ * attached units.
+ */
void zfcp_erp_port_access_denied(struct zfcp_port *port, u8 id, void *ref)
{
unsigned long flags;
@@ -3083,6 +1643,14 @@ void zfcp_erp_port_access_denied(struct zfcp_port *port, u8 id, void *ref)
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
+/**
+ * zfcp_erp_unit_access_denied - Adapter denied access to unit.
+ * @unit: unit where access has been denied
+ * @id: id for debug trace
+ * @ref: reference for debug trace
+ *
+ * Since the adapter has denied access, stop using the unit.
+ */
void zfcp_erp_unit_access_denied(struct zfcp_unit *unit, u8 id, void *ref)
{
zfcp_erp_modify_unit_status(unit, id, ref,
@@ -3090,67 +1658,50 @@ void zfcp_erp_unit_access_denied(struct zfcp_unit *unit, u8 id, void *ref)
ZFCP_STATUS_COMMON_ACCESS_DENIED, ZFCP_SET);
}
-void zfcp_erp_adapter_access_changed(struct zfcp_adapter *adapter, u8 id,
- void *ref)
+static void zfcp_erp_unit_access_changed(struct zfcp_unit *unit, u8 id,
+ void *ref)
{
- struct zfcp_port *port;
- unsigned long flags;
-
- if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
+ int status = atomic_read(&unit->status);
+ if (!(status & (ZFCP_STATUS_COMMON_ACCESS_DENIED |
+ ZFCP_STATUS_COMMON_ACCESS_BOXED)))
return;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- if (adapter->nameserver_port)
- zfcp_erp_port_access_changed(adapter->nameserver_port, id, ref);
- list_for_each_entry(port, &adapter->port_list_head, list)
- if (port != adapter->nameserver_port)
- zfcp_erp_port_access_changed(port, id, ref);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref);
}
-void zfcp_erp_port_access_changed(struct zfcp_port *port, u8 id, void *ref)
+static void zfcp_erp_port_access_changed(struct zfcp_port *port, u8 id,
+ void *ref)
{
- struct zfcp_adapter *adapter = port->adapter;
struct zfcp_unit *unit;
+ int status = atomic_read(&port->status);
- if (!atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED,
- &port->status) &&
- !atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_BOXED,
- &port->status)) {
- if (!atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
- list_for_each_entry(unit, &port->unit_list_head, list)
- zfcp_erp_unit_access_changed(unit, id, ref);
+ if (!(status & (ZFCP_STATUS_COMMON_ACCESS_DENIED |
+ ZFCP_STATUS_COMMON_ACCESS_BOXED))) {
+ list_for_each_entry(unit, &port->unit_list_head, list)
+ zfcp_erp_unit_access_changed(unit, id, ref);
return;
}
- ZFCP_LOG_NORMAL("reopen of port 0x%016Lx on adapter %s "
- "(due to ACT update)\n",
- port->wwpn, zfcp_get_busid_by_adapter(adapter));
- if (zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref))
- ZFCP_LOG_NORMAL("failed reopen of port"
- "(adapter %s, wwpn=0x%016Lx)\n",
- zfcp_get_busid_by_adapter(adapter), port->wwpn);
+ zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref);
}
-void zfcp_erp_unit_access_changed(struct zfcp_unit *unit, u8 id, void *ref)
+/**
+ * zfcp_erp_adapter_access_changed - Process change in adapter ACT
+ * @adapter: Adapter where the Access Control Table (ACT) changed
+ * @id: Id for debug trace
+ * @ref: Reference for debug trace
+ */
+void zfcp_erp_adapter_access_changed(struct zfcp_adapter *adapter, u8 id,
+ void *ref)
{
- struct zfcp_adapter *adapter = unit->port->adapter;
+ struct zfcp_port *port;
+ unsigned long flags;
- if (!atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED,
- &unit->status) &&
- !atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_BOXED,
- &unit->status))
+ if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
return;
- ZFCP_LOG_NORMAL("reopen of unit 0x%016Lx on port 0x%016Lx "
- " on adapter %s (due to ACT update)\n",
- unit->fcp_lun, unit->port->wwpn,
- zfcp_get_busid_by_adapter(adapter));
- if (zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref))
- ZFCP_LOG_NORMAL("failed reopen of unit (adapter %s, "
- "wwpn=0x%016Lx, fcp_lun=0x%016Lx)\n",
- zfcp_get_busid_by_adapter(adapter),
- unit->port->wwpn, unit->fcp_lun);
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ list_for_each_entry(port, &adapter->port_list_head, list)
+ zfcp_erp_port_access_changed(port, id, ref);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 6abf178fda5..b5adeda93e1 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -1,22 +1,9 @@
/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
*
- * (C) Copyright IBM Corp. 2002, 2006
+ * External function declarations.
*
- * 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 Corporation 2002, 2008
*/
#ifndef ZFCP_EXT_H
@@ -24,172 +11,51 @@
#include "zfcp_def.h"
-extern struct zfcp_data zfcp_data;
-
-/******************************** SYSFS *************************************/
-extern struct attribute_group *zfcp_driver_attr_groups[];
-extern int zfcp_sysfs_adapter_create_files(struct device *);
-extern void zfcp_sysfs_adapter_remove_files(struct device *);
-extern int zfcp_sysfs_port_create_files(struct device *, u32);
-extern void zfcp_sysfs_port_remove_files(struct device *, u32);
-extern int zfcp_sysfs_unit_create_files(struct device *);
-extern void zfcp_sysfs_unit_remove_files(struct device *);
-extern void zfcp_sysfs_port_release(struct device *);
-extern void zfcp_sysfs_unit_release(struct device *);
-
-/**************************** CONFIGURATION *********************************/
-extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, fcp_lun_t);
-extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, wwn_t);
-extern struct zfcp_port *zfcp_get_port_by_did(struct zfcp_adapter *, u32);
-struct zfcp_adapter *zfcp_get_adapter_by_busid(char *);
-extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
-extern int zfcp_adapter_debug_register(struct zfcp_adapter *);
-extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
-extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *);
-extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t,
- u32, u32);
-extern void zfcp_port_dequeue(struct zfcp_port *);
-extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, fcp_lun_t);
-extern void zfcp_unit_dequeue(struct zfcp_unit *);
-
-/******************************* S/390 IO ************************************/
-extern int zfcp_ccw_register(void);
-
-extern void zfcp_qdio_zero_sbals(struct qdio_buffer **, int, int);
-extern int zfcp_qdio_allocate(struct zfcp_adapter *);
-extern int zfcp_qdio_allocate_queues(struct zfcp_adapter *);
-extern void zfcp_qdio_free_queues(struct zfcp_adapter *);
-extern int zfcp_qdio_determine_pci(struct zfcp_qdio_queue *,
- struct zfcp_fsf_req *);
-
-extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_req
- (struct zfcp_fsf_req *, int, int);
-extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_curr
- (struct zfcp_fsf_req *);
-extern int zfcp_qdio_sbals_from_sg
- (struct zfcp_fsf_req *, unsigned long, struct scatterlist *, int, int);
-extern int zfcp_qdio_sbals_from_scsicmnd
- (struct zfcp_fsf_req *, unsigned long, struct scsi_cmnd *);
-
-
-/******************************** FSF ****************************************/
-extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
-extern int zfcp_fsf_close_port(struct zfcp_erp_action *);
-extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
-
-extern int zfcp_fsf_open_unit(struct zfcp_erp_action *);
-extern int zfcp_fsf_close_unit(struct zfcp_erp_action *);
-
-extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *);
-extern int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *,
- struct fsf_qtcb_bottom_config *);
-extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *);
-extern int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *,
- struct fsf_qtcb_bottom_port *);
-extern int zfcp_fsf_control_file(struct zfcp_adapter *, struct zfcp_fsf_req **,
- u32, u32, struct zfcp_sg_list *);
-extern void zfcp_fsf_start_timer(struct zfcp_fsf_req *, unsigned long);
-extern void zfcp_erp_start_timer(struct zfcp_fsf_req *);
-extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
-extern int zfcp_fsf_status_read(struct zfcp_adapter *, int);
-extern int zfcp_fsf_req_create(struct zfcp_adapter *, u32, int, mempool_t *,
- unsigned long *, struct zfcp_fsf_req **);
-extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
- struct zfcp_erp_action *);
-extern int zfcp_fsf_send_els(struct zfcp_send_els *);
-extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *,
- struct zfcp_unit *,
- struct scsi_cmnd *, int, int);
-extern int zfcp_fsf_req_complete(struct zfcp_fsf_req *);
-extern void zfcp_fsf_incoming_els(struct zfcp_fsf_req *);
-extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
-extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_command_task_management(
- struct zfcp_adapter *, struct zfcp_unit *, u8, int);
-extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(
- unsigned long, struct zfcp_adapter *, struct zfcp_unit *, int);
-
-/******************************* FC/FCP **************************************/
-extern int zfcp_nameserver_enqueue(struct zfcp_adapter *);
-extern int zfcp_ns_gid_pn_request(struct zfcp_erp_action *);
-extern int zfcp_check_ct_response(struct ct_hdr *);
-extern int zfcp_handle_els_rjt(u32, struct zfcp_ls_rjt_par *);
-extern void zfcp_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
-
-/******************************* SCSI ****************************************/
-extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
-extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
-extern void zfcp_set_fcp_dl(struct fcp_cmnd_iu *, fcp_dl_t);
-extern char *zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *);
-extern void set_host_byte(int *, char);
-extern void set_driver_byte(int *, char);
-extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
-extern fcp_dl_t zfcp_get_fcp_dl(struct fcp_cmnd_iu *);
-
-extern int zfcp_scsi_command_async(struct zfcp_adapter *,struct zfcp_unit *,
- struct scsi_cmnd *, int);
-extern int zfcp_scsi_command_sync(struct zfcp_unit *, struct scsi_cmnd *, int);
-extern struct fc_function_template zfcp_transport_functions;
-
-/******************************** ERP ****************************************/
-extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u8, void *,
- u32, int);
-extern int zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, u8, void *);
-extern int zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, u8, void *);
-extern void zfcp_erp_adapter_failed(struct zfcp_adapter *, u8, void *);
-
-extern void zfcp_erp_modify_port_status(struct zfcp_port *, u8, void *, u32,
- int);
-extern int zfcp_erp_port_reopen(struct zfcp_port *, int, u8, void *);
-extern int zfcp_erp_port_shutdown(struct zfcp_port *, int, u8, void *);
-extern int zfcp_erp_port_forced_reopen(struct zfcp_port *, int, u8, void *);
-extern void zfcp_erp_port_failed(struct zfcp_port *, u8, void *);
-extern int zfcp_erp_port_reopen_all(struct zfcp_adapter *, int, u8, void *);
-
-extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, u8, void *, u32,
- int);
-extern int zfcp_erp_unit_reopen(struct zfcp_unit *, int, u8, void *);
-extern int zfcp_erp_unit_shutdown(struct zfcp_unit *, int, u8, void *);
-extern void zfcp_erp_unit_failed(struct zfcp_unit *, u8, void *);
-
-extern int zfcp_erp_thread_setup(struct zfcp_adapter *);
-extern int zfcp_erp_thread_kill(struct zfcp_adapter *);
-extern int zfcp_erp_wait(struct zfcp_adapter *);
-extern void zfcp_erp_async_handler(struct zfcp_erp_action *, unsigned long);
-
-extern int zfcp_test_link(struct zfcp_port *);
-
-extern void zfcp_erp_port_boxed(struct zfcp_port *, u8 id, void *ref);
-extern void zfcp_erp_unit_boxed(struct zfcp_unit *, u8 id, void *ref);
-extern void zfcp_erp_port_access_denied(struct zfcp_port *, u8 id, void *ref);
-extern void zfcp_erp_unit_access_denied(struct zfcp_unit *, u8 id, void *ref);
-extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, u8, void *);
-extern void zfcp_erp_port_access_changed(struct zfcp_port *, u8, void *);
-extern void zfcp_erp_unit_access_changed(struct zfcp_unit *, u8, void *);
-
-/******************************** AUX ****************************************/
-extern void zfcp_rec_dbf_event_thread(u8 id, struct zfcp_adapter *adapter,
- int lock);
-extern void zfcp_rec_dbf_event_adapter(u8 id, void *ref, struct zfcp_adapter *);
-extern void zfcp_rec_dbf_event_port(u8 id, void *ref, struct zfcp_port *port);
-extern void zfcp_rec_dbf_event_unit(u8 id, void *ref, struct zfcp_unit *unit);
-extern void zfcp_rec_dbf_event_trigger(u8 id, void *ref, u8 want, u8 need,
- void *action, struct zfcp_adapter *,
+/* zfcp_aux.c */
+extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, u64);
+extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, u64);
+extern int zfcp_adapter_enqueue(struct ccw_device *);
+extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
+extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, u64, u32,
+ u32);
+extern void zfcp_port_dequeue(struct zfcp_port *);
+extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, u64);
+extern void zfcp_unit_dequeue(struct zfcp_unit *);
+extern int zfcp_reqlist_isempty(struct zfcp_adapter *);
+extern void zfcp_sg_free_table(struct scatterlist *, int);
+extern int zfcp_sg_setup_table(struct scatterlist *, int);
+
+/* zfcp_ccw.c */
+extern int zfcp_ccw_register(void);
+extern struct zfcp_adapter *zfcp_get_adapter_by_busid(char *);
+
+/* zfcp_cfdc.c */
+extern struct miscdevice zfcp_cfdc_misc;
+
+/* zfcp_dbf.c */
+extern int zfcp_adapter_debug_register(struct zfcp_adapter *);
+extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *);
+extern void zfcp_rec_dbf_event_thread(u8, struct zfcp_adapter *);
+extern void zfcp_rec_dbf_event_thread_lock(u8, struct zfcp_adapter *);
+extern void zfcp_rec_dbf_event_adapter(u8, void *, struct zfcp_adapter *);
+extern void zfcp_rec_dbf_event_port(u8, void *, struct zfcp_port *);
+extern void zfcp_rec_dbf_event_unit(u8, void *, struct zfcp_unit *);
+extern void zfcp_rec_dbf_event_trigger(u8, void *, u8, u8, void *,
+ struct zfcp_adapter *,
struct zfcp_port *, struct zfcp_unit *);
-extern void zfcp_rec_dbf_event_action(u8 id, struct zfcp_erp_action *);
-
+extern void zfcp_rec_dbf_event_action(u8, struct zfcp_erp_action *);
extern void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *);
extern void zfcp_hba_dbf_event_fsf_unsol(const char *, struct zfcp_adapter *,
struct fsf_status_read_buffer *);
-extern void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *,
- unsigned int, unsigned int, unsigned int,
- int, int);
-
+extern void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *, unsigned int, int,
+ int);
+extern void zfcp_hba_dbf_event_berr(struct zfcp_adapter *,
+ struct zfcp_fsf_req *);
extern void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *);
extern void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *);
extern void zfcp_san_dbf_event_els_request(struct zfcp_fsf_req *);
extern void zfcp_san_dbf_event_els_response(struct zfcp_fsf_req *);
extern void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *);
-
extern void zfcp_scsi_dbf_event_result(const char *, int, struct zfcp_adapter *,
struct scsi_cmnd *,
struct zfcp_fsf_req *);
@@ -198,6 +64,101 @@ extern void zfcp_scsi_dbf_event_abort(const char *, struct zfcp_adapter *,
unsigned long);
extern void zfcp_scsi_dbf_event_devreset(const char *, u8, struct zfcp_unit *,
struct scsi_cmnd *);
-extern int zfcp_reqlist_isempty(struct zfcp_adapter *);
+
+/* zfcp_erp.c */
+extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u8, void *,
+ u32, int);
+extern void zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, u8, void *);
+extern void zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, u8, void *);
+extern void zfcp_erp_adapter_failed(struct zfcp_adapter *, u8, void *);
+extern void zfcp_erp_modify_port_status(struct zfcp_port *, u8, void *, u32,
+ int);
+extern int zfcp_erp_port_reopen(struct zfcp_port *, int, u8, void *);
+extern void zfcp_erp_port_shutdown(struct zfcp_port *, int, u8, void *);
+extern void zfcp_erp_port_forced_reopen(struct zfcp_port *, int, u8, void *);
+extern void zfcp_erp_port_failed(struct zfcp_port *, u8, void *);
+extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, u8, void *, u32,
+ int);
+extern void zfcp_erp_unit_reopen(struct zfcp_unit *, int, u8, void *);
+extern void zfcp_erp_unit_shutdown(struct zfcp_unit *, int, u8, void *);
+extern void zfcp_erp_unit_failed(struct zfcp_unit *, u8, void *);
+extern int zfcp_erp_thread_setup(struct zfcp_adapter *);
+extern void zfcp_erp_thread_kill(struct zfcp_adapter *);
+extern void zfcp_erp_wait(struct zfcp_adapter *);
+extern void zfcp_erp_notify(struct zfcp_erp_action *, unsigned long);
+extern void zfcp_erp_port_boxed(struct zfcp_port *, u8, void *);
+extern void zfcp_erp_unit_boxed(struct zfcp_unit *, u8, void *);
+extern void zfcp_erp_port_access_denied(struct zfcp_port *, u8, void *);
+extern void zfcp_erp_unit_access_denied(struct zfcp_unit *, u8, void *);
+extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, u8, void *);
+extern void zfcp_erp_timeout_handler(unsigned long);
+extern void zfcp_erp_port_strategy_open_lookup(struct work_struct *);
+
+/* zfcp_fc.c */
+extern int zfcp_scan_ports(struct zfcp_adapter *);
+extern void _zfcp_scan_ports_later(struct work_struct *);
+extern void zfcp_fc_incoming_els(struct zfcp_fsf_req *);
+extern int zfcp_fc_ns_gid_pn(struct zfcp_erp_action *);
+extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
+extern void zfcp_test_link(struct zfcp_port *);
+extern void zfcp_fc_nameserver_init(struct zfcp_adapter *);
+
+/* zfcp_fsf.c */
+extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
+extern int zfcp_fsf_open_wka_port(struct zfcp_wka_port *);
+extern int zfcp_fsf_close_wka_port(struct zfcp_wka_port *);
+extern int zfcp_fsf_close_port(struct zfcp_erp_action *);
+extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
+extern int zfcp_fsf_open_unit(struct zfcp_erp_action *);
+extern int zfcp_fsf_close_unit(struct zfcp_erp_action *);
+extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *);
+extern int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *,
+ struct fsf_qtcb_bottom_config *);
+extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *);
+extern int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *,
+ struct fsf_qtcb_bottom_port *);
+extern struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *,
+ struct zfcp_fsf_cfdc *);
+extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
+extern int zfcp_fsf_status_read(struct zfcp_adapter *);
+extern int zfcp_status_read_refill(struct zfcp_adapter *adapter);
+extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
+ struct zfcp_erp_action *);
+extern int zfcp_fsf_send_els(struct zfcp_send_els *);
+extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *,
+ struct zfcp_unit *,
+ struct scsi_cmnd *, int, int);
+extern void zfcp_fsf_req_complete(struct zfcp_fsf_req *);
+extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
+extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_adapter *,
+ struct zfcp_unit *, u8, int);
+extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long,
+ struct zfcp_adapter *,
+ struct zfcp_unit *, int);
+
+/* zfcp_qdio.c */
+extern int zfcp_qdio_allocate(struct zfcp_adapter *);
+extern void zfcp_qdio_free(struct zfcp_adapter *);
+extern int zfcp_qdio_send(struct zfcp_fsf_req *);
+extern struct qdio_buffer_element *zfcp_qdio_sbale_req(struct zfcp_fsf_req *);
+extern struct qdio_buffer_element *zfcp_qdio_sbale_curr(struct zfcp_fsf_req *);
+extern int zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *, unsigned long,
+ struct scatterlist *, int);
+extern int zfcp_qdio_open(struct zfcp_adapter *);
+extern void zfcp_qdio_close(struct zfcp_adapter *);
+
+/* zfcp_scsi.c */
+extern struct zfcp_data zfcp_data;
+extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
+extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
+extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
+extern struct fc_function_template zfcp_transport_functions;
+
+/* zfcp_sysfs.c */
+extern struct attribute_group zfcp_sysfs_unit_attrs;
+extern struct attribute_group zfcp_sysfs_adapter_attrs;
+extern struct attribute_group zfcp_sysfs_port_attrs;
+extern struct device_attribute *zfcp_sysfs_sdev_attrs[];
+extern struct device_attribute *zfcp_sysfs_shost_attrs[];
#endif /* ZFCP_EXT_H */
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
new file mode 100644
index 00000000000..1a7c80a77ff
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -0,0 +1,647 @@
+/*
+ * zfcp device driver
+ *
+ * Fibre Channel related functions for the zfcp device driver.
+ *
+ * Copyright IBM Corporation 2008
+ */
+
+#include "zfcp_ext.h"
+
+struct ct_iu_gpn_ft_req {
+ struct ct_hdr header;
+ u8 flags;
+ u8 domain_id_scope;
+ u8 area_id_scope;
+ u8 fc4_type;
+} __attribute__ ((packed));
+
+struct gpn_ft_resp_acc {
+ u8 control;
+ u8 port_id[3];
+ u8 reserved[4];
+ u64 wwpn;
+} __attribute__ ((packed));
+
+#define ZFCP_GPN_FT_ENTRIES ((PAGE_SIZE - sizeof(struct ct_hdr)) \
+ / sizeof(struct gpn_ft_resp_acc))
+#define ZFCP_GPN_FT_BUFFERS 4
+#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1)
+
+struct ct_iu_gpn_ft_resp {
+ struct ct_hdr header;
+ struct gpn_ft_resp_acc accept[ZFCP_GPN_FT_ENTRIES];
+} __attribute__ ((packed));
+
+struct zfcp_gpn_ft {
+ struct zfcp_send_ct ct;
+ struct scatterlist sg_req;
+ struct scatterlist sg_resp[ZFCP_GPN_FT_BUFFERS];
+};
+
+struct zfcp_fc_ns_handler_data {
+ struct completion done;
+ void (*handler)(unsigned long);
+ unsigned long handler_data;
+};
+
+static int zfcp_wka_port_get(struct zfcp_wka_port *wka_port)
+{
+ if (mutex_lock_interruptible(&wka_port->mutex))
+ return -ERESTARTSYS;
+
+ if (wka_port->status != ZFCP_WKA_PORT_ONLINE) {
+ wka_port->status = ZFCP_WKA_PORT_OPENING;
+ if (zfcp_fsf_open_wka_port(wka_port))
+ wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+ }
+
+ mutex_unlock(&wka_port->mutex);
+
+ wait_event_timeout(
+ wka_port->completion_wq,
+ wka_port->status == ZFCP_WKA_PORT_ONLINE ||
+ wka_port->status == ZFCP_WKA_PORT_OFFLINE,
+ HZ >> 1);
+
+ if (wka_port->status == ZFCP_WKA_PORT_ONLINE) {
+ atomic_inc(&wka_port->refcount);
+ return 0;
+ }
+ return -EIO;
+}
+
+static void zfcp_wka_port_offline(struct work_struct *work)
+{
+ struct delayed_work *dw = container_of(work, struct delayed_work, work);
+ struct zfcp_wka_port *wka_port =
+ container_of(dw, struct zfcp_wka_port, work);
+
+ wait_event(wka_port->completion_wq,
+ atomic_read(&wka_port->refcount) == 0);
+
+ mutex_lock(&wka_port->mutex);
+ if ((atomic_read(&wka_port->refcount) != 0) ||
+ (wka_port->status != ZFCP_WKA_PORT_ONLINE))
+ goto out;
+
+ wka_port->status = ZFCP_WKA_PORT_CLOSING;
+ if (zfcp_fsf_close_wka_port(wka_port)) {
+ wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+ wake_up(&wka_port->completion_wq);
+ }
+out:
+ mutex_unlock(&wka_port->mutex);
+}
+
+static void zfcp_wka_port_put(struct zfcp_wka_port *wka_port)
+{
+ if (atomic_dec_return(&wka_port->refcount) != 0)
+ return;
+ /* wait 10 miliseconds, other reqs might pop in */
+ schedule_delayed_work(&wka_port->work, HZ / 100);
+}
+
+void zfcp_fc_nameserver_init(struct zfcp_adapter *adapter)
+{
+ struct zfcp_wka_port *wka_port = &adapter->nsp;
+
+ init_waitqueue_head(&wka_port->completion_wq);
+
+ wka_port->adapter = adapter;
+ wka_port->d_id = ZFCP_DID_DIRECTORY_SERVICE;
+
+ wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+ atomic_set(&wka_port->refcount, 0);
+ mutex_init(&wka_port->mutex);
+ INIT_DELAYED_WORK(&wka_port->work, zfcp_wka_port_offline);
+}
+
+static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
+ struct fcp_rscn_element *elem)
+{
+ unsigned long flags;
+ struct zfcp_port *port;
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ list_for_each_entry(port, &fsf_req->adapter->port_list_head, list) {
+ /* FIXME: ZFCP_STATUS_PORT_DID_DID check is racy */
+ if (!(atomic_read(&port->status) & ZFCP_STATUS_PORT_DID_DID))
+ /* Try to connect to unused ports anyway. */
+ zfcp_erp_port_reopen(port,
+ ZFCP_STATUS_COMMON_ERP_FAILED,
+ 82, fsf_req);
+ else if ((port->d_id & range) == (elem->nport_did & range))
+ /* Check connection status for connected ports */
+ zfcp_test_link(port);
+ }
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+}
+
+static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
+{
+ struct fsf_status_read_buffer *status_buffer = (void *)fsf_req->data;
+ struct fcp_rscn_head *fcp_rscn_head;
+ struct fcp_rscn_element *fcp_rscn_element;
+ u16 i;
+ u16 no_entries;
+ u32 range_mask;
+
+ fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload.data;
+ fcp_rscn_element = (struct fcp_rscn_element *) fcp_rscn_head;
+
+ /* see FC-FS */
+ no_entries = fcp_rscn_head->payload_len /
+ sizeof(struct fcp_rscn_element);
+
+ for (i = 1; i < no_entries; i++) {
+ /* skip head and start with 1st element */
+ fcp_rscn_element++;
+ switch (fcp_rscn_element->addr_format) {
+ case ZFCP_PORT_ADDRESS:
+ range_mask = ZFCP_PORTS_RANGE_PORT;
+ break;
+ case ZFCP_AREA_ADDRESS:
+ range_mask = ZFCP_PORTS_RANGE_AREA;
+ break;
+ case ZFCP_DOMAIN_ADDRESS:
+ range_mask = ZFCP_PORTS_RANGE_DOMAIN;
+ break;
+ case ZFCP_FABRIC_ADDRESS:
+ range_mask = ZFCP_PORTS_RANGE_FABRIC;
+ break;
+ default:
+ continue;
+ }
+ _zfcp_fc_incoming_rscn(fsf_req, range_mask, fcp_rscn_element);
+ }
+ schedule_work(&fsf_req->adapter->scan_work);
+}
+
+static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, u64 wwpn)
+{
+ struct zfcp_adapter *adapter = req->adapter;
+ struct zfcp_port *port;
+ unsigned long flags;
+
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ list_for_each_entry(port, &adapter->port_list_head, list)
+ if (port->wwpn == wwpn)
+ break;
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+ if (port && (port->wwpn == wwpn))
+ zfcp_erp_port_forced_reopen(port, 0, 83, req);
+}
+
+static void zfcp_fc_incoming_plogi(struct zfcp_fsf_req *req)
+{
+ struct fsf_status_read_buffer *status_buffer =
+ (struct fsf_status_read_buffer *)req->data;
+ struct fsf_plogi *els_plogi =
+ (struct fsf_plogi *) status_buffer->payload.data;
+
+ zfcp_fc_incoming_wwpn(req, els_plogi->serv_param.wwpn);
+}
+
+static void zfcp_fc_incoming_logo(struct zfcp_fsf_req *req)
+{
+ struct fsf_status_read_buffer *status_buffer =
+ (struct fsf_status_read_buffer *)req->data;
+ struct fcp_logo *els_logo =
+ (struct fcp_logo *) status_buffer->payload.data;
+
+ zfcp_fc_incoming_wwpn(req, els_logo->nport_wwpn);
+}
+
+/**
+ * zfcp_fc_incoming_els - handle incoming ELS
+ * @fsf_req - request which contains incoming ELS
+ */
+void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req)
+{
+ struct fsf_status_read_buffer *status_buffer =
+ (struct fsf_status_read_buffer *) fsf_req->data;
+ unsigned int els_type = status_buffer->payload.data[0];
+
+ zfcp_san_dbf_event_incoming_els(fsf_req);
+ if (els_type == LS_PLOGI)
+ zfcp_fc_incoming_plogi(fsf_req);
+ else if (els_type == LS_LOGO)
+ zfcp_fc_incoming_logo(fsf_req);
+ else if (els_type == LS_RSCN)
+ zfcp_fc_incoming_rscn(fsf_req);
+}
+
+static void zfcp_fc_ns_handler(unsigned long data)
+{
+ struct zfcp_fc_ns_handler_data *compl_rec =
+ (struct zfcp_fc_ns_handler_data *) data;
+
+ if (compl_rec->handler)
+ compl_rec->handler(compl_rec->handler_data);
+
+ complete(&compl_rec->done);
+}
+
+static void zfcp_fc_ns_gid_pn_eval(unsigned long data)
+{
+ struct zfcp_gid_pn_data *gid_pn = (struct zfcp_gid_pn_data *) data;
+ struct zfcp_send_ct *ct = &gid_pn->ct;
+ struct ct_iu_gid_pn_req *ct_iu_req = sg_virt(ct->req);
+ struct ct_iu_gid_pn_resp *ct_iu_resp = sg_virt(ct->resp);
+ struct zfcp_port *port = gid_pn->port;
+
+ if (ct->status)
+ return;
+ if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT) {
+ atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
+ return;
+ }
+ /* paranoia */
+ if (ct_iu_req->wwpn != port->wwpn)
+ return;
+ /* looks like a valid d_id */
+ port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
+ atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
+}
+
+int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
+ struct zfcp_gid_pn_data *gid_pn)
+{
+ struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_fc_ns_handler_data compl_rec;
+ int ret;
+
+ /* setup parameters for send generic command */
+ gid_pn->port = erp_action->port;
+ gid_pn->ct.wka_port = &adapter->nsp;
+ gid_pn->ct.handler = zfcp_fc_ns_handler;
+ gid_pn->ct.handler_data = (unsigned long) &compl_rec;
+ gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
+ gid_pn->ct.req = &gid_pn->req;
+ gid_pn->ct.resp = &gid_pn->resp;
+ gid_pn->ct.req_count = 1;
+ gid_pn->ct.resp_count = 1;
+ sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req,
+ sizeof(struct ct_iu_gid_pn_req));
+ sg_init_one(&gid_pn->resp, &gid_pn->ct_iu_resp,
+ sizeof(struct ct_iu_gid_pn_resp));
+
+ /* setup nameserver request */
+ gid_pn->ct_iu_req.header.revision = ZFCP_CT_REVISION;
+ gid_pn->ct_iu_req.header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
+ gid_pn->ct_iu_req.header.gs_subtype = ZFCP_CT_NAME_SERVER;
+ gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS;
+ gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN;
+ gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_MAX_SIZE;
+ gid_pn->ct_iu_req.wwpn = erp_action->port->wwpn;
+
+ init_completion(&compl_rec.done);
+ compl_rec.handler = zfcp_fc_ns_gid_pn_eval;
+ compl_rec.handler_data = (unsigned long) gid_pn;
+ ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp,
+ erp_action);
+ if (!ret)
+ wait_for_completion(&compl_rec.done);
+ return ret;
+}
+
+/**
+ * zfcp_fc_ns_gid_pn_request - initiate GID_PN nameserver request
+ * @erp_action: pointer to zfcp_erp_action where GID_PN request is needed
+ * return: -ENOMEM on error, 0 otherwise
+ */
+int zfcp_fc_ns_gid_pn(struct zfcp_erp_action *erp_action)
+{
+ int ret;
+ struct zfcp_gid_pn_data *gid_pn;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+
+ gid_pn = mempool_alloc(adapter->pool.data_gid_pn, GFP_ATOMIC);
+ if (!gid_pn)
+ return -ENOMEM;
+
+ memset(gid_pn, 0, sizeof(*gid_pn));
+
+ ret = zfcp_wka_port_get(&adapter->nsp);
+ if (ret)
+ goto out;
+
+ ret = zfcp_fc_ns_gid_pn_request(erp_action, gid_pn);
+
+ zfcp_wka_port_put(&adapter->nsp);
+out:
+ mempool_free(gid_pn, adapter->pool.data_gid_pn);
+ return ret;
+}
+
+/**
+ * zfcp_fc_plogi_evaluate - evaluate PLOGI playload
+ * @port: zfcp_port structure
+ * @plogi: plogi payload
+ *
+ * Evaluate PLOGI playload and copy important fields into zfcp_port structure
+ */
+void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fsf_plogi *plogi)
+{
+ port->maxframe_size = plogi->serv_param.common_serv_param[7] |
+ ((plogi->serv_param.common_serv_param[6] & 0x0F) << 8);
+ if (plogi->serv_param.class1_serv_param[0] & 0x80)
+ port->supported_classes |= FC_COS_CLASS1;
+ if (plogi->serv_param.class2_serv_param[0] & 0x80)
+ port->supported_classes |= FC_COS_CLASS2;
+ if (plogi->serv_param.class3_serv_param[0] & 0x80)
+ port->supported_classes |= FC_COS_CLASS3;
+ if (plogi->serv_param.class4_serv_param[0] & 0x80)
+ port->supported_classes |= FC_COS_CLASS4;
+}
+
+struct zfcp_els_adisc {
+ struct zfcp_send_els els;
+ struct scatterlist req;
+ struct scatterlist resp;
+ struct zfcp_ls_adisc ls_adisc;
+ struct zfcp_ls_adisc ls_adisc_acc;
+};
+
+static void zfcp_fc_adisc_handler(unsigned long data)
+{
+ struct zfcp_els_adisc *adisc = (struct zfcp_els_adisc *) data;
+ struct zfcp_port *port = adisc->els.port;
+ struct zfcp_ls_adisc *ls_adisc = &adisc->ls_adisc_acc;
+
+ if (adisc->els.status) {
+ /* request rejected or timed out */
+ zfcp_erp_port_forced_reopen(port, 0, 63, NULL);
+ goto out;
+ }
+
+ if (!port->wwnn)
+ port->wwnn = ls_adisc->wwnn;
+
+ if (port->wwpn != ls_adisc->wwpn)
+ zfcp_erp_port_reopen(port, 0, 64, NULL);
+
+ out:
+ zfcp_port_put(port);
+ kfree(adisc);
+}
+
+static int zfcp_fc_adisc(struct zfcp_port *port)
+{
+ struct zfcp_els_adisc *adisc;
+ struct zfcp_adapter *adapter = port->adapter;
+
+ adisc = kzalloc(sizeof(struct zfcp_els_adisc), GFP_ATOMIC);
+ if (!adisc)
+ return -ENOMEM;
+
+ adisc->els.req = &adisc->req;
+ adisc->els.resp = &adisc->resp;
+ sg_init_one(adisc->els.req, &adisc->ls_adisc,
+ sizeof(struct zfcp_ls_adisc));
+ sg_init_one(adisc->els.resp, &adisc->ls_adisc_acc,
+ sizeof(struct zfcp_ls_adisc));
+
+ adisc->els.req_count = 1;
+ adisc->els.resp_count = 1;
+ adisc->els.adapter = adapter;
+ adisc->els.port = port;
+ adisc->els.d_id = port->d_id;
+ adisc->els.handler = zfcp_fc_adisc_handler;
+ adisc->els.handler_data = (unsigned long) adisc;
+ adisc->els.ls_code = adisc->ls_adisc.code = ZFCP_LS_ADISC;
+
+ /* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
+ without FC-AL-2 capability, so we don't set it */
+ adisc->ls_adisc.wwpn = fc_host_port_name(adapter->scsi_host);
+ adisc->ls_adisc.wwnn = fc_host_node_name(adapter->scsi_host);
+ adisc->ls_adisc.nport_id = fc_host_port_id(adapter->scsi_host);
+
+ return zfcp_fsf_send_els(&adisc->els);
+}
+
+/**
+ * zfcp_test_link - lightweight link test procedure
+ * @port: port to be tested
+ *
+ * Test status of a link to a remote port using the ELS command ADISC.
+ * If there is a problem with the remote port, error recovery steps
+ * will be triggered.
+ */
+void zfcp_test_link(struct zfcp_port *port)
+{
+ int retval;
+
+ zfcp_port_get(port);
+ retval = zfcp_fc_adisc(port);
+ if (retval == 0)
+ return;
+
+ /* send of ADISC was not possible */
+ zfcp_port_put(port);
+ if (retval != -EBUSY)
+ zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
+}
+
+static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft)
+{
+ struct scatterlist *sg = &gpn_ft->sg_req;
+
+ kfree(sg_virt(sg)); /* free request buffer */
+ zfcp_sg_free_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS);
+
+ kfree(gpn_ft);
+}
+
+static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
+{
+ struct zfcp_gpn_ft *gpn_ft;
+ struct ct_iu_gpn_ft_req *req;
+
+ gpn_ft = kzalloc(sizeof(*gpn_ft), GFP_KERNEL);
+ if (!gpn_ft)
+ return NULL;
+
+ req = kzalloc(sizeof(struct ct_iu_gpn_ft_req), GFP_KERNEL);
+ if (!req) {
+ kfree(gpn_ft);
+ gpn_ft = NULL;
+ goto out;
+ }
+ sg_init_one(&gpn_ft->sg_req, req, sizeof(*req));
+
+ if (zfcp_sg_setup_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS)) {
+ zfcp_free_sg_env(gpn_ft);
+ gpn_ft = NULL;
+ }
+out:
+ return gpn_ft;
+}
+
+
+static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
+ struct zfcp_adapter *adapter)
+{
+ struct zfcp_send_ct *ct = &gpn_ft->ct;
+ struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
+ struct zfcp_fc_ns_handler_data compl_rec;
+ int ret;
+
+ /* prepare CT IU for GPN_FT */
+ req->header.revision = ZFCP_CT_REVISION;
+ req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
+ req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
+ req->header.options = ZFCP_CT_SYNCHRONOUS;
+ req->header.cmd_rsp_code = ZFCP_CT_GPN_FT;
+ req->header.max_res_size = (sizeof(struct gpn_ft_resp_acc) *
+ (ZFCP_GPN_FT_MAX_ENTRIES - 1)) >> 2;
+ req->flags = 0;
+ req->domain_id_scope = 0;
+ req->area_id_scope = 0;
+ req->fc4_type = ZFCP_CT_SCSI_FCP;
+
+ /* prepare zfcp_send_ct */
+ ct->wka_port = &adapter->nsp;
+ ct->handler = zfcp_fc_ns_handler;
+ ct->handler_data = (unsigned long)&compl_rec;
+ ct->timeout = 10;
+ ct->req = &gpn_ft->sg_req;
+ ct->resp = gpn_ft->sg_resp;
+ ct->req_count = 1;
+ ct->resp_count = ZFCP_GPN_FT_BUFFERS;
+
+ init_completion(&compl_rec.done);
+ compl_rec.handler = NULL;
+ ret = zfcp_fsf_send_ct(ct, NULL, NULL);
+ if (!ret)
+ wait_for_completion(&compl_rec.done);
+ return ret;
+}
+
+static void zfcp_validate_port(struct zfcp_port *port)
+{
+ struct zfcp_adapter *adapter = port->adapter;
+
+ atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status);
+
+ if ((port->supported_classes != 0) ||
+ !list_empty(&port->unit_list_head)) {
+ zfcp_port_put(port);
+ return;
+ }
+ zfcp_erp_port_shutdown(port, 0, 151, NULL);
+ zfcp_erp_wait(adapter);
+ zfcp_port_put(port);
+ zfcp_port_dequeue(port);
+}
+
+static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
+{
+ struct zfcp_send_ct *ct = &gpn_ft->ct;
+ struct scatterlist *sg = gpn_ft->sg_resp;
+ struct ct_hdr *hdr = sg_virt(sg);
+ struct gpn_ft_resp_acc *acc = sg_virt(sg);
+ struct zfcp_adapter *adapter = ct->wka_port->adapter;
+ struct zfcp_port *port, *tmp;
+ u32 d_id;
+ int ret = 0, x, last = 0;
+
+ if (ct->status)
+ return -EIO;
+
+ if (hdr->cmd_rsp_code != ZFCP_CT_ACCEPT) {
+ if (hdr->reason_code == ZFCP_CT_UNABLE_TO_PERFORM_CMD)
+ return -EAGAIN; /* might be a temporary condition */
+ return -EIO;
+ }
+
+ if (hdr->max_res_size)
+ return -E2BIG;
+
+ down(&zfcp_data.config_sema);
+
+ /* first entry is the header */
+ for (x = 1; x < ZFCP_GPN_FT_MAX_ENTRIES && !last; x++) {
+ if (x % (ZFCP_GPN_FT_ENTRIES + 1))
+ acc++;
+ else
+ acc = sg_virt(++sg);
+
+ last = acc->control & 0x80;
+ d_id = acc->port_id[0] << 16 | acc->port_id[1] << 8 |
+ acc->port_id[2];
+
+ /* don't attach ports with a well known address */
+ if ((d_id & ZFCP_DID_WKA) == ZFCP_DID_WKA)
+ continue;
+ /* skip the adapter's port and known remote ports */
+ if (acc->wwpn == fc_host_port_name(adapter->scsi_host))
+ continue;
+ port = zfcp_get_port_by_wwpn(adapter, acc->wwpn);
+ if (port) {
+ zfcp_port_get(port);
+ continue;
+ }
+
+ port = zfcp_port_enqueue(adapter, acc->wwpn,
+ ZFCP_STATUS_PORT_DID_DID |
+ ZFCP_STATUS_COMMON_NOESC, d_id);
+ if (IS_ERR(port))
+ ret = PTR_ERR(port);
+ else
+ zfcp_erp_port_reopen(port, 0, 149, NULL);
+ }
+
+ zfcp_erp_wait(adapter);
+ list_for_each_entry_safe(port, tmp, &adapter->port_list_head, list)
+ zfcp_validate_port(port);
+ up(&zfcp_data.config_sema);
+ return ret;
+}
+
+/**
+ * zfcp_scan_ports - scan remote ports and attach new ports
+ * @adapter: pointer to struct zfcp_adapter
+ */
+int zfcp_scan_ports(struct zfcp_adapter *adapter)
+{
+ int ret, i;
+ struct zfcp_gpn_ft *gpn_ft;
+
+ zfcp_erp_wait(adapter); /* wait until adapter is finished with ERP */
+ if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT)
+ return 0;
+
+ ret = zfcp_wka_port_get(&adapter->nsp);
+ if (ret)
+ return ret;
+
+ gpn_ft = zfcp_alloc_sg_env();
+ if (!gpn_ft) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter);
+ if (!ret) {
+ ret = zfcp_scan_eval_gpn_ft(gpn_ft);
+ if (ret == -EAGAIN)
+ ssleep(1);
+ else
+ break;
+ }
+ }
+ zfcp_free_sg_env(gpn_ft);
+out:
+ zfcp_wka_port_put(&adapter->nsp);
+ return ret;
+}
+
+
+void _zfcp_scan_ports_later(struct work_struct *work)
+{
+ zfcp_scan_ports(container_of(work, struct zfcp_adapter, scan_work));
+}
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index b2ea4ea051f..739356a5c12 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -1,54 +1,37 @@
/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
*
- * (C) Copyright IBM Corp. 2002, 2006
+ * Implementation of FSF commands.
*
- * 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 Corporation 2002, 2008
*/
#include "zfcp_ext.h"
-static int zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *);
-static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_open_port_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_close_port_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_send_fcp_command_task_management_handler(
- struct zfcp_fsf_req *);
-static int zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_status_read_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_control_file_handler(struct zfcp_fsf_req *);
-static inline int zfcp_fsf_req_sbal_check(
- unsigned long *, struct zfcp_qdio_queue *, int);
-static inline int zfcp_use_one_sbal(
- struct scatterlist *, int, struct scatterlist *, int);
-static struct zfcp_fsf_req *zfcp_fsf_req_alloc(mempool_t *, int);
-static int zfcp_fsf_req_send(struct zfcp_fsf_req *);
-static int zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *);
-static int zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *);
-static int zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *);
-static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *, u8,
- struct fsf_link_down_info *);
-static int zfcp_fsf_req_dispatch(struct zfcp_fsf_req *);
+static void zfcp_fsf_request_timeout_handler(unsigned long data)
+{
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
+ zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 62,
+ NULL);
+}
+
+static void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req,
+ unsigned long timeout)
+{
+ fsf_req->timer.function = zfcp_fsf_request_timeout_handler;
+ fsf_req->timer.data = (unsigned long) fsf_req->adapter;
+ fsf_req->timer.expires = jiffies + timeout;
+ add_timer(&fsf_req->timer);
+}
+
+static void zfcp_fsf_start_erp_timer(struct zfcp_fsf_req *fsf_req)
+{
+ BUG_ON(!fsf_req->erp_action);
+ fsf_req->timer.function = zfcp_erp_timeout_handler;
+ fsf_req->timer.data = (unsigned long) fsf_req->erp_action;
+ fsf_req->timer.expires = jiffies + 30 * HZ;
+ add_timer(&fsf_req->timer);
+}
/* association between FSF command and FSF QTCB type */
static u32 fsf_qtcb_type[] = {
@@ -67,96 +50,74 @@ static u32 fsf_qtcb_type[] = {
[FSF_QTCB_UPLOAD_CONTROL_FILE] = FSF_SUPPORT_COMMAND
};
-static const char zfcp_act_subtable_type[5][8] = {
- "unknown", "OS", "WWPN", "DID", "LUN"
-};
-
-/****************************************************************/
-/*************** FSF related Functions *************************/
-/****************************************************************/
-
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_FSF
-
-/*
- * function: zfcp_fsf_req_alloc
- *
- * purpose: Obtains an fsf_req and potentially a qtcb (for all but
- * unsolicited requests) via helper functions
- * Does some initial fsf request set-up.
- *
- * returns: pointer to allocated fsf_req if successfull
- * NULL otherwise
- *
- * locks: none
- *
- */
-static struct zfcp_fsf_req *
-zfcp_fsf_req_alloc(mempool_t *pool, int req_flags)
+static void zfcp_act_eval_err(struct zfcp_adapter *adapter, u32 table)
{
- size_t size;
- void *ptr;
- struct zfcp_fsf_req *fsf_req = NULL;
-
- if (req_flags & ZFCP_REQ_NO_QTCB)
- size = sizeof(struct zfcp_fsf_req);
- else
- size = sizeof(struct zfcp_fsf_req_qtcb);
-
- if (likely(pool))
- ptr = mempool_alloc(pool, GFP_ATOMIC);
- else {
- if (req_flags & ZFCP_REQ_NO_QTCB)
- ptr = kmalloc(size, GFP_ATOMIC);
- else
- ptr = kmem_cache_alloc(zfcp_data.fsf_req_qtcb_cache,
- GFP_ATOMIC);
- }
-
- if (unlikely(!ptr))
- goto out;
-
- memset(ptr, 0, size);
+ u16 subtable = table >> 16;
+ u16 rule = table & 0xffff;
+ const char *act_type[] = { "unknown", "OS", "WWPN", "DID", "LUN" };
+
+ if (subtable && subtable < ARRAY_SIZE(act_type))
+ dev_warn(&adapter->ccw_device->dev,
+ "Access denied according to ACT rule type %s, "
+ "rule %d\n", act_type[subtable], rule);
+}
- if (req_flags & ZFCP_REQ_NO_QTCB) {
- fsf_req = (struct zfcp_fsf_req *) ptr;
- } else {
- fsf_req = &((struct zfcp_fsf_req_qtcb *) ptr)->fsf_req;
- fsf_req->qtcb = &((struct zfcp_fsf_req_qtcb *) ptr)->qtcb;
- }
+static void zfcp_fsf_access_denied_port(struct zfcp_fsf_req *req,
+ struct zfcp_port *port)
+{
+ struct fsf_qtcb_header *header = &req->qtcb->header;
+ dev_warn(&req->adapter->ccw_device->dev,
+ "Access denied to port 0x%016Lx\n",
+ (unsigned long long)port->wwpn);
+ zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[0]);
+ zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[1]);
+ zfcp_erp_port_access_denied(port, 55, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+}
- fsf_req->pool = pool;
+static void zfcp_fsf_access_denied_unit(struct zfcp_fsf_req *req,
+ struct zfcp_unit *unit)
+{
+ struct fsf_qtcb_header *header = &req->qtcb->header;
+ dev_warn(&req->adapter->ccw_device->dev,
+ "Access denied to unit 0x%016Lx on port 0x%016Lx\n",
+ (unsigned long long)unit->fcp_lun,
+ (unsigned long long)unit->port->wwpn);
+ zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[0]);
+ zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[1]);
+ zfcp_erp_unit_access_denied(unit, 59, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+}
- out:
- return fsf_req;
+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, 123, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
-/*
- * function: zfcp_fsf_req_free
- *
- * purpose: Frees the memory of an fsf_req (and potentially a qtcb) or
- * returns it into the pool via helper functions.
- *
- * returns: sod all
- *
- * locks: none
+/**
+ * zfcp_fsf_req_free - free memory used by fsf request
+ * @fsf_req: pointer to struct zfcp_fsf_req
*/
-void
-zfcp_fsf_req_free(struct zfcp_fsf_req *fsf_req)
+void zfcp_fsf_req_free(struct zfcp_fsf_req *req)
{
- if (likely(fsf_req->pool)) {
- mempool_free(fsf_req, fsf_req->pool);
+ if (likely(req->pool)) {
+ mempool_free(req, req->pool);
return;
}
- if (fsf_req->qtcb) {
- kmem_cache_free(zfcp_data.fsf_req_qtcb_cache, fsf_req);
+ if (req->qtcb) {
+ kmem_cache_free(zfcp_data.fsf_req_qtcb_cache, req);
return;
}
-
- kfree(fsf_req);
}
-/*
+/**
+ * zfcp_fsf_req_dismiss_all - dismiss all fsf requests
+ * @adapter: pointer to struct zfcp_adapter
+ *
* Never ever call this without shutting down the adapter first.
* Otherwise the adapter would continue using and corrupting s390 storage.
* Included BUG_ON() call to ensure this is done.
@@ -164,2353 +125,1301 @@ zfcp_fsf_req_free(struct zfcp_fsf_req *fsf_req)
*/
void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter)
{
- struct zfcp_fsf_req *fsf_req, *tmp;
+ struct zfcp_fsf_req *req, *tmp;
unsigned long flags;
LIST_HEAD(remove_queue);
unsigned int i;
- BUG_ON(atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status));
+ BUG_ON(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP);
spin_lock_irqsave(&adapter->req_list_lock, flags);
- atomic_set(&adapter->reqs_active, 0);
for (i = 0; i < REQUEST_LIST_SIZE; i++)
list_splice_init(&adapter->req_list[i], &remove_queue);
spin_unlock_irqrestore(&adapter->req_list_lock, flags);
- list_for_each_entry_safe(fsf_req, tmp, &remove_queue, list) {
- list_del(&fsf_req->list);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
- zfcp_fsf_req_complete(fsf_req);
+ list_for_each_entry_safe(req, tmp, &remove_queue, list) {
+ list_del(&req->list);
+ req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
+ zfcp_fsf_req_complete(req);
}
}
-/*
- * function: zfcp_fsf_req_complete
- *
- * purpose: Updates active counts and timers for openfcp-reqs
- * May cleanup request after req_eval returns
- *
- * returns: 0 - success
- * !0 - failure
- *
- * context:
- */
-int
-zfcp_fsf_req_complete(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *req)
{
- int retval = 0;
- int cleanup;
-
- if (unlikely(fsf_req->fsf_command == FSF_QTCB_UNSOLICITED_STATUS)) {
- ZFCP_LOG_DEBUG("Status read response received\n");
- /*
- * Note: all cleanup handling is done in the callchain of
- * the function call-chain below.
- */
- zfcp_fsf_status_read_handler(fsf_req);
- goto out;
- } else {
- del_timer(&fsf_req->timer);
- zfcp_fsf_protstatus_eval(fsf_req);
- }
-
- /*
- * fsf_req may be deleted due to waking up functions, so
- * cleanup is saved here and used later
- */
- if (likely(fsf_req->status & ZFCP_STATUS_FSFREQ_CLEANUP))
- cleanup = 1;
- else
- cleanup = 0;
-
- fsf_req->status |= ZFCP_STATUS_FSFREQ_COMPLETED;
-
- /* cleanup request if requested by initiator */
- if (likely(cleanup)) {
- ZFCP_LOG_TRACE("removing FSF request %p\n", fsf_req);
- /*
- * lock must not be held here since it will be
- * grabed by the called routine, too
- */
- zfcp_fsf_req_free(fsf_req);
- } else {
- /* notify initiator waiting for the requests completion */
- ZFCP_LOG_TRACE("waking initiator of FSF request %p\n",fsf_req);
- /*
- * FIXME: Race! We must not access fsf_req here as it might have been
- * cleaned up already due to the set ZFCP_STATUS_FSFREQ_COMPLETED
- * flag. It's an improbable case. But, we have the same paranoia for
- * the cleanup flag already.
- * Might better be handled using complete()?
- * (setting the flag and doing wakeup ought to be atomic
- * with regard to checking the flag as long as waitqueue is
- * part of the to be released structure)
- */
- wake_up(&fsf_req->completion_wq);
- }
+ struct fsf_status_read_buffer *sr_buf = req->data;
+ struct zfcp_adapter *adapter = req->adapter;
+ struct zfcp_port *port;
+ int d_id = sr_buf->d_id & ZFCP_DID_MASK;
+ unsigned long flags;
- out:
- return retval;
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ list_for_each_entry(port, &adapter->port_list_head, list)
+ if (port->d_id == d_id) {
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ switch (sr_buf->status_subtype) {
+ case FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT:
+ zfcp_erp_port_reopen(port, 0, 101, req);
+ break;
+ case FSF_STATUS_READ_SUB_ERROR_PORT:
+ zfcp_erp_port_shutdown(port, 0, 122, req);
+ break;
+ }
+ return;
+ }
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
-/*
- * function: zfcp_fsf_protstatus_eval
- *
- * purpose: evaluates the QTCB of the finished FSF request
- * and initiates appropriate actions
- * (usually calling FSF command specific handlers)
- *
- * returns:
- *
- * context:
- *
- * locks:
- */
-static int
-zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, u8 id,
+ struct fsf_link_down_info *link_down)
{
- int retval = 0;
- struct zfcp_adapter *adapter = fsf_req->adapter;
- struct fsf_qtcb *qtcb = fsf_req->qtcb;
- union fsf_prot_status_qual *prot_status_qual =
- &qtcb->prefix.prot_status_qual;
-
- zfcp_hba_dbf_event_fsf_response(fsf_req);
-
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
- ZFCP_LOG_DEBUG("fsf_req 0x%lx has been dismissed\n",
- (unsigned long) fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY; /* only for SCSI cmnds. */
- goto skip_protstatus;
- }
+ struct zfcp_adapter *adapter = req->adapter;
- /* evaluate FSF Protocol Status */
- switch (qtcb->prefix.prot_status) {
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED)
+ return;
- case FSF_PROT_GOOD:
- case FSF_PROT_FSF_STATUS_PRESENTED:
- break;
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
- case FSF_PROT_QTCB_VERSION_ERROR:
- ZFCP_LOG_NORMAL("error: The adapter %s contains "
- "microcode of version 0x%x, the device driver "
- "only supports 0x%x. Aborting.\n",
- zfcp_get_busid_by_adapter(adapter),
- prot_status_qual->version_error.fsf_version,
- ZFCP_QTCB_VERSION);
- zfcp_erp_adapter_shutdown(adapter, 0, 117, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
+ if (!link_down)
+ goto out;
- case FSF_PROT_SEQ_NUMB_ERROR:
- ZFCP_LOG_NORMAL("bug: Sequence number mismatch between "
- "driver (0x%x) and adapter %s (0x%x). "
- "Restarting all operations on this adapter.\n",
- qtcb->prefix.req_seq_no,
- zfcp_get_busid_by_adapter(adapter),
- prot_status_qual->sequence_error.exp_req_seq_no);
- zfcp_erp_adapter_reopen(adapter, 0, 98, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY;
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ switch (link_down->error_code) {
+ case FSF_PSQ_LINK_NO_LIGHT:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "There is no light signal from the local "
+ "fibre channel cable\n");
break;
-
- case FSF_PROT_UNSUPP_QTCB_TYPE:
- ZFCP_LOG_NORMAL("error: Packet header type used by the "
- "device driver is incompatible with "
- "that used on adapter %s. "
- "Stopping all operations on this adapter.\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_erp_adapter_shutdown(adapter, 0, 118, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ case FSF_PSQ_LINK_WRAP_PLUG:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "There is a wrap plug instead of a fibre "
+ "channel cable\n");
break;
-
- case FSF_PROT_HOST_CONNECTION_INITIALIZING:
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
- &(adapter->status));
+ case FSF_PSQ_LINK_NO_FCP:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The adjacent fibre channel node does not "
+ "support FCP\n");
break;
-
- case FSF_PROT_DUPLICATE_REQUEST_ID:
- ZFCP_LOG_NORMAL("bug: The request identifier 0x%Lx "
- "to the adapter %s is ambiguous. "
- "Stopping all operations on this adapter.\n",
- *(unsigned long long*)
- (&qtcb->bottom.support.req_handle),
- zfcp_get_busid_by_adapter(adapter));
- zfcp_erp_adapter_shutdown(adapter, 0, 78, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ case FSF_PSQ_LINK_FIRMWARE_UPDATE:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The FCP device is suspended because of a "
+ "firmware update\n");
break;
-
- case FSF_PROT_LINK_DOWN:
- zfcp_fsf_link_down_info_eval(fsf_req, 37,
- &prot_status_qual->link_down_info);
- /* FIXME: reopening adapter now? better wait for link up */
- zfcp_erp_adapter_reopen(adapter, 0, 79, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ case FSF_PSQ_LINK_INVALID_WWPN:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The FCP device detected a WWPN that is "
+ "duplicate or not valid\n");
break;
-
- case FSF_PROT_REEST_QUEUE:
- ZFCP_LOG_NORMAL("The local link to adapter with "
- "%s was re-plugged. "
- "Re-starting operations on this adapter.\n",
- zfcp_get_busid_by_adapter(adapter));
- /* All ports should be marked as ready to run again */
- zfcp_erp_modify_adapter_status(adapter, 28, NULL,
- ZFCP_STATUS_COMMON_RUNNING,
- ZFCP_SET);
- zfcp_erp_adapter_reopen(adapter,
- ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED
- | ZFCP_STATUS_COMMON_ERP_FAILED,
- 99, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ case FSF_PSQ_LINK_NO_NPIV_SUPPORT:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The fibre channel fabric does not support NPIV\n");
break;
-
- case FSF_PROT_ERROR_STATE:
- ZFCP_LOG_NORMAL("error: The adapter %s "
- "has entered the error state. "
- "Restarting all operations on this "
- "adapter.\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_erp_adapter_reopen(adapter, 0, 100, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY;
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ case FSF_PSQ_LINK_NO_FCP_RESOURCES:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The FCP adapter cannot support more NPIV ports\n");
+ break;
+ case FSF_PSQ_LINK_NO_FABRIC_RESOURCES:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The adjacent switch cannot support "
+ "more NPIV ports\n");
+ break;
+ case FSF_PSQ_LINK_FABRIC_LOGIN_UNABLE:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The FCP adapter could not log in to the "
+ "fibre channel fabric\n");
+ break;
+ case FSF_PSQ_LINK_WWPN_ASSIGNMENT_CORRUPTED:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The WWPN assignment file on the FCP adapter "
+ "has been damaged\n");
+ break;
+ case FSF_PSQ_LINK_MODE_TABLE_CURRUPTED:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The mode table on the FCP adapter "
+ "has been damaged\n");
+ break;
+ case FSF_PSQ_LINK_NO_WWPN_ASSIGNMENT:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "All NPIV ports on the FCP adapter have "
+ "been assigned\n");
break;
-
default:
- ZFCP_LOG_NORMAL("bug: Transfer protocol status information "
- "provided by the adapter %s "
- "is not compatible with the device driver. "
- "Stopping all operations on this adapter. "
- "(debug info 0x%x).\n",
- zfcp_get_busid_by_adapter(adapter),
- qtcb->prefix.prot_status);
- zfcp_erp_adapter_shutdown(adapter, 0, 119, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The link between the FCP adapter and "
+ "the FC fabric is down\n");
}
+out:
+ zfcp_erp_adapter_failed(adapter, id, req);
+}
- skip_protstatus:
- /*
- * always call specific handlers to give them a chance to do
- * something meaningful even in error cases
- */
- zfcp_fsf_fsfstatus_eval(fsf_req);
- return retval;
+static void zfcp_fsf_status_read_link_down(struct zfcp_fsf_req *req)
+{
+ struct fsf_status_read_buffer *sr_buf = req->data;
+ struct fsf_link_down_info *ldi =
+ (struct fsf_link_down_info *) &sr_buf->payload;
+
+ switch (sr_buf->status_subtype) {
+ case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK:
+ zfcp_fsf_link_down_info_eval(req, 38, ldi);
+ break;
+ case FSF_STATUS_READ_SUB_FDISC_FAILED:
+ zfcp_fsf_link_down_info_eval(req, 39, ldi);
+ break;
+ case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE:
+ zfcp_fsf_link_down_info_eval(req, 40, NULL);
+ };
}
-/*
- * function: zfcp_fsf_fsfstatus_eval
- *
- * purpose: evaluates FSF status of completed FSF request
- * and acts accordingly
- *
- * returns:
- */
-static int
-zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
{
- int retval = 0;
+ struct zfcp_adapter *adapter = req->adapter;
+ struct fsf_status_read_buffer *sr_buf = req->data;
- if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
- goto skip_fsfstatus;
+ if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
+ zfcp_hba_dbf_event_fsf_unsol("dism", adapter, sr_buf);
+ mempool_free(sr_buf, adapter->pool.data_status_read);
+ zfcp_fsf_req_free(req);
+ return;
}
- /* evaluate FSF Status */
- switch (fsf_req->qtcb->header.fsf_status) {
- case FSF_UNKNOWN_COMMAND:
- ZFCP_LOG_NORMAL("bug: Command issued by the device driver is "
- "not known by the adapter %s "
- "Stopping all operations on this adapter. "
- "(debug info 0x%x).\n",
- zfcp_get_busid_by_adapter(fsf_req->adapter),
- fsf_req->qtcb->header.fsf_command);
- zfcp_erp_adapter_shutdown(fsf_req->adapter, 0, 120, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
+ zfcp_hba_dbf_event_fsf_unsol("read", adapter, sr_buf);
- case FSF_FCP_RSP_AVAILABLE:
- ZFCP_LOG_DEBUG("FCP Sense data will be presented to the "
- "SCSI stack.\n");
+ switch (sr_buf->status_type) {
+ case FSF_STATUS_READ_PORT_CLOSED:
+ zfcp_fsf_status_read_port_closed(req);
break;
-
- case FSF_ADAPTER_STATUS_AVAILABLE:
- zfcp_fsf_fsfstatus_qual_eval(fsf_req);
+ case FSF_STATUS_READ_INCOMING_ELS:
+ zfcp_fc_incoming_els(req);
+ break;
+ case FSF_STATUS_READ_SENSE_DATA_AVAIL:
+ break;
+ case FSF_STATUS_READ_BIT_ERROR_THRESHOLD:
+ dev_warn(&adapter->ccw_device->dev,
+ "The error threshold for checksum statistics "
+ "has been exceeded\n");
+ zfcp_hba_dbf_event_berr(adapter, req);
+ break;
+ case FSF_STATUS_READ_LINK_DOWN:
+ zfcp_fsf_status_read_link_down(req);
+ break;
+ case FSF_STATUS_READ_LINK_UP:
+ dev_info(&adapter->ccw_device->dev,
+ "The local link has been restored\n");
+ /* All ports should be marked as ready to run again */
+ zfcp_erp_modify_adapter_status(adapter, 30, NULL,
+ ZFCP_STATUS_COMMON_RUNNING,
+ ZFCP_SET);
+ zfcp_erp_adapter_reopen(adapter,
+ ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
+ ZFCP_STATUS_COMMON_ERP_FAILED,
+ 102, req);
+ break;
+ case FSF_STATUS_READ_NOTIFICATION_LOST:
+ if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_ACT_UPDATED)
+ zfcp_erp_adapter_access_changed(adapter, 135, req);
+ if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_INCOMING_ELS)
+ schedule_work(&adapter->scan_work);
+ break;
+ case FSF_STATUS_READ_CFDC_UPDATED:
+ zfcp_erp_adapter_access_changed(adapter, 136, req);
+ break;
+ case FSF_STATUS_READ_FEATURE_UPDATE_ALERT:
+ adapter->adapter_features = sr_buf->payload.word[0];
break;
}
- skip_fsfstatus:
- /*
- * always call specific handlers to give them a chance to do
- * something meaningful even in error cases
- */
- zfcp_fsf_req_dispatch(fsf_req);
+ mempool_free(sr_buf, adapter->pool.data_status_read);
+ zfcp_fsf_req_free(req);
- return retval;
+ atomic_inc(&adapter->stat_miss);
+ queue_work(zfcp_data.work_queue, &adapter->stat_work);
}
-/*
- * function: zfcp_fsf_fsfstatus_qual_eval
- *
- * purpose: evaluates FSF status-qualifier of completed FSF request
- * and acts accordingly
- *
- * returns:
- */
-static int
-zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *req)
{
- int retval = 0;
-
- switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+ switch (req->qtcb->header.fsf_status_qual.word[0]) {
case FSF_SQ_FCP_RSP_AVAILABLE:
- break;
- case FSF_SQ_RETRY_IF_POSSIBLE:
- /* The SCSI-stack may now issue retries or escalate */
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
- case FSF_SQ_COMMAND_ABORTED:
- /* Carry the aborted state on to upper layer */
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTED;
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
- case FSF_SQ_NO_RECOM:
- ZFCP_LOG_NORMAL("bug: No recommendation could be given for a "
- "problem on the adapter %s "
- "Stopping all operations on this adapter. ",
- zfcp_get_busid_by_adapter(fsf_req->adapter));
- zfcp_erp_adapter_shutdown(fsf_req->adapter, 0, 121, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
- case FSF_SQ_ULP_PROGRAMMING_ERROR:
- ZFCP_LOG_NORMAL("error: not enough SBALs for data transfer "
- "(adapter %s)\n",
- zfcp_get_busid_by_adapter(fsf_req->adapter));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
case FSF_SQ_NO_RETRY_POSSIBLE:
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- /* dealt with in the respective functions */
+ return;
+ case FSF_SQ_COMMAND_ABORTED:
+ req->status |= ZFCP_STATUS_FSFREQ_ABORTED;
break;
- default:
- ZFCP_LOG_NORMAL("bug: Additional status info could "
- "not be interpreted properly.\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
- (char *) &fsf_req->qtcb->header.fsf_status_qual,
- sizeof (union fsf_status_qual));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ case FSF_SQ_NO_RECOM:
+ dev_err(&req->adapter->ccw_device->dev,
+ "The FCP adapter reported a problem "
+ "that cannot be recovered\n");
+ zfcp_erp_adapter_shutdown(req->adapter, 0, 121, req);
break;
}
-
- return retval;
+ /* all non-return stats set FSFREQ_ERROR*/
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
-/**
- * zfcp_fsf_link_down_info_eval - evaluate link down information block
- */
-static void
-zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *fsf_req, u8 id,
- struct fsf_link_down_info *link_down)
+static void zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *req)
{
- struct zfcp_adapter *adapter = fsf_req->adapter;
-
- if (atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
- &adapter->status))
+ if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR))
return;
- atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
-
- if (link_down == NULL)
- goto out;
-
- switch (link_down->error_code) {
- case FSF_PSQ_LINK_NO_LIGHT:
- ZFCP_LOG_NORMAL("The local link to adapter %s is down "
- "(no light detected)\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
- case FSF_PSQ_LINK_WRAP_PLUG:
- ZFCP_LOG_NORMAL("The local link to adapter %s is down "
- "(wrap plug detected)\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
- case FSF_PSQ_LINK_NO_FCP:
- ZFCP_LOG_NORMAL("The local link to adapter %s is down "
- "(adjacent node on link does not support FCP)\n",
- zfcp_get_busid_by_adapter(adapter));
+ switch (req->qtcb->header.fsf_status) {
+ case FSF_UNKNOWN_COMMAND:
+ 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, 120, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
- case FSF_PSQ_LINK_FIRMWARE_UPDATE:
- ZFCP_LOG_NORMAL("The local link to adapter %s is down "
- "(firmware update in progress)\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
- case FSF_PSQ_LINK_INVALID_WWPN:
- ZFCP_LOG_NORMAL("The local link to adapter %s is down "
- "(duplicate or invalid WWPN detected)\n",
- zfcp_get_busid_by_adapter(adapter));
+ case FSF_ADAPTER_STATUS_AVAILABLE:
+ zfcp_fsf_fsfstatus_qual_eval(req);
break;
- case FSF_PSQ_LINK_NO_NPIV_SUPPORT:
- ZFCP_LOG_NORMAL("The local link to adapter %s is down "
- "(no support for NPIV by Fabric)\n",
- zfcp_get_busid_by_adapter(adapter));
+ }
+}
+
+static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
+{
+ struct zfcp_adapter *adapter = req->adapter;
+ struct fsf_qtcb *qtcb = req->qtcb;
+ union fsf_prot_status_qual *psq = &qtcb->prefix.prot_status_qual;
+
+ zfcp_hba_dbf_event_fsf_response(req);
+
+ if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+ ZFCP_STATUS_FSFREQ_RETRY; /* only for SCSI cmnds. */
+ return;
+ }
+
+ switch (qtcb->prefix.prot_status) {
+ case FSF_PROT_GOOD:
+ case FSF_PROT_FSF_STATUS_PRESENTED:
+ return;
+ case FSF_PROT_QTCB_VERSION_ERROR:
+ dev_err(&adapter->ccw_device->dev,
+ "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, 117, req);
break;
- case FSF_PSQ_LINK_NO_FCP_RESOURCES:
- ZFCP_LOG_NORMAL("The local link to adapter %s is down "
- "(out of resource in FCP daughtercard)\n",
- zfcp_get_busid_by_adapter(adapter));
+ case FSF_PROT_ERROR_STATE:
+ case FSF_PROT_SEQ_NUMB_ERROR:
+ zfcp_erp_adapter_reopen(adapter, 0, 98, req);
+ req->status |= ZFCP_STATUS_FSFREQ_RETRY;
break;
- case FSF_PSQ_LINK_NO_FABRIC_RESOURCES:
- ZFCP_LOG_NORMAL("The local link to adapter %s is down "
- "(out of resource in Fabric)\n",
- zfcp_get_busid_by_adapter(adapter));
+ 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, 118, req);
break;
- case FSF_PSQ_LINK_FABRIC_LOGIN_UNABLE:
- ZFCP_LOG_NORMAL("The local link to adapter %s is down "
- "(unable to Fabric login)\n",
- zfcp_get_busid_by_adapter(adapter));
+ case FSF_PROT_HOST_CONNECTION_INITIALIZING:
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+ &adapter->status);
break;
- case FSF_PSQ_LINK_WWPN_ASSIGNMENT_CORRUPTED:
- ZFCP_LOG_NORMAL("WWPN assignment file corrupted on adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
+ case FSF_PROT_DUPLICATE_REQUEST_ID:
+ 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, 78, req);
break;
- case FSF_PSQ_LINK_MODE_TABLE_CURRUPTED:
- ZFCP_LOG_NORMAL("Mode table corrupted on adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
+ case FSF_PROT_LINK_DOWN:
+ zfcp_fsf_link_down_info_eval(req, 37, &psq->link_down_info);
+ /* FIXME: reopening adapter now? better wait for link up */
+ zfcp_erp_adapter_reopen(adapter, 0, 79, req);
break;
- case FSF_PSQ_LINK_NO_WWPN_ASSIGNMENT:
- ZFCP_LOG_NORMAL("No WWPN for assignment table on adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
+ case FSF_PROT_REEST_QUEUE:
+ /* All ports should be marked as ready to run again */
+ zfcp_erp_modify_adapter_status(adapter, 28, NULL,
+ ZFCP_STATUS_COMMON_RUNNING,
+ ZFCP_SET);
+ zfcp_erp_adapter_reopen(adapter,
+ ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
+ ZFCP_STATUS_COMMON_ERP_FAILED, 99, req);
break;
default:
- ZFCP_LOG_NORMAL("The local link to adapter %s is down "
- "(warning: unknown reason code %d)\n",
- zfcp_get_busid_by_adapter(adapter),
- link_down->error_code);
+ dev_err(&adapter->ccw_device->dev,
+ "0x%x is not a valid transfer protocol status\n",
+ qtcb->prefix.prot_status);
+ zfcp_erp_adapter_shutdown(adapter, 0, 119, req);
}
-
- if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
- ZFCP_LOG_DEBUG("Debug information to link down: "
- "primary_status=0x%02x "
- "ioerr_code=0x%02x "
- "action_code=0x%02x "
- "reason_code=0x%02x "
- "explanation_code=0x%02x "
- "vendor_specific_code=0x%02x\n",
- link_down->primary_status,
- link_down->ioerr_code,
- link_down->action_code,
- link_down->reason_code,
- link_down->explanation_code,
- link_down->vendor_specific_code);
-
- out:
- zfcp_erp_adapter_failed(adapter, id, fsf_req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
-/*
- * function: zfcp_fsf_req_dispatch
- *
- * purpose: calls the appropriate command specific handler
+/**
+ * zfcp_fsf_req_complete - process completion of a FSF request
+ * @fsf_req: The FSF request that has been completed.
*
- * returns:
+ * When a request has been completed either from the FCP adapter,
+ * or it has been dismissed due to a queue shutdown, this function
+ * is called to process the completion status and trigger further
+ * events related to the FSF request.
*/
-static int
-zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req)
+void zfcp_fsf_req_complete(struct zfcp_fsf_req *req)
{
- struct zfcp_erp_action *erp_action = fsf_req->erp_action;
- struct zfcp_adapter *adapter = fsf_req->adapter;
- int retval = 0;
-
+ if (unlikely(req->fsf_command == FSF_QTCB_UNSOLICITED_STATUS)) {
+ zfcp_fsf_status_read_handler(req);
+ return;
+ }
- switch (fsf_req->fsf_command) {
+ del_timer(&req->timer);
+ zfcp_fsf_protstatus_eval(req);
+ zfcp_fsf_fsfstatus_eval(req);
+ req->handler(req);
- case FSF_QTCB_FCP_CMND:
- zfcp_fsf_send_fcp_command_handler(fsf_req);
- break;
+ if (req->erp_action)
+ zfcp_erp_notify(req->erp_action, 0);
+ req->status |= ZFCP_STATUS_FSFREQ_COMPLETED;
- case FSF_QTCB_ABORT_FCP_CMND:
- zfcp_fsf_abort_fcp_command_handler(fsf_req);
- break;
+ if (likely(req->status & ZFCP_STATUS_FSFREQ_CLEANUP))
+ zfcp_fsf_req_free(req);
+ else
+ /* notify initiator waiting for the requests completion */
+ /*
+ * FIXME: Race! We must not access fsf_req here as it might have been
+ * cleaned up already due to the set ZFCP_STATUS_FSFREQ_COMPLETED
+ * flag. It's an improbable case. But, we have the same paranoia for
+ * the cleanup flag already.
+ * Might better be handled using complete()?
+ * (setting the flag and doing wakeup ought to be atomic
+ * with regard to checking the flag as long as waitqueue is
+ * part of the to be released structure)
+ */
+ wake_up(&req->completion_wq);
+}
- case FSF_QTCB_SEND_GENERIC:
- zfcp_fsf_send_ct_handler(fsf_req);
- break;
+static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
+{
+ struct fsf_qtcb_bottom_config *bottom;
+ struct zfcp_adapter *adapter = req->adapter;
+ struct Scsi_Host *shost = adapter->scsi_host;
- case FSF_QTCB_OPEN_PORT_WITH_DID:
- zfcp_fsf_open_port_handler(fsf_req);
- break;
+ bottom = &req->qtcb->bottom.config;
- case FSF_QTCB_OPEN_LUN:
- zfcp_fsf_open_unit_handler(fsf_req);
- break;
+ if (req->data)
+ memcpy(req->data, bottom, sizeof(*bottom));
- case FSF_QTCB_CLOSE_LUN:
- zfcp_fsf_close_unit_handler(fsf_req);
- break;
+ fc_host_node_name(shost) = bottom->nport_serv_param.wwnn;
+ fc_host_port_name(shost) = bottom->nport_serv_param.wwpn;
+ fc_host_port_id(shost) = bottom->s_id & ZFCP_DID_MASK;
+ fc_host_speed(shost) = bottom->fc_link_speed;
+ fc_host_supported_classes(shost) = FC_COS_CLASS2 | FC_COS_CLASS3;
- case FSF_QTCB_CLOSE_PORT:
- zfcp_fsf_close_port_handler(fsf_req);
- break;
+ adapter->hydra_version = bottom->adapter_type;
+ adapter->timer_ticks = bottom->timer_interval;
- case FSF_QTCB_CLOSE_PHYSICAL_PORT:
- zfcp_fsf_close_physical_port_handler(fsf_req);
- break;
+ if (fc_host_permanent_port_name(shost) == -1)
+ fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
- case FSF_QTCB_EXCHANGE_CONFIG_DATA:
- zfcp_fsf_exchange_config_data_handler(fsf_req);
+ switch (bottom->fc_topology) {
+ case FSF_TOPO_P2P:
+ adapter->peer_d_id = bottom->peer_d_id & ZFCP_DID_MASK;
+ adapter->peer_wwpn = bottom->plogi_payload.wwpn;
+ adapter->peer_wwnn = bottom->plogi_payload.wwnn;
+ fc_host_port_type(shost) = FC_PORTTYPE_PTP;
break;
-
- case FSF_QTCB_EXCHANGE_PORT_DATA:
- zfcp_fsf_exchange_port_data_handler(fsf_req);
+ case FSF_TOPO_FABRIC:
+ fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
break;
+ case FSF_TOPO_AL:
+ fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
+ default:
+ dev_err(&adapter->ccw_device->dev,
+ "Unknown or unsupported arbitrated loop "
+ "fibre channel topology detected\n");
+ zfcp_erp_adapter_shutdown(adapter, 0, 127, req);
+ return -EIO;
+ }
- case FSF_QTCB_SEND_ELS:
- zfcp_fsf_send_els_handler(fsf_req);
- break;
+ return 0;
+}
- case FSF_QTCB_DOWNLOAD_CONTROL_FILE:
- zfcp_fsf_control_file_handler(fsf_req);
- break;
+static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req)
+{
+ struct zfcp_adapter *adapter = req->adapter;
+ struct fsf_qtcb *qtcb = req->qtcb;
+ struct fsf_qtcb_bottom_config *bottom = &qtcb->bottom.config;
+ struct Scsi_Host *shost = adapter->scsi_host;
- case FSF_QTCB_UPLOAD_CONTROL_FILE:
- zfcp_fsf_control_file_handler(fsf_req);
- break;
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return;
- default:
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- ZFCP_LOG_NORMAL("bug: Command issued by the device driver is "
- "not supported by the adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- if (fsf_req->fsf_command != fsf_req->qtcb->header.fsf_command)
- ZFCP_LOG_NORMAL
- ("bug: Command issued by the device driver differs "
- "from the command returned by the adapter %s "
- "(debug info 0x%x, 0x%x).\n",
- zfcp_get_busid_by_adapter(adapter),
- fsf_req->fsf_command,
- fsf_req->qtcb->header.fsf_command);
- }
+ adapter->fsf_lic_version = bottom->lic_version;
+ adapter->adapter_features = bottom->adapter_features;
+ adapter->connection_features = bottom->connection_features;
+ adapter->peer_wwpn = 0;
+ adapter->peer_wwnn = 0;
+ adapter->peer_d_id = 0;
- if (!erp_action)
- return retval;
+ switch (qtcb->header.fsf_status) {
+ case FSF_GOOD:
+ if (zfcp_fsf_exchange_config_evaluate(req))
+ return;
- zfcp_erp_async_handler(erp_action, 0);
+ if (bottom->max_qtcb_size < sizeof(struct fsf_qtcb)) {
+ dev_err(&adapter->ccw_device->dev,
+ "FCP adapter maximum QTCB size (%d bytes) "
+ "is too small\n",
+ bottom->max_qtcb_size);
+ zfcp_erp_adapter_shutdown(adapter, 0, 129, req);
+ return;
+ }
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
+ &adapter->status);
+ break;
+ case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
+ fc_host_node_name(shost) = 0;
+ fc_host_port_name(shost) = 0;
+ fc_host_port_id(shost) = 0;
+ fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
+ fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
+ adapter->hydra_version = 0;
- return retval;
-}
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
+ &adapter->status);
-/*
- * function: zfcp_fsf_status_read
- *
- * purpose: initiates a Status Read command at the specified adapter
- *
- * returns:
- */
-int
-zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags)
-{
- struct zfcp_fsf_req *fsf_req;
- struct fsf_status_read_buffer *status_buffer;
- unsigned long lock_flags;
- volatile struct qdio_buffer_element *sbale;
- int retval = 0;
-
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(adapter, FSF_QTCB_UNSOLICITED_STATUS,
- req_flags | ZFCP_REQ_NO_QTCB,
- adapter->pool.fsf_req_status_read,
- &lock_flags, &fsf_req);
- if (retval < 0) {
- ZFCP_LOG_INFO("error: Could not create unsolicited status "
- "buffer for adapter %s.\n",
- zfcp_get_busid_by_adapter(adapter));
- goto failed_req_create;
+ zfcp_fsf_link_down_info_eval(req, 42,
+ &qtcb->header.fsf_status_qual.link_down_info);
+ break;
+ default:
+ zfcp_erp_adapter_shutdown(adapter, 0, 130, req);
+ return;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
- sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS;
- sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY;
- fsf_req->sbale_curr = 2;
-
- status_buffer =
- mempool_alloc(adapter->pool.data_status_read, GFP_ATOMIC);
- if (!status_buffer) {
- ZFCP_LOG_NORMAL("bug: could not get some buffer\n");
- goto failed_buf;
+ if (adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT) {
+ adapter->hardware_version = bottom->hardware_version;
+ memcpy(fc_host_serial_number(shost), bottom->serial_number,
+ min(FC_SERIAL_NUMBER_SIZE, 17));
+ EBCASC(fc_host_serial_number(shost),
+ min(FC_SERIAL_NUMBER_SIZE, 17));
}
- memset(status_buffer, 0, sizeof (struct fsf_status_read_buffer));
- fsf_req->data = (unsigned long) status_buffer;
- /* insert pointer to respective buffer */
- sbale = zfcp_qdio_sbale_curr(fsf_req);
- sbale->addr = (void *) status_buffer;
- sbale->length = sizeof(struct fsf_status_read_buffer);
-
- retval = zfcp_fsf_req_send(fsf_req);
- if (retval) {
- ZFCP_LOG_DEBUG("error: Could not set-up unsolicited status "
- "environment.\n");
- goto failed_req_send;
+ if (FSF_QTCB_CURRENT_VERSION < bottom->low_qtcb_version) {
+ dev_err(&adapter->ccw_device->dev,
+ "The FCP adapter only supports newer "
+ "control block versions\n");
+ zfcp_erp_adapter_shutdown(adapter, 0, 125, req);
+ 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, 126, req);
}
+}
- ZFCP_LOG_TRACE("Status Read request initiated (adapter%s)\n",
- zfcp_get_busid_by_adapter(adapter));
- goto out;
+static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req)
+{
+ struct zfcp_adapter *adapter = req->adapter;
+ struct fsf_qtcb_bottom_port *bottom = &req->qtcb->bottom.port;
+ struct Scsi_Host *shost = adapter->scsi_host;
- failed_req_send:
- mempool_free(status_buffer, adapter->pool.data_status_read);
+ if (req->data)
+ memcpy(req->data, bottom, sizeof(*bottom));
- failed_buf:
- zfcp_fsf_req_free(fsf_req);
- failed_req_create:
- zfcp_hba_dbf_event_fsf_unsol("fail", adapter, NULL);
- out:
- write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
- return retval;
+ if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
+ fc_host_permanent_port_name(shost) = bottom->wwpn;
+ else
+ fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
+ fc_host_maxframe_size(shost) = bottom->maximum_frame_size;
+ fc_host_supported_speeds(shost) = bottom->supported_speed;
}
-static int
-zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req)
{
- struct fsf_status_read_buffer *status_buffer;
- struct zfcp_adapter *adapter;
- struct zfcp_port *port;
- unsigned long flags;
+ struct fsf_qtcb *qtcb = req->qtcb;
- status_buffer = (struct fsf_status_read_buffer *) fsf_req->data;
- adapter = fsf_req->adapter;
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- list_for_each_entry(port, &adapter->port_list_head, list)
- if (port->d_id == (status_buffer->d_id & ZFCP_DID_MASK))
+ switch (qtcb->header.fsf_status) {
+ case FSF_GOOD:
+ zfcp_fsf_exchange_port_evaluate(req);
+ break;
+ case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
+ zfcp_fsf_exchange_port_evaluate(req);
+ zfcp_fsf_link_down_info_eval(req, 43,
+ &qtcb->header.fsf_status_qual.link_down_info);
break;
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
- if (!port || (port->d_id != (status_buffer->d_id & ZFCP_DID_MASK))) {
- ZFCP_LOG_NORMAL("bug: Reopen port indication received for "
- "nonexisting port with d_id 0x%06x on "
- "adapter %s. Ignored.\n",
- status_buffer->d_id & ZFCP_DID_MASK,
- zfcp_get_busid_by_adapter(adapter));
- goto out;
}
+}
- switch (status_buffer->status_subtype) {
+static int zfcp_fsf_sbal_check(struct zfcp_adapter *adapter)
+{
+ struct zfcp_qdio_queue *req_q = &adapter->req_q;
- case FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT:
- zfcp_erp_port_reopen(port, 0, 101, fsf_req);
- break;
+ spin_lock_bh(&adapter->req_q_lock);
+ if (atomic_read(&req_q->count))
+ return 1;
+ spin_unlock_bh(&adapter->req_q_lock);
+ return 0;
+}
- case FSF_STATUS_READ_SUB_ERROR_PORT:
- zfcp_erp_port_shutdown(port, 0, 122, fsf_req);
- break;
+static int zfcp_fsf_sbal_available(struct zfcp_adapter *adapter)
+{
+ unsigned int count = atomic_read(&adapter->req_q.count);
+ if (!count)
+ atomic_inc(&adapter->qdio_outb_full);
+ return count > 0;
+}
- default:
- ZFCP_LOG_NORMAL("bug: Undefined status subtype received "
- "for a reopen indication on port with "
- "d_id 0x%06x on the adapter %s. "
- "Ignored. (debug info 0x%x)\n",
- status_buffer->d_id,
- zfcp_get_busid_by_adapter(adapter),
- status_buffer->status_subtype);
- }
- out:
- return 0;
+static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter)
+{
+ long ret;
+
+ spin_unlock_bh(&adapter->req_q_lock);
+ ret = wait_event_interruptible_timeout(adapter->request_wq,
+ zfcp_fsf_sbal_check(adapter), 5 * HZ);
+ if (ret > 0)
+ return 0;
+ if (!ret)
+ atomic_inc(&adapter->qdio_outb_full);
+
+ spin_lock_bh(&adapter->req_q_lock);
+ return -EIO;
}
-/*
- * function: zfcp_fsf_status_read_handler
- *
- * purpose: is called for finished Open Port command
- *
- * returns:
- */
-static int
-zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req)
+static struct zfcp_fsf_req *zfcp_fsf_alloc_noqtcb(mempool_t *pool)
{
- int retval = 0;
- struct zfcp_adapter *adapter = fsf_req->adapter;
- struct fsf_status_read_buffer *status_buffer =
- (struct fsf_status_read_buffer *) fsf_req->data;
- struct fsf_bit_error_payload *fsf_bit_error;
-
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
- zfcp_hba_dbf_event_fsf_unsol("dism", adapter, status_buffer);
- mempool_free(status_buffer, adapter->pool.data_status_read);
- zfcp_fsf_req_free(fsf_req);
- goto out;
- }
+ struct zfcp_fsf_req *req;
+ req = mempool_alloc(pool, GFP_ATOMIC);
+ if (!req)
+ return NULL;
+ memset(req, 0, sizeof(*req));
+ return req;
+}
- zfcp_hba_dbf_event_fsf_unsol("read", adapter, status_buffer);
+static struct zfcp_fsf_req *zfcp_fsf_alloc_qtcb(mempool_t *pool)
+{
+ struct zfcp_fsf_req_qtcb *qtcb;
- switch (status_buffer->status_type) {
+ if (likely(pool))
+ qtcb = mempool_alloc(pool, GFP_ATOMIC);
+ else
+ qtcb = kmem_cache_alloc(zfcp_data.fsf_req_qtcb_cache,
+ GFP_ATOMIC);
+ if (unlikely(!qtcb))
+ return NULL;
- case FSF_STATUS_READ_PORT_CLOSED:
- zfcp_fsf_status_read_port_closed(fsf_req);
- break;
+ memset(qtcb, 0, sizeof(*qtcb));
+ qtcb->fsf_req.qtcb = &qtcb->qtcb;
+ qtcb->fsf_req.pool = pool;
- case FSF_STATUS_READ_INCOMING_ELS:
- zfcp_fsf_incoming_els(fsf_req);
- break;
+ return &qtcb->fsf_req;
+}
- case FSF_STATUS_READ_SENSE_DATA_AVAIL:
- ZFCP_LOG_INFO("unsolicited sense data received (adapter %s)\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
+static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_adapter *adapter,
+ u32 fsf_cmd, int req_flags,
+ mempool_t *pool)
+{
+ struct qdio_buffer_element *sbale;
- case FSF_STATUS_READ_BIT_ERROR_THRESHOLD:
- fsf_bit_error = (struct fsf_bit_error_payload *)
- status_buffer->payload;
- ZFCP_LOG_NORMAL("Warning: bit error threshold data "
- "received (adapter %s, "
- "link failures = %i, loss of sync errors = %i, "
- "loss of signal errors = %i, "
- "primitive sequence errors = %i, "
- "invalid transmission word errors = %i, "
- "CRC errors = %i)\n",
- zfcp_get_busid_by_adapter(adapter),
- fsf_bit_error->link_failure_error_count,
- fsf_bit_error->loss_of_sync_error_count,
- fsf_bit_error->loss_of_signal_error_count,
- fsf_bit_error->primitive_sequence_error_count,
- fsf_bit_error->invalid_transmission_word_error_count,
- fsf_bit_error->crc_error_count);
- ZFCP_LOG_INFO("Additional bit error threshold data "
- "(adapter %s, "
- "primitive sequence event time-outs = %i, "
- "elastic buffer overrun errors = %i, "
- "advertised receive buffer-to-buffer credit = %i, "
- "current receice buffer-to-buffer credit = %i, "
- "advertised transmit buffer-to-buffer credit = %i, "
- "current transmit buffer-to-buffer credit = %i)\n",
- zfcp_get_busid_by_adapter(adapter),
- fsf_bit_error->primitive_sequence_event_timeout_count,
- fsf_bit_error->elastic_buffer_overrun_error_count,
- fsf_bit_error->advertised_receive_b2b_credit,
- fsf_bit_error->current_receive_b2b_credit,
- fsf_bit_error->advertised_transmit_b2b_credit,
- fsf_bit_error->current_transmit_b2b_credit);
- break;
+ struct zfcp_fsf_req *req;
+ struct zfcp_qdio_queue *req_q = &adapter->req_q;
- case FSF_STATUS_READ_LINK_DOWN:
- switch (status_buffer->status_subtype) {
- case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK:
- ZFCP_LOG_INFO("Physical link to adapter %s is down\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_fsf_link_down_info_eval(fsf_req, 38,
- (struct fsf_link_down_info *)
- &status_buffer->payload);
- break;
- case FSF_STATUS_READ_SUB_FDISC_FAILED:
- ZFCP_LOG_INFO("Local link to adapter %s is down "
- "due to failed FDISC login\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_fsf_link_down_info_eval(fsf_req, 39,
- (struct fsf_link_down_info *)
- &status_buffer->payload);
- break;
- case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE:
- ZFCP_LOG_INFO("Local link to adapter %s is down "
- "due to firmware update on adapter\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_fsf_link_down_info_eval(fsf_req, 40, NULL);
- break;
- default:
- ZFCP_LOG_INFO("Local link to adapter %s is down "
- "due to unknown reason\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_fsf_link_down_info_eval(fsf_req, 41, NULL);
- };
- break;
+ if (req_flags & ZFCP_REQ_NO_QTCB)
+ req = zfcp_fsf_alloc_noqtcb(pool);
+ else
+ req = zfcp_fsf_alloc_qtcb(pool);
- case FSF_STATUS_READ_LINK_UP:
- ZFCP_LOG_NORMAL("Local link to adapter %s was replugged. "
- "Restarting operations on this adapter\n",
- zfcp_get_busid_by_adapter(adapter));
- /* All ports should be marked as ready to run again */
- zfcp_erp_modify_adapter_status(adapter, 30, NULL,
- ZFCP_STATUS_COMMON_RUNNING,
- ZFCP_SET);
- zfcp_erp_adapter_reopen(adapter,
- ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED
- | ZFCP_STATUS_COMMON_ERP_FAILED,
- 102, fsf_req);
- break;
+ if (unlikely(!req))
+ return ERR_PTR(-EIO);
- case FSF_STATUS_READ_NOTIFICATION_LOST:
- ZFCP_LOG_NORMAL("Unsolicited status notification(s) lost: "
- "adapter %s%s%s%s%s%s%s%s%s\n",
- zfcp_get_busid_by_adapter(adapter),
- (status_buffer->status_subtype &
- FSF_STATUS_READ_SUB_INCOMING_ELS) ?
- ", incoming ELS" : "",
- (status_buffer->status_subtype &
- FSF_STATUS_READ_SUB_SENSE_DATA) ?
- ", sense data" : "",
- (status_buffer->status_subtype &
- FSF_STATUS_READ_SUB_LINK_STATUS) ?
- ", link status change" : "",
- (status_buffer->status_subtype &
- FSF_STATUS_READ_SUB_PORT_CLOSED) ?
- ", port close" : "",
- (status_buffer->status_subtype &
- FSF_STATUS_READ_SUB_BIT_ERROR_THRESHOLD) ?
- ", bit error exception" : "",
- (status_buffer->status_subtype &
- FSF_STATUS_READ_SUB_ACT_UPDATED) ?
- ", ACT update" : "",
- (status_buffer->status_subtype &
- FSF_STATUS_READ_SUB_ACT_HARDENED) ?
- ", ACT hardening" : "",
- (status_buffer->status_subtype &
- FSF_STATUS_READ_SUB_FEATURE_UPDATE_ALERT) ?
- ", adapter feature change" : "");
-
- if (status_buffer->status_subtype &
- FSF_STATUS_READ_SUB_ACT_UPDATED)
- zfcp_erp_adapter_access_changed(adapter, 135, fsf_req);
- break;
+ if (adapter->req_no == 0)
+ adapter->req_no++;
- case FSF_STATUS_READ_CFDC_UPDATED:
- ZFCP_LOG_NORMAL("CFDC has been updated on the adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_erp_adapter_access_changed(adapter, 136, fsf_req);
- break;
+ INIT_LIST_HEAD(&req->list);
+ init_timer(&req->timer);
+ init_waitqueue_head(&req->completion_wq);
- case FSF_STATUS_READ_CFDC_HARDENED:
- switch (status_buffer->status_subtype) {
- case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE:
- ZFCP_LOG_NORMAL("CFDC of adapter %s saved on SE\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
- case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2:
- ZFCP_LOG_NORMAL("CFDC of adapter %s has been copied "
- "to the secondary SE\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
- default:
- ZFCP_LOG_NORMAL("CFDC of adapter %s has been hardened\n",
- zfcp_get_busid_by_adapter(adapter));
- }
- break;
+ req->adapter = adapter;
+ req->fsf_command = fsf_cmd;
+ req->req_id = adapter->req_no++;
+ req->sbal_number = 1;
+ req->sbal_first = req_q->first;
+ req->sbal_last = req_q->first;
+ req->sbale_curr = 1;
- case FSF_STATUS_READ_FEATURE_UPDATE_ALERT:
- ZFCP_LOG_INFO("List of supported features on adapter %s has "
- "been changed from 0x%08X to 0x%08X\n",
- zfcp_get_busid_by_adapter(adapter),
- *(u32*) (status_buffer->payload + 4),
- *(u32*) (status_buffer->payload));
- adapter->adapter_features = *(u32*) status_buffer->payload;
- break;
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].addr = (void *) req->req_id;
+ sbale[0].flags |= SBAL_FLAGS0_COMMAND;
- default:
- ZFCP_LOG_NORMAL("warning: An unsolicited status packet of unknown "
- "type was received (debug info 0x%x)\n",
- status_buffer->status_type);
- ZFCP_LOG_DEBUG("Dump of status_read_buffer %p:\n",
- status_buffer);
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) status_buffer,
- sizeof (struct fsf_status_read_buffer));
- break;
- }
- mempool_free(status_buffer, adapter->pool.data_status_read);
- zfcp_fsf_req_free(fsf_req);
- /*
- * recycle buffer and start new request repeat until outbound
- * queue is empty or adapter shutdown is requested
- */
- /*
- * FIXME(qdio):
- * we may wait in the req_create for 5s during shutdown, so
- * qdio_cleanup will have to wait at least that long before returning
- * with failure to allow us a proper cleanup under all circumstances
- */
- /*
- * FIXME:
- * allocation failure possible? (Is this code needed?)
- */
- retval = zfcp_fsf_status_read(adapter, 0);
- if (retval < 0) {
- ZFCP_LOG_INFO("Failed to create unsolicited status read "
- "request for the adapter %s.\n",
- zfcp_get_busid_by_adapter(adapter));
- /* temporary fix to avoid status read buffer shortage */
- adapter->status_read_failed++;
- if ((ZFCP_STATUS_READS_RECOM - adapter->status_read_failed)
- < ZFCP_STATUS_READ_FAILED_THRESHOLD) {
- ZFCP_LOG_INFO("restart adapter %s due to status read "
- "buffer shortage\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_erp_adapter_reopen(adapter, 0, 103, fsf_req);
- }
+ if (likely(req->qtcb)) {
+ req->qtcb->prefix.req_seq_no = req->adapter->fsf_req_seq_no;
+ req->qtcb->prefix.req_id = req->req_id;
+ req->qtcb->prefix.ulp_info = 26;
+ req->qtcb->prefix.qtcb_type = fsf_qtcb_type[req->fsf_command];
+ req->qtcb->prefix.qtcb_version = FSF_QTCB_CURRENT_VERSION;
+ req->qtcb->header.req_handle = req->req_id;
+ req->qtcb->header.fsf_command = req->fsf_command;
+ req->seq_no = adapter->fsf_req_seq_no;
+ req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no;
+ sbale[1].addr = (void *) req->qtcb;
+ sbale[1].length = sizeof(struct fsf_qtcb);
}
- out:
- return retval;
-}
-/*
- * function: zfcp_fsf_abort_fcp_command
- *
- * purpose: tells FSF to abort a running SCSI command
- *
- * returns: address of initiated FSF request
- * NULL - request could not be initiated
- *
- * FIXME(design): should be watched by a timeout !!!
- * FIXME(design) shouldn't this be modified to return an int
- * also...don't know how though
- */
-struct zfcp_fsf_req *
-zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
- struct zfcp_adapter *adapter,
- struct zfcp_unit *unit, int req_flags)
-{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req = NULL;
- unsigned long lock_flags;
- int retval = 0;
-
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(adapter, FSF_QTCB_ABORT_FCP_CMND,
- req_flags, adapter->pool.fsf_req_abort,
- &lock_flags, &fsf_req);
- if (retval < 0) {
- ZFCP_LOG_INFO("error: Failed to create an abort command "
- "request for lun 0x%016Lx on port 0x%016Lx "
- "on adapter %s.\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_adapter(adapter));
- goto out;
+ if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) {
+ zfcp_fsf_req_free(req);
+ return ERR_PTR(-EIO);
}
- if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &unit->status)))
- goto unit_blocked;
-
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
- sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
- sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+ if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP))
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- fsf_req->data = (unsigned long) unit;
+ return req;
+}
- /* set handles of unit and its parent port in QTCB */
- fsf_req->qtcb->header.lun_handle = unit->handle;
- fsf_req->qtcb->header.port_handle = unit->port->handle;
+static int zfcp_fsf_req_send(struct zfcp_fsf_req *req)
+{
+ struct zfcp_adapter *adapter = req->adapter;
+ struct zfcp_qdio_queue *req_q = &adapter->req_q;
+ int idx;
- /* set handle of request which should be aborted */
- fsf_req->qtcb->bottom.support.req_handle = (u64) old_req_id;
+ /* put allocated FSF request into hash table */
+ spin_lock(&adapter->req_list_lock);
+ idx = zfcp_reqlist_hash(req->req_id);
+ list_add_tail(&req->list, &adapter->req_list[idx]);
+ spin_unlock(&adapter->req_list_lock);
- zfcp_fsf_start_timer(fsf_req, ZFCP_SCSI_ER_TIMEOUT);
- retval = zfcp_fsf_req_send(fsf_req);
- if (!retval)
- goto out;
+ req->issued = get_clock();
+ if (zfcp_qdio_send(req)) {
+ /* Queues are down..... */
+ del_timer(&req->timer);
+ spin_lock(&adapter->req_list_lock);
+ zfcp_reqlist_remove(adapter, req);
+ spin_unlock(&adapter->req_list_lock);
+ /* undo changes in request queue made for this request */
+ atomic_add(req->sbal_number, &req_q->count);
+ req_q->first -= req->sbal_number;
+ req_q->first += QDIO_MAX_BUFFERS_PER_Q;
+ req_q->first %= QDIO_MAX_BUFFERS_PER_Q; /* wrap */
+ zfcp_erp_adapter_reopen(adapter, 0, 116, req);
+ return -EIO;
+ }
- unit_blocked:
- zfcp_fsf_req_free(fsf_req);
- fsf_req = NULL;
+ /* Don't increase for unsolicited status */
+ if (req->qtcb)
+ adapter->fsf_req_seq_no++;
- out:
- write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
- return fsf_req;
+ return 0;
}
-/*
- * function: zfcp_fsf_abort_fcp_command_handler
- *
- * purpose: is called for finished Abort FCP Command request
- *
- * returns:
+/**
+ * zfcp_fsf_status_read - send status read request
+ * @adapter: pointer to struct zfcp_adapter
+ * @req_flags: request flags
+ * Returns: 0 on success, ERROR otherwise
*/
-static int
-zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *new_fsf_req)
+int zfcp_fsf_status_read(struct zfcp_adapter *adapter)
{
- int retval = -EINVAL;
- struct zfcp_unit *unit;
- union fsf_status_qual *fsf_stat_qual =
- &new_fsf_req->qtcb->header.fsf_status_qual;
+ struct zfcp_fsf_req *req;
+ struct fsf_status_read_buffer *sr_buf;
+ struct qdio_buffer_element *sbale;
+ int retval = -EIO;
- if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- /* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */
- goto skip_fsfstatus;
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
+
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_UNSOLICITED_STATUS,
+ ZFCP_REQ_NO_QTCB,
+ adapter->pool.fsf_req_status_read);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
+ goto out;
+ }
+
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS;
+ sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY;
+ req->sbale_curr = 2;
+
+ sr_buf = mempool_alloc(adapter->pool.data_status_read, GFP_ATOMIC);
+ if (!sr_buf) {
+ retval = -ENOMEM;
+ goto failed_buf;
}
+ memset(sr_buf, 0, sizeof(*sr_buf));
+ req->data = sr_buf;
+ sbale = zfcp_qdio_sbale_curr(req);
+ sbale->addr = (void *) sr_buf;
+ sbale->length = sizeof(*sr_buf);
+
+ retval = zfcp_fsf_req_send(req);
+ if (retval)
+ goto failed_req_send;
+
+ goto out;
+
+failed_req_send:
+ mempool_free(sr_buf, adapter->pool.data_status_read);
+failed_buf:
+ zfcp_fsf_req_free(req);
+ zfcp_hba_dbf_event_fsf_unsol("fail", adapter, NULL);
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
+ return retval;
+}
- unit = (struct zfcp_unit *) new_fsf_req->data;
+static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
+{
+ struct zfcp_unit *unit = req->data;
+ union fsf_status_qual *fsq = &req->qtcb->header.fsf_status_qual;
- /* evaluate FSF status in QTCB */
- switch (new_fsf_req->qtcb->header.fsf_status) {
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return;
+ switch (req->qtcb->header.fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID:
- if (fsf_stat_qual->word[0] != fsf_stat_qual->word[1]) {
- /*
- * In this case a command that was sent prior to a port
- * reopen was aborted (handles are different). This is
- * fine.
- */
- } else {
- ZFCP_LOG_INFO("Temporary port identifier 0x%x for "
- "port 0x%016Lx on adapter %s invalid. "
- "This may happen occasionally.\n",
- unit->port->handle,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- ZFCP_LOG_INFO("status qualifier:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
- (char *) &new_fsf_req->qtcb->header.
- fsf_status_qual,
- sizeof (union fsf_status_qual));
- /* Let's hope this sorts out the mess */
+ if (fsq->word[0] == fsq->word[1]) {
zfcp_erp_adapter_reopen(unit->port->adapter, 0, 104,
- new_fsf_req);
- new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
break;
-
case FSF_LUN_HANDLE_NOT_VALID:
- if (fsf_stat_qual->word[0] != fsf_stat_qual->word[1]) {
- /*
- * In this case a command that was sent prior to a unit
- * reopen was aborted (handles are different).
- * This is fine.
- */
- } else {
- ZFCP_LOG_INFO
- ("Warning: Temporary LUN identifier 0x%x of LUN "
- "0x%016Lx on port 0x%016Lx on adapter %s is "
- "invalid. This may happen in rare cases. "
- "Trying to re-establish link.\n",
- unit->handle,
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- ZFCP_LOG_DEBUG("Status qualifier data:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &new_fsf_req->qtcb->header.
- fsf_status_qual,
- sizeof (union fsf_status_qual));
- /* Let's hope this sorts out the mess */
- zfcp_erp_port_reopen(unit->port, 0, 105, new_fsf_req);
- new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ if (fsq->word[0] == fsq->word[1]) {
+ zfcp_erp_port_reopen(unit->port, 0, 105, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
}
break;
-
case FSF_FCP_COMMAND_DOES_NOT_EXIST:
- retval = 0;
- new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED;
+ req->status |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED;
break;
-
case FSF_PORT_BOXED:
- ZFCP_LOG_INFO("Remote port 0x%016Lx on adapter %s needs to "
- "be reopened\n", unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- zfcp_erp_port_boxed(unit->port, 47, new_fsf_req);
- new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
- | ZFCP_STATUS_FSFREQ_RETRY;
+ zfcp_erp_port_boxed(unit->port, 47, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+ ZFCP_STATUS_FSFREQ_RETRY;
break;
-
case FSF_LUN_BOXED:
- ZFCP_LOG_INFO(
- "unit 0x%016Lx on port 0x%016Lx on adapter %s needs "
- "to be reopened\n",
- unit->fcp_lun, unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- zfcp_erp_unit_boxed(unit, 48, new_fsf_req);
- new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
- | ZFCP_STATUS_FSFREQ_RETRY;
+ zfcp_erp_unit_boxed(unit, 48, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+ ZFCP_STATUS_FSFREQ_RETRY;
break;
-
case FSF_ADAPTER_STATUS_AVAILABLE:
- switch (new_fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+ switch (fsq->word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
zfcp_test_link(unit->port);
- new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- /* SCSI stack will escalate */
- new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
- default:
- ZFCP_LOG_NORMAL
- ("bug: Wrong status qualifier 0x%x arrived.\n",
- new_fsf_req->qtcb->header.fsf_status_qual.word[0]);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
break;
-
case FSF_GOOD:
- retval = 0;
- new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED;
- break;
-
- default:
- ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
- "(debug info 0x%x)\n",
- new_fsf_req->qtcb->header.fsf_status);
+ req->status |= ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED;
break;
}
- skip_fsfstatus:
- return retval;
}
/**
- * zfcp_use_one_sbal - checks whether req buffer and resp bother each fit into
- * one SBALE
- * Two scatter-gather lists are passed, one for the reqeust and one for the
- * response.
+ * zfcp_fsf_abort_fcp_command - abort running SCSI command
+ * @old_req_id: unsigned long
+ * @adapter: pointer to struct zfcp_adapter
+ * @unit: pointer to struct zfcp_unit
+ * @req_flags: integer specifying the request flags
+ * Returns: pointer to struct zfcp_fsf_req
+ *
+ * FIXME(design): should be watched by a timeout !!!
*/
-static inline int
-zfcp_use_one_sbal(struct scatterlist *req, int req_count,
- struct scatterlist *resp, int resp_count)
-{
- return ((req_count == 1) &&
- (resp_count == 1) &&
- (((unsigned long) zfcp_sg_to_address(&req[0]) &
- PAGE_MASK) ==
- ((unsigned long) (zfcp_sg_to_address(&req[0]) +
- req[0].length - 1) & PAGE_MASK)) &&
- (((unsigned long) zfcp_sg_to_address(&resp[0]) &
- PAGE_MASK) ==
- ((unsigned long) (zfcp_sg_to_address(&resp[0]) +
- resp[0].length - 1) & PAGE_MASK)));
-}
-/**
- * zfcp_fsf_send_ct - initiate a Generic Service request (FC-GS)
- * @ct: pointer to struct zfcp_send_ct which conatins all needed data for
- * the request
- * @pool: pointer to memory pool, if non-null this pool is used to allocate
- * a struct zfcp_fsf_req
- * @erp_action: pointer to erp_action, if non-null the Generic Service request
- * is sent within error recovery
- */
-int
-zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
- struct zfcp_erp_action *erp_action)
+struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
+ struct zfcp_adapter *adapter,
+ struct zfcp_unit *unit,
+ int req_flags)
{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_port *port;
- struct zfcp_adapter *adapter;
- struct zfcp_fsf_req *fsf_req;
- unsigned long lock_flags;
- int bytes;
- int ret = 0;
-
- port = ct->port;
- adapter = port->adapter;
-
- ret = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_GENERIC,
- ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
- pool, &lock_flags, &fsf_req);
- if (ret < 0) {
- ZFCP_LOG_INFO("error: Could not create CT request (FC-GS) for "
- "adapter: %s\n",
- zfcp_get_busid_by_adapter(adapter));
- goto failed_req;
- }
+ struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *req = NULL;
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
- if (zfcp_use_one_sbal(ct->req, ct->req_count,
- ct->resp, ct->resp_count)){
- /* both request buffer and response buffer
- fit into one sbale each */
- sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
- sbale[2].addr = zfcp_sg_to_address(&ct->req[0]);
- sbale[2].length = ct->req[0].length;
- sbale[3].addr = zfcp_sg_to_address(&ct->resp[0]);
- sbale[3].length = ct->resp[0].length;
- sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
- } else if (adapter->adapter_features &
- FSF_FEATURE_ELS_CT_CHAINED_SBALS) {
- /* try to use chained SBALs */
- bytes = zfcp_qdio_sbals_from_sg(fsf_req,
- SBAL_FLAGS0_TYPE_WRITE_READ,
- ct->req, ct->req_count,
- ZFCP_MAX_SBALS_PER_CT_REQ);
- if (bytes <= 0) {
- ZFCP_LOG_INFO("error: creation of CT request failed "
- "on adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- if (bytes == 0)
- ret = -ENOMEM;
- else
- ret = bytes;
-
- goto failed_send;
- }
- fsf_req->qtcb->bottom.support.req_buf_length = bytes;
- fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
- bytes = zfcp_qdio_sbals_from_sg(fsf_req,
- SBAL_FLAGS0_TYPE_WRITE_READ,
- ct->resp, ct->resp_count,
- ZFCP_MAX_SBALS_PER_CT_REQ);
- if (bytes <= 0) {
- ZFCP_LOG_INFO("error: creation of CT request failed "
- "on adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- if (bytes == 0)
- ret = -ENOMEM;
- else
- ret = bytes;
-
- goto failed_send;
- }
- fsf_req->qtcb->bottom.support.resp_buf_length = bytes;
- } else {
- /* reject send generic request */
- ZFCP_LOG_INFO(
- "error: microcode does not support chained SBALs,"
- "CT request too big (adapter %s)\n",
- zfcp_get_busid_by_adapter(adapter));
- ret = -EOPNOTSUPP;
- goto failed_send;
- }
-
- /* settings in QTCB */
- fsf_req->qtcb->header.port_handle = port->handle;
- fsf_req->qtcb->bottom.support.service_class =
- ZFCP_FC_SERVICE_CLASS_DEFAULT;
- fsf_req->qtcb->bottom.support.timeout = ct->timeout;
- fsf_req->data = (unsigned long) ct;
-
- zfcp_san_dbf_event_ct_request(fsf_req);
+ spin_lock(&adapter->req_q_lock);
+ if (!zfcp_fsf_sbal_available(adapter))
+ goto out;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_ABORT_FCP_CMND,
+ req_flags, adapter->pool.fsf_req_abort);
+ if (IS_ERR(req))
+ goto out;
- if (erp_action) {
- erp_action->fsf_req = fsf_req;
- fsf_req->erp_action = erp_action;
- zfcp_erp_start_timer(fsf_req);
- } else
- zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
+ if (unlikely(!(atomic_read(&unit->status) &
+ ZFCP_STATUS_COMMON_UNBLOCKED)))
+ goto out_error_free;
- ret = zfcp_fsf_req_send(fsf_req);
- if (ret) {
- ZFCP_LOG_DEBUG("error: initiation of CT request failed "
- "(adapter %s, port 0x%016Lx)\n",
- zfcp_get_busid_by_adapter(adapter), port->wwpn);
- goto failed_send;
- }
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
- ZFCP_LOG_DEBUG("CT request initiated (adapter %s, port 0x%016Lx)\n",
- zfcp_get_busid_by_adapter(adapter), port->wwpn);
- goto out;
+ req->data = unit;
+ req->handler = zfcp_fsf_abort_fcp_command_handler;
+ req->qtcb->header.lun_handle = unit->handle;
+ req->qtcb->header.port_handle = unit->port->handle;
+ req->qtcb->bottom.support.req_handle = (u64) old_req_id;
- failed_send:
- zfcp_fsf_req_free(fsf_req);
- if (erp_action != NULL) {
- erp_action->fsf_req = NULL;
- }
- failed_req:
- out:
- write_unlock_irqrestore(&adapter->request_queue.queue_lock,
- lock_flags);
- return ret;
+ zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT);
+ if (!zfcp_fsf_req_send(req))
+ goto out;
+
+out_error_free:
+ zfcp_fsf_req_free(req);
+ req = NULL;
+out:
+ spin_unlock(&adapter->req_q_lock);
+ return req;
}
-/**
- * zfcp_fsf_send_ct_handler - handler for Generic Service requests
- * @fsf_req: pointer to struct zfcp_fsf_req
- *
- * Data specific for the Generic Service request is passed using
- * fsf_req->data. There we find the pointer to struct zfcp_send_ct.
- * Usually a specific handler for the CT request is called which is
- * found in this structure.
- */
-static int
-zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
{
- struct zfcp_port *port;
- struct zfcp_adapter *adapter;
- struct zfcp_send_ct *send_ct;
- struct fsf_qtcb_header *header;
- struct fsf_qtcb_bottom_support *bottom;
- int retval = -EINVAL;
- u16 subtable, rule, counter;
+ struct zfcp_adapter *adapter = req->adapter;
+ struct zfcp_send_ct *send_ct = req->data;
+ struct fsf_qtcb_header *header = &req->qtcb->header;
- adapter = fsf_req->adapter;
- send_ct = (struct zfcp_send_ct *) fsf_req->data;
- port = send_ct->port;
- header = &fsf_req->qtcb->header;
- bottom = &fsf_req->qtcb->bottom.support;
+ send_ct->status = -EINVAL;
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
goto skip_fsfstatus;
- /* evaluate FSF status in QTCB */
switch (header->fsf_status) {
-
case FSF_GOOD:
- zfcp_san_dbf_event_ct_response(fsf_req);
- retval = 0;
+ zfcp_san_dbf_event_ct_response(req);
+ send_ct->status = 0;
break;
-
case FSF_SERVICE_CLASS_NOT_SUPPORTED:
- ZFCP_LOG_INFO("error: adapter %s does not support fc "
- "class %d.\n",
- zfcp_get_busid_by_port(port),
- ZFCP_FC_SERVICE_CLASS_DEFAULT);
- /* stop operation for this adapter */
- zfcp_erp_adapter_shutdown(adapter, 0, 123, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_fsf_class_not_supp(req);
break;
-
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (header->fsf_status_qual.word[0]){
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
- /* reopening link to port */
- zfcp_test_link(port);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- /* ERP strategy will escalate */
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
- default:
- ZFCP_LOG_INFO("bug: Wrong status qualifier 0x%x "
- "arrived.\n",
- header->fsf_status_qual.word[0]);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
break;
-
case FSF_ACCESS_DENIED:
- ZFCP_LOG_NORMAL("access denied, cannot send generic service "
- "command (adapter %s, port d_id=0x%06x)\n",
- zfcp_get_busid_by_port(port), port->d_id);
- for (counter = 0; counter < 2; counter++) {
- subtable = header->fsf_status_qual.halfword[counter * 2];
- rule = header->fsf_status_qual.halfword[counter * 2 + 1];
- switch (subtable) {
- case FSF_SQ_CFDC_SUBTABLE_OS:
- case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
- case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
- case FSF_SQ_CFDC_SUBTABLE_LUN:
- ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
- zfcp_act_subtable_type[subtable], rule);
- break;
- }
- }
- zfcp_erp_port_access_denied(port, 55, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
- case FSF_GENERIC_COMMAND_REJECTED:
- ZFCP_LOG_INFO("generic service command rejected "
- "(adapter %s, port d_id=0x%06x)\n",
- zfcp_get_busid_by_port(port), port->d_id);
- ZFCP_LOG_INFO("status qualifier:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
- (char *) &header->fsf_status_qual,
- sizeof (union fsf_status_qual));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
- case FSF_PORT_HANDLE_NOT_VALID:
- ZFCP_LOG_DEBUG("Temporary port identifier 0x%x for port "
- "0x%016Lx on adapter %s invalid. This may "
- "happen occasionally.\n", port->handle,
- port->wwpn, zfcp_get_busid_by_port(port));
- ZFCP_LOG_INFO("status qualifier:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
- (char *) &header->fsf_status_qual,
- sizeof (union fsf_status_qual));
- zfcp_erp_adapter_reopen(adapter, 0, 106, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
case FSF_PORT_BOXED:
- ZFCP_LOG_INFO("port needs to be reopened "
- "(adapter %s, port d_id=0x%06x)\n",
- zfcp_get_busid_by_port(port), port->d_id);
- zfcp_erp_port_boxed(port, 49, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
- | ZFCP_STATUS_FSFREQ_RETRY;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+ ZFCP_STATUS_FSFREQ_RETRY;
break;
-
- /* following states should never occure, all cases avoided
- in zfcp_fsf_send_ct - but who knows ... */
+ case FSF_PORT_HANDLE_NOT_VALID:
+ zfcp_erp_adapter_reopen(adapter, 0, 106, req);
+ case FSF_GENERIC_COMMAND_REJECTED:
case FSF_PAYLOAD_SIZE_MISMATCH:
- ZFCP_LOG_INFO("payload size mismatch (adapter: %s, "
- "req_buf_length=%d, resp_buf_length=%d)\n",
- zfcp_get_busid_by_adapter(adapter),
- bottom->req_buf_length, bottom->resp_buf_length);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
case FSF_REQUEST_SIZE_TOO_LARGE:
- ZFCP_LOG_INFO("request size too large (adapter: %s, "
- "req_buf_length=%d)\n",
- zfcp_get_busid_by_adapter(adapter),
- bottom->req_buf_length);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
case FSF_RESPONSE_SIZE_TOO_LARGE:
- ZFCP_LOG_INFO("response size too large (adapter: %s, "
- "resp_buf_length=%d)\n",
- zfcp_get_busid_by_adapter(adapter),
- bottom->resp_buf_length);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
case FSF_SBAL_MISMATCH:
- ZFCP_LOG_INFO("SBAL mismatch (adapter: %s, req_buf_length=%d, "
- "resp_buf_length=%d)\n",
- zfcp_get_busid_by_adapter(adapter),
- bottom->req_buf_length, bottom->resp_buf_length);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
- default:
- ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
- "(debug info 0x%x)\n", header->fsf_status);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
skip_fsfstatus:
- send_ct->status = retval;
-
- if (send_ct->handler != NULL)
+ if (send_ct->handler)
send_ct->handler(send_ct->handler_data);
+}
- return retval;
+static int zfcp_fsf_setup_sbals(struct zfcp_fsf_req *req,
+ struct scatterlist *sg_req,
+ struct scatterlist *sg_resp, int max_sbals)
+{
+ int bytes;
+
+ bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
+ sg_req, max_sbals);
+ if (bytes <= 0)
+ return -ENOMEM;
+ req->qtcb->bottom.support.req_buf_length = bytes;
+ req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
+
+ bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
+ sg_resp, max_sbals);
+ if (bytes <= 0)
+ return -ENOMEM;
+ req->qtcb->bottom.support.resp_buf_length = bytes;
+
+ return 0;
}
/**
- * zfcp_fsf_send_els - initiate an ELS command (FC-FS)
- * @els: pointer to struct zfcp_send_els which contains all needed data for
- * the command.
+ * zfcp_fsf_send_ct - initiate a Generic Service request (FC-GS)
+ * @ct: pointer to struct zfcp_send_ct with data for request
+ * @pool: if non-null this mempool is used to allocate struct zfcp_fsf_req
+ * @erp_action: if non-null the Generic Service request sent within ERP
*/
-int
-zfcp_fsf_send_els(struct zfcp_send_els *els)
+int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
+ struct zfcp_erp_action *erp_action)
{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req;
- u32 d_id;
- struct zfcp_adapter *adapter;
- unsigned long lock_flags;
- int bytes;
- int ret = 0;
-
- d_id = els->d_id;
- adapter = els->adapter;
-
- ret = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS,
- ZFCP_REQ_AUTO_CLEANUP,
- NULL, &lock_flags, &fsf_req);
- if (ret < 0) {
- ZFCP_LOG_INFO("error: creation of ELS request failed "
- "(adapter %s, port d_id: 0x%06x)\n",
- zfcp_get_busid_by_adapter(adapter), d_id);
- goto failed_req;
- }
+ struct zfcp_wka_port *wka_port = ct->wka_port;
+ struct zfcp_adapter *adapter = wka_port->adapter;
+ struct zfcp_fsf_req *req;
+ int ret = -EIO;
- if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &els->port->status))) {
- ret = -EBUSY;
- goto port_blocked;
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
+
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_GENERIC,
+ ZFCP_REQ_AUTO_CLEANUP, pool);
+ if (IS_ERR(req)) {
+ ret = PTR_ERR(req);
+ goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
- if (zfcp_use_one_sbal(els->req, els->req_count,
- els->resp, els->resp_count)){
- /* both request buffer and response buffer
- fit into one sbale each */
- sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
- sbale[2].addr = zfcp_sg_to_address(&els->req[0]);
- sbale[2].length = els->req[0].length;
- sbale[3].addr = zfcp_sg_to_address(&els->resp[0]);
- sbale[3].length = els->resp[0].length;
- sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
- } else if (adapter->adapter_features &
- FSF_FEATURE_ELS_CT_CHAINED_SBALS) {
- /* try to use chained SBALs */
- bytes = zfcp_qdio_sbals_from_sg(fsf_req,
- SBAL_FLAGS0_TYPE_WRITE_READ,
- els->req, els->req_count,
- ZFCP_MAX_SBALS_PER_ELS_REQ);
- if (bytes <= 0) {
- ZFCP_LOG_INFO("error: creation of ELS request failed "
- "(adapter %s, port d_id: 0x%06x)\n",
- zfcp_get_busid_by_adapter(adapter), d_id);
- if (bytes == 0) {
- ret = -ENOMEM;
- } else {
- ret = bytes;
- }
- goto failed_send;
- }
- fsf_req->qtcb->bottom.support.req_buf_length = bytes;
- fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
- bytes = zfcp_qdio_sbals_from_sg(fsf_req,
- SBAL_FLAGS0_TYPE_WRITE_READ,
- els->resp, els->resp_count,
- ZFCP_MAX_SBALS_PER_ELS_REQ);
- if (bytes <= 0) {
- ZFCP_LOG_INFO("error: creation of ELS request failed "
- "(adapter %s, port d_id: 0x%06x)\n",
- zfcp_get_busid_by_adapter(adapter), d_id);
- if (bytes == 0) {
- ret = -ENOMEM;
- } else {
- ret = bytes;
- }
- goto failed_send;
- }
- fsf_req->qtcb->bottom.support.resp_buf_length = bytes;
- } else {
- /* reject request */
- ZFCP_LOG_INFO("error: microcode does not support chained SBALs"
- ", ELS request too big (adapter %s, "
- "port d_id: 0x%06x)\n",
- zfcp_get_busid_by_adapter(adapter), d_id);
- ret = -EOPNOTSUPP;
- goto failed_send;
- }
-
- /* settings in QTCB */
- fsf_req->qtcb->bottom.support.d_id = d_id;
- fsf_req->qtcb->bottom.support.service_class =
- ZFCP_FC_SERVICE_CLASS_DEFAULT;
- fsf_req->qtcb->bottom.support.timeout = ZFCP_ELS_TIMEOUT;
- fsf_req->data = (unsigned long) els;
-
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-
- zfcp_san_dbf_event_els_request(fsf_req);
-
- zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
- ret = zfcp_fsf_req_send(fsf_req);
- if (ret) {
- ZFCP_LOG_DEBUG("error: initiation of ELS request failed "
- "(adapter %s, port d_id: 0x%06x)\n",
- zfcp_get_busid_by_adapter(adapter), d_id);
+ ret = zfcp_fsf_setup_sbals(req, ct->req, ct->resp,
+ FSF_MAX_SBALS_PER_REQ);
+ if (ret)
goto failed_send;
- }
- ZFCP_LOG_DEBUG("ELS request initiated (adapter %s, port d_id: "
- "0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id);
- goto out;
+ req->handler = zfcp_fsf_send_ct_handler;
+ req->qtcb->header.port_handle = wka_port->handle;
+ req->qtcb->bottom.support.service_class = FSF_CLASS_3;
+ req->qtcb->bottom.support.timeout = ct->timeout;
+ req->data = ct;
- port_blocked:
- failed_send:
- zfcp_fsf_req_free(fsf_req);
+ zfcp_san_dbf_event_ct_request(req);
+
+ if (erp_action) {
+ erp_action->fsf_req = req;
+ req->erp_action = erp_action;
+ zfcp_fsf_start_erp_timer(req);
+ } else
+ zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+
+ ret = zfcp_fsf_req_send(req);
+ if (ret)
+ goto failed_send;
- failed_req:
- out:
- write_unlock_irqrestore(&adapter->request_queue.queue_lock,
- lock_flags);
+ goto out;
- return ret;
+failed_send:
+ zfcp_fsf_req_free(req);
+ if (erp_action)
+ erp_action->fsf_req = NULL;
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
+ return ret;
}
-/**
- * zfcp_fsf_send_els_handler - handler for ELS commands
- * @fsf_req: pointer to struct zfcp_fsf_req
- *
- * Data specific for the ELS command is passed using
- * fsf_req->data. There we find the pointer to struct zfcp_send_els.
- * Usually a specific handler for the ELS command is called which is
- * found in this structure.
- */
-static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req)
{
- struct zfcp_adapter *adapter;
- struct zfcp_port *port;
- u32 d_id;
- struct fsf_qtcb_header *header;
- struct fsf_qtcb_bottom_support *bottom;
- struct zfcp_send_els *send_els;
- int retval = -EINVAL;
- u16 subtable, rule, counter;
-
- send_els = (struct zfcp_send_els *) fsf_req->data;
- adapter = send_els->adapter;
- port = send_els->port;
- d_id = send_els->d_id;
- header = &fsf_req->qtcb->header;
- bottom = &fsf_req->qtcb->bottom.support;
-
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ struct zfcp_send_els *send_els = req->data;
+ struct zfcp_port *port = send_els->port;
+ struct fsf_qtcb_header *header = &req->qtcb->header;
+
+ send_els->status = -EINVAL;
+
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
goto skip_fsfstatus;
switch (header->fsf_status) {
-
case FSF_GOOD:
- zfcp_san_dbf_event_els_response(fsf_req);
- retval = 0;
+ zfcp_san_dbf_event_els_response(req);
+ send_els->status = 0;
break;
-
case FSF_SERVICE_CLASS_NOT_SUPPORTED:
- ZFCP_LOG_INFO("error: adapter %s does not support fc "
- "class %d.\n",
- zfcp_get_busid_by_adapter(adapter),
- ZFCP_FC_SERVICE_CLASS_DEFAULT);
- /* stop operation for this adapter */
- zfcp_erp_adapter_shutdown(adapter, 0, 124, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_fsf_class_not_supp(req);
break;
-
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (header->fsf_status_qual.word[0]){
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
if (port && (send_els->ls_code != ZFCP_LS_ADISC))
zfcp_test_link(port);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
+ /*fall through */
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval =
- zfcp_handle_els_rjt(header->fsf_status_qual.word[1],
- (struct zfcp_ls_rjt_par *)
- &header->fsf_status_qual.word[2]);
- break;
case FSF_SQ_RETRY_IF_POSSIBLE:
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
- default:
- ZFCP_LOG_INFO("bug: Wrong status qualifier 0x%x\n",
- header->fsf_status_qual.word[0]);
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
- (char*)header->fsf_status_qual.word, 16);
}
break;
-
case FSF_ELS_COMMAND_REJECTED:
- ZFCP_LOG_INFO("ELS has been rejected because command filter "
- "prohibited sending "
- "(adapter: %s, port d_id: 0x%06x)\n",
- zfcp_get_busid_by_adapter(adapter), d_id);
-
- break;
-
case FSF_PAYLOAD_SIZE_MISMATCH:
- ZFCP_LOG_INFO(
- "ELS request size and ELS response size must be either "
- "both 0, or both greater than 0 "
- "(adapter: %s, req_buf_length=%d resp_buf_length=%d)\n",
- zfcp_get_busid_by_adapter(adapter),
- bottom->req_buf_length,
- bottom->resp_buf_length);
- break;
-
case FSF_REQUEST_SIZE_TOO_LARGE:
- ZFCP_LOG_INFO(
- "Length of the ELS request buffer, "
- "specified in QTCB bottom, "
- "exceeds the size of the buffers "
- "that have been allocated for ELS request data "
- "(adapter: %s, req_buf_length=%d)\n",
- zfcp_get_busid_by_adapter(adapter),
- bottom->req_buf_length);
- break;
-
case FSF_RESPONSE_SIZE_TOO_LARGE:
- ZFCP_LOG_INFO(
- "Length of the ELS response buffer, "
- "specified in QTCB bottom, "
- "exceeds the size of the buffers "
- "that have been allocated for ELS response data "
- "(adapter: %s, resp_buf_length=%d)\n",
- zfcp_get_busid_by_adapter(adapter),
- bottom->resp_buf_length);
- break;
-
- case FSF_SBAL_MISMATCH:
- /* should never occure, avoided in zfcp_fsf_send_els */
- ZFCP_LOG_INFO("SBAL mismatch (adapter: %s, req_buf_length=%d, "
- "resp_buf_length=%d)\n",
- zfcp_get_busid_by_adapter(adapter),
- bottom->req_buf_length, bottom->resp_buf_length);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
case FSF_ACCESS_DENIED:
- ZFCP_LOG_NORMAL("access denied, cannot send ELS command "
- "(adapter %s, port d_id=0x%06x)\n",
- zfcp_get_busid_by_adapter(adapter), d_id);
- for (counter = 0; counter < 2; counter++) {
- subtable = header->fsf_status_qual.halfword[counter * 2];
- rule = header->fsf_status_qual.halfword[counter * 2 + 1];
- switch (subtable) {
- case FSF_SQ_CFDC_SUBTABLE_OS:
- case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
- case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
- case FSF_SQ_CFDC_SUBTABLE_LUN:
- ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
- zfcp_act_subtable_type[subtable], rule);
- break;
- }
- }
- if (port != NULL)
- zfcp_erp_port_access_denied(port, 56, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_fsf_access_denied_port(req, port);
break;
-
+ case FSF_SBAL_MISMATCH:
+ /* should never occure, avoided in zfcp_fsf_send_els */
+ /* fall through */
default:
- ZFCP_LOG_NORMAL(
- "bug: An unknown FSF Status was presented "
- "(adapter: %s, fsf_status=0x%08x)\n",
- zfcp_get_busid_by_adapter(adapter),
- header->fsf_status);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
-
skip_fsfstatus:
- send_els->status = retval;
-
if (send_els->handler)
send_els->handler(send_els->handler_data);
+}
- return retval;
+/**
+ * zfcp_fsf_send_els - initiate an ELS command (FC-FS)
+ * @els: pointer to struct zfcp_send_els with data for the command
+ */
+int zfcp_fsf_send_els(struct zfcp_send_els *els)
+{
+ struct zfcp_fsf_req *req;
+ struct zfcp_adapter *adapter = els->adapter;
+ struct fsf_qtcb_bottom_support *bottom;
+ int ret = -EIO;
+
+ if (unlikely(!(atomic_read(&els->port->status) &
+ ZFCP_STATUS_COMMON_UNBLOCKED)))
+ return -EBUSY;
+
+ spin_lock(&adapter->req_q_lock);
+ if (!zfcp_fsf_sbal_available(adapter))
+ goto out;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS,
+ ZFCP_REQ_AUTO_CLEANUP, NULL);
+ if (IS_ERR(req)) {
+ ret = PTR_ERR(req);
+ goto out;
+ }
+
+ ret = zfcp_fsf_setup_sbals(req, els->req, els->resp, 2);
+
+ if (ret)
+ goto failed_send;
+
+ bottom = &req->qtcb->bottom.support;
+ req->handler = zfcp_fsf_send_els_handler;
+ bottom->d_id = els->d_id;
+ bottom->service_class = FSF_CLASS_3;
+ bottom->timeout = 2 * R_A_TOV;
+ req->data = els;
+
+ zfcp_san_dbf_event_els_request(req);
+
+ zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+ ret = zfcp_fsf_req_send(req);
+ if (ret)
+ goto failed_send;
+
+ goto out;
+
+failed_send:
+ zfcp_fsf_req_free(req);
+out:
+ spin_unlock(&adapter->req_q_lock);
+ return ret;
}
-int
-zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
+int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req;
+ struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *req;
struct zfcp_adapter *adapter = erp_action->adapter;
- unsigned long lock_flags;
- int retval;
-
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(adapter,
- FSF_QTCB_EXCHANGE_CONFIG_DATA,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_erp,
- &lock_flags, &fsf_req);
- if (retval) {
- ZFCP_LOG_INFO("error: Could not create exchange configuration "
- "data request for adapter %s.\n",
- zfcp_get_busid_by_adapter(adapter));
- write_unlock_irqrestore(&adapter->request_queue.queue_lock,
- lock_flags);
- return retval;
+ int retval = -EIO;
+
+ spin_lock_bh(&adapter->req_q_lock);
+ if (!zfcp_fsf_sbal_available(adapter))
+ goto out;
+ req = zfcp_fsf_req_create(adapter,
+ FSF_QTCB_EXCHANGE_CONFIG_DATA,
+ ZFCP_REQ_AUTO_CLEANUP,
+ adapter->pool.fsf_req_erp);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
+ goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
- fsf_req->qtcb->bottom.config.feature_selection =
+ req->qtcb->bottom.config.feature_selection =
FSF_FEATURE_CFDC |
FSF_FEATURE_LUN_SHARING |
FSF_FEATURE_NOTIFICATION_LOST |
FSF_FEATURE_UPDATE_ALERT;
- fsf_req->erp_action = erp_action;
- erp_action->fsf_req = fsf_req;
+ req->erp_action = erp_action;
+ req->handler = zfcp_fsf_exchange_config_data_handler;
+ erp_action->fsf_req = req;
- zfcp_erp_start_timer(fsf_req);
- retval = zfcp_fsf_req_send(fsf_req);
- write_unlock_irqrestore(&adapter->request_queue.queue_lock,
- lock_flags);
+ zfcp_fsf_start_erp_timer(req);
+ retval = zfcp_fsf_req_send(req);
if (retval) {
- ZFCP_LOG_INFO("error: Could not send exchange configuration "
- "data command on the adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_fsf_req_free(fsf_req);
+ zfcp_fsf_req_free(req);
erp_action->fsf_req = NULL;
}
- else
- ZFCP_LOG_DEBUG("exchange configuration data request initiated "
- "(adapter %s)\n",
- zfcp_get_busid_by_adapter(adapter));
-
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
return retval;
}
-int
-zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter,
- struct fsf_qtcb_bottom_config *data)
+int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter,
+ struct fsf_qtcb_bottom_config *data)
{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req;
- unsigned long lock_flags;
- int retval;
-
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA,
- ZFCP_WAIT_FOR_SBAL, NULL, &lock_flags,
- &fsf_req);
- if (retval) {
- ZFCP_LOG_INFO("error: Could not create exchange configuration "
- "data request for adapter %s.\n",
- zfcp_get_busid_by_adapter(adapter));
- write_unlock_irqrestore(&adapter->request_queue.queue_lock,
- lock_flags);
- return retval;
+ struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *req = NULL;
+ int retval = -EIO;
+
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
+
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA,
+ 0, NULL);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
+ goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+ req->handler = zfcp_fsf_exchange_config_data_handler;
- fsf_req->qtcb->bottom.config.feature_selection =
+ req->qtcb->bottom.config.feature_selection =
FSF_FEATURE_CFDC |
FSF_FEATURE_LUN_SHARING |
FSF_FEATURE_NOTIFICATION_LOST |
FSF_FEATURE_UPDATE_ALERT;
if (data)
- fsf_req->data = (unsigned long) data;
+ req->data = data;
- zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
- retval = zfcp_fsf_req_send(fsf_req);
- write_unlock_irqrestore(&adapter->request_queue.queue_lock,
- lock_flags);
- if (retval)
- ZFCP_LOG_INFO("error: Could not send exchange configuration "
- "data command on the adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- else
- wait_event(fsf_req->completion_wq,
- fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+ retval = zfcp_fsf_req_send(req);
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
+ if (!retval)
+ wait_event(req->completion_wq,
+ req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
- zfcp_fsf_req_free(fsf_req);
+ zfcp_fsf_req_free(req);
return retval;
}
/**
- * zfcp_fsf_exchange_config_evaluate
- * @fsf_req: fsf_req which belongs to xchg config data request
- * @xchg_ok: specifies if xchg config data was incomplete or complete (0/1)
- *
- * returns: -EIO on error, 0 otherwise
- */
-static int
-zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok)
-{
- struct fsf_qtcb_bottom_config *bottom;
- struct zfcp_adapter *adapter = fsf_req->adapter;
- struct Scsi_Host *shost = adapter->scsi_host;
-
- bottom = &fsf_req->qtcb->bottom.config;
- ZFCP_LOG_DEBUG("low/high QTCB version 0x%x/0x%x of FSF\n",
- bottom->low_qtcb_version, bottom->high_qtcb_version);
- adapter->fsf_lic_version = bottom->lic_version;
- adapter->adapter_features = bottom->adapter_features;
- adapter->connection_features = bottom->connection_features;
- adapter->peer_wwpn = 0;
- adapter->peer_wwnn = 0;
- adapter->peer_d_id = 0;
-
- if (xchg_ok) {
-
- if (fsf_req->data)
- memcpy((struct fsf_qtcb_bottom_config *) fsf_req->data,
- bottom, sizeof (struct fsf_qtcb_bottom_config));
-
- fc_host_node_name(shost) = bottom->nport_serv_param.wwnn;
- fc_host_port_name(shost) = bottom->nport_serv_param.wwpn;
- fc_host_port_id(shost) = bottom->s_id & ZFCP_DID_MASK;
- fc_host_speed(shost) = bottom->fc_link_speed;
- fc_host_supported_classes(shost) =
- FC_COS_CLASS2 | FC_COS_CLASS3;
- adapter->hydra_version = bottom->adapter_type;
- if (fc_host_permanent_port_name(shost) == -1)
- fc_host_permanent_port_name(shost) =
- fc_host_port_name(shost);
- if (bottom->fc_topology == FSF_TOPO_P2P) {
- adapter->peer_d_id = bottom->peer_d_id & ZFCP_DID_MASK;
- adapter->peer_wwpn = bottom->plogi_payload.wwpn;
- adapter->peer_wwnn = bottom->plogi_payload.wwnn;
- fc_host_port_type(shost) = FC_PORTTYPE_PTP;
- } else if (bottom->fc_topology == FSF_TOPO_FABRIC)
- fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
- else if (bottom->fc_topology == FSF_TOPO_AL)
- fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
- else
- fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
- } else {
- fc_host_node_name(shost) = 0;
- fc_host_port_name(shost) = 0;
- fc_host_port_id(shost) = 0;
- fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
- fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
- adapter->hydra_version = 0;
- }
-
- if (adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT) {
- adapter->hardware_version = bottom->hardware_version;
- memcpy(fc_host_serial_number(shost), bottom->serial_number,
- min(FC_SERIAL_NUMBER_SIZE, 17));
- EBCASC(fc_host_serial_number(shost),
- min(FC_SERIAL_NUMBER_SIZE, 17));
- }
-
- if (fsf_req->erp_action)
- ZFCP_LOG_NORMAL("The adapter %s reported the following "
- "characteristics:\n"
- "WWNN 0x%016Lx, WWPN 0x%016Lx, "
- "S_ID 0x%06x,\n"
- "adapter version 0x%x, "
- "LIC version 0x%x, "
- "FC link speed %d Gb/s\n",
- zfcp_get_busid_by_adapter(adapter),
- (wwn_t) fc_host_node_name(shost),
- (wwn_t) fc_host_port_name(shost),
- fc_host_port_id(shost),
- adapter->hydra_version,
- adapter->fsf_lic_version,
- fc_host_speed(shost));
- if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) {
- ZFCP_LOG_NORMAL("error: the adapter %s "
- "only supports newer control block "
- "versions in comparison to this device "
- "driver (try updated device driver)\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_erp_adapter_shutdown(adapter, 0, 125, fsf_req);
- return -EIO;
- }
- if (ZFCP_QTCB_VERSION > bottom->high_qtcb_version) {
- ZFCP_LOG_NORMAL("error: the adapter %s "
- "only supports older control block "
- "versions than this device driver uses"
- "(consider a microcode upgrade)\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_erp_adapter_shutdown(adapter, 0, 126, fsf_req);
- return -EIO;
- }
- return 0;
-}
-
-/**
- * function: zfcp_fsf_exchange_config_data_handler
- *
- * purpose: is called for finished Exchange Configuration Data command
- *
- * returns:
- */
-static int
-zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req)
-{
- struct fsf_qtcb_bottom_config *bottom;
- struct zfcp_adapter *adapter = fsf_req->adapter;
- struct fsf_qtcb *qtcb = fsf_req->qtcb;
-
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
- return -EIO;
-
- switch (qtcb->header.fsf_status) {
-
- case FSF_GOOD:
- if (zfcp_fsf_exchange_config_evaluate(fsf_req, 1))
- return -EIO;
-
- switch (fc_host_port_type(adapter->scsi_host)) {
- case FC_PORTTYPE_PTP:
- ZFCP_LOG_NORMAL("Point-to-Point fibrechannel "
- "configuration detected at adapter %s\n"
- "Peer WWNN 0x%016llx, "
- "peer WWPN 0x%016llx, "
- "peer d_id 0x%06x\n",
- zfcp_get_busid_by_adapter(adapter),
- adapter->peer_wwnn,
- adapter->peer_wwpn,
- adapter->peer_d_id);
- break;
- case FC_PORTTYPE_NLPORT:
- ZFCP_LOG_NORMAL("error: Arbitrated loop fibrechannel "
- "topology detected at adapter %s "
- "unsupported, shutting down adapter\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_erp_adapter_shutdown(adapter, 0, 127, fsf_req);
- return -EIO;
- case FC_PORTTYPE_NPORT:
- if (fsf_req->erp_action)
- ZFCP_LOG_NORMAL("Switched fabric fibrechannel "
- "network detected at adapter "
- "%s.\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
- default:
- ZFCP_LOG_NORMAL("bug: The fibrechannel topology "
- "reported by the exchange "
- "configuration command for "
- "the adapter %s is not "
- "of a type known to the zfcp "
- "driver, shutting down adapter\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_erp_adapter_shutdown(adapter, 0, 128, fsf_req);
- return -EIO;
- }
- bottom = &qtcb->bottom.config;
- if (bottom->max_qtcb_size < sizeof(struct fsf_qtcb)) {
- ZFCP_LOG_NORMAL("bug: Maximum QTCB size (%d bytes) "
- "allowed by the adapter %s "
- "is lower than the minimum "
- "required by the driver (%ld bytes).\n",
- bottom->max_qtcb_size,
- zfcp_get_busid_by_adapter(adapter),
- sizeof(struct fsf_qtcb));
- zfcp_erp_adapter_shutdown(adapter, 0, 129, fsf_req);
- return -EIO;
- }
- atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
- &adapter->status);
- break;
- case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
- if (zfcp_fsf_exchange_config_evaluate(fsf_req, 0))
- return -EIO;
-
- atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
- &adapter->status);
-
- zfcp_fsf_link_down_info_eval(fsf_req, 42,
- &qtcb->header.fsf_status_qual.link_down_info);
- break;
- default:
- zfcp_erp_adapter_shutdown(adapter, 0, 130, fsf_req);
- return -EIO;
- }
- return 0;
-}
-
-/**
* zfcp_fsf_exchange_port_data - request information about local port
* @erp_action: ERP action for the adapter for which port data is requested
+ * Returns: 0 on success, error otherwise
*/
-int
-zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action)
+int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action)
{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req;
+ struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *req;
struct zfcp_adapter *adapter = erp_action->adapter;
- unsigned long lock_flags;
- int retval;
+ int retval = -EIO;
- if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT)) {
- ZFCP_LOG_INFO("error: exchange port data "
- "command not supported by adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
+ if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT))
return -EOPNOTSUPP;
- }
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_erp,
- &lock_flags, &fsf_req);
- if (retval) {
- ZFCP_LOG_INFO("error: Out of resources. Could not create an "
- "exchange port data request for "
- "the adapter %s.\n",
- zfcp_get_busid_by_adapter(adapter));
- write_unlock_irqrestore(&adapter->request_queue.queue_lock,
- lock_flags);
- return retval;
+ spin_lock_bh(&adapter->req_q_lock);
+ if (!zfcp_fsf_sbal_available(adapter))
+ goto out;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
+ ZFCP_REQ_AUTO_CLEANUP,
+ adapter->pool.fsf_req_erp);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
+ goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
- erp_action->fsf_req = fsf_req;
- fsf_req->erp_action = erp_action;
- zfcp_erp_start_timer(fsf_req);
-
- retval = zfcp_fsf_req_send(fsf_req);
- write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
+ req->handler = zfcp_fsf_exchange_port_data_handler;
+ req->erp_action = erp_action;
+ erp_action->fsf_req = req;
+ zfcp_fsf_start_erp_timer(req);
+ retval = zfcp_fsf_req_send(req);
if (retval) {
- ZFCP_LOG_INFO("error: Could not send an exchange port data "
- "command on the adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_fsf_req_free(fsf_req);
+ zfcp_fsf_req_free(req);
erp_action->fsf_req = NULL;
}
- else
- ZFCP_LOG_DEBUG("exchange port data request initiated "
- "(adapter %s)\n",
- zfcp_get_busid_by_adapter(adapter));
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
return retval;
}
-
/**
* zfcp_fsf_exchange_port_data_sync - request information about local port
- * and wait until information is ready
+ * @adapter: pointer to struct zfcp_adapter
+ * @data: pointer to struct fsf_qtcb_bottom_port
+ * Returns: 0 on success, error otherwise
*/
-int
-zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
- struct fsf_qtcb_bottom_port *data)
+int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
+ struct fsf_qtcb_bottom_port *data)
{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req;
- unsigned long lock_flags;
- int retval;
-
- if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT)) {
- ZFCP_LOG_INFO("error: exchange port data "
- "command not supported by adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
+ struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *req = NULL;
+ int retval = -EIO;
+
+ if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT))
return -EOPNOTSUPP;
- }
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
- 0, NULL, &lock_flags, &fsf_req);
- if (retval) {
- ZFCP_LOG_INFO("error: Out of resources. Could not create an "
- "exchange port data request for "
- "the adapter %s.\n",
- zfcp_get_busid_by_adapter(adapter));
- write_unlock_irqrestore(&adapter->request_queue.queue_lock,
- lock_flags);
- return retval;
+ spin_lock_bh(&adapter->req_q_lock);
+ if (!zfcp_fsf_sbal_available(adapter))
+ goto out;
+
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, 0,
+ NULL);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
+ goto out;
}
if (data)
- fsf_req->data = (unsigned long) data;
+ req->data = data;
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
- zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
- retval = zfcp_fsf_req_send(fsf_req);
- write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-
- if (retval)
- ZFCP_LOG_INFO("error: Could not send an exchange port data "
- "command on the adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- else
- wait_event(fsf_req->completion_wq,
- fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
-
- zfcp_fsf_req_free(fsf_req);
-
- return retval;
-}
-
-/**
- * zfcp_fsf_exchange_port_evaluate
- * @fsf_req: fsf_req which belongs to xchg port data request
- * @xchg_ok: specifies if xchg port data was incomplete or complete (0/1)
- */
-static void
-zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok)
-{
- struct zfcp_adapter *adapter;
- struct fsf_qtcb_bottom_port *bottom;
- struct Scsi_Host *shost;
-
- adapter = fsf_req->adapter;
- bottom = &fsf_req->qtcb->bottom.port;
- shost = adapter->scsi_host;
-
- if (fsf_req->data)
- memcpy((struct fsf_qtcb_bottom_port*) fsf_req->data, bottom,
- sizeof(struct fsf_qtcb_bottom_port));
-
- if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
- fc_host_permanent_port_name(shost) = bottom->wwpn;
- else
- fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
- fc_host_maxframe_size(shost) = bottom->maximum_frame_size;
- fc_host_supported_speeds(shost) = bottom->supported_speed;
-}
-
-/**
- * zfcp_fsf_exchange_port_data_handler - handler for exchange_port_data request
- * @fsf_req: pointer to struct zfcp_fsf_req
- */
-static void
-zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *fsf_req)
-{
- struct zfcp_adapter *adapter;
- struct fsf_qtcb *qtcb;
-
- adapter = fsf_req->adapter;
- qtcb = fsf_req->qtcb;
-
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
- return;
-
- switch (qtcb->header.fsf_status) {
- case FSF_GOOD:
- zfcp_fsf_exchange_port_evaluate(fsf_req, 1);
- atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
- break;
- case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
- zfcp_fsf_exchange_port_evaluate(fsf_req, 0);
- atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
- zfcp_fsf_link_down_info_eval(fsf_req, 43,
- &qtcb->header.fsf_status_qual.link_down_info);
- break;
- }
-}
-
-
-/*
- * function: zfcp_fsf_open_port
- *
- * purpose:
- *
- * returns: address of initiated FSF request
- * NULL - request could not be initiated
- */
-int
-zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
-{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req;
- unsigned long lock_flags;
- int retval = 0;
-
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(erp_action->adapter,
- FSF_QTCB_OPEN_PORT_WITH_DID,
- ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
- erp_action->adapter->pool.fsf_req_erp,
- &lock_flags, &fsf_req);
- if (retval < 0) {
- ZFCP_LOG_INFO("error: Could not create open port request "
- "for port 0x%016Lx on adapter %s.\n",
- erp_action->port->wwpn,
- zfcp_get_busid_by_adapter(erp_action->adapter));
- goto out;
- }
-
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
- sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
- sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-
- fsf_req->qtcb->bottom.support.d_id = erp_action->port->d_id;
- atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status);
- fsf_req->data = (unsigned long) erp_action->port;
- fsf_req->erp_action = erp_action;
- erp_action->fsf_req = fsf_req;
-
- zfcp_erp_start_timer(fsf_req);
- retval = zfcp_fsf_req_send(fsf_req);
- if (retval) {
- ZFCP_LOG_INFO("error: Could not send open port request for "
- "port 0x%016Lx on adapter %s.\n",
- erp_action->port->wwpn,
- zfcp_get_busid_by_adapter(erp_action->adapter));
- zfcp_fsf_req_free(fsf_req);
- erp_action->fsf_req = NULL;
- goto out;
- }
+ req->handler = zfcp_fsf_exchange_port_data_handler;
+ zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+ retval = zfcp_fsf_req_send(req);
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
+ if (!retval)
+ wait_event(req->completion_wq,
+ req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ zfcp_fsf_req_free(req);
- ZFCP_LOG_DEBUG("open port request initiated "
- "(adapter %s, port 0x%016Lx)\n",
- zfcp_get_busid_by_adapter(erp_action->adapter),
- erp_action->port->wwpn);
- out:
- write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
- lock_flags);
return retval;
}
-/*
- * function: zfcp_fsf_open_port_handler
- *
- * purpose: is called for finished Open Port command
- *
- * returns:
- */
-static int
-zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
{
- int retval = -EINVAL;
- struct zfcp_port *port;
+ struct zfcp_port *port = req->data;
+ struct fsf_qtcb_header *header = &req->qtcb->header;
struct fsf_plogi *plogi;
- struct fsf_qtcb_header *header;
- u16 subtable, rule, counter;
- port = (struct zfcp_port *) fsf_req->data;
- header = &fsf_req->qtcb->header;
-
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- /* don't change port status in our bookkeeping */
- goto skip_fsfstatus;
- }
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return;
- /* evaluate FSF status in QTCB */
switch (header->fsf_status) {
-
case FSF_PORT_ALREADY_OPEN:
- ZFCP_LOG_NORMAL("bug: remote port 0x%016Lx on adapter %s "
- "is already open.\n",
- port->wwpn, zfcp_get_busid_by_port(port));
- /*
- * This is a bug, however operation should continue normally
- * if it is simply ignored
- */
break;
-
case FSF_ACCESS_DENIED:
- ZFCP_LOG_NORMAL("Access denied, cannot open port 0x%016Lx "
- "on adapter %s\n",
- port->wwpn, zfcp_get_busid_by_port(port));
- for (counter = 0; counter < 2; counter++) {
- subtable = header->fsf_status_qual.halfword[counter * 2];
- rule = header->fsf_status_qual.halfword[counter * 2 + 1];
- switch (subtable) {
- case FSF_SQ_CFDC_SUBTABLE_OS:
- case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
- case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
- case FSF_SQ_CFDC_SUBTABLE_LUN:
- ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
- zfcp_act_subtable_type[subtable], rule);
- break;
- }
- }
- zfcp_erp_port_access_denied(port, 57, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_fsf_access_denied_port(req, port);
break;
-
case FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED:
- ZFCP_LOG_INFO("error: The FSF adapter is out of resources. "
- "The remote port 0x%016Lx on adapter %s "
- "could not be opened. Disabling it.\n",
- port->wwpn, zfcp_get_busid_by_port(port));
- zfcp_erp_port_failed(port, 31, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ dev_warn(&req->adapter->ccw_device->dev,
+ "Not enough FCP adapter resources to open "
+ "remote port 0x%016Lx\n",
+ (unsigned long long)port->wwpn);
+ zfcp_erp_port_failed(port, 31, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (header->fsf_status_qual.word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
- /* ERP strategy will escalate */
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- /* ERP strategy will escalate */
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_SQ_NO_RETRY_POSSIBLE:
- ZFCP_LOG_NORMAL("The remote port 0x%016Lx on "
- "adapter %s could not be opened. "
- "Disabling it.\n",
- port->wwpn,
- zfcp_get_busid_by_port(port));
- zfcp_erp_port_failed(port, 32, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
- default:
- ZFCP_LOG_NORMAL
- ("bug: Wrong status qualifier 0x%x arrived.\n",
- header->fsf_status_qual.word[0]);
+ dev_warn(&req->adapter->ccw_device->dev,
+ "Remote port 0x%016Lx could not be opened\n",
+ (unsigned long long)port->wwpn);
+ zfcp_erp_port_failed(port, 32, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
break;
-
case FSF_GOOD:
- /* save port handle assigned by FSF */
port->handle = header->port_handle;
- ZFCP_LOG_INFO("The remote port 0x%016Lx via adapter %s "
- "was opened, it's port handle is 0x%x\n",
- port->wwpn, zfcp_get_busid_by_port(port),
- port->handle);
- /* mark port as open */
atomic_set_mask(ZFCP_STATUS_COMMON_OPEN |
ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
ZFCP_STATUS_COMMON_ACCESS_BOXED,
&port->status);
- retval = 0;
/* check whether D_ID has changed during open */
/*
* FIXME: This check is not airtight, as the FCP channel does
@@ -2526,320 +1435,281 @@ zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req)
* another GID_PN straight after a port has been opened.
* Alternately, an ADISC/PDISC ELS should suffice, as well.
*/
- plogi = (struct fsf_plogi *) fsf_req->qtcb->bottom.support.els;
- if (!atomic_test_mask(ZFCP_STATUS_PORT_NO_WWPN, &port->status))
- {
- if (fsf_req->qtcb->bottom.support.els1_length <
- sizeof (struct fsf_plogi)) {
- ZFCP_LOG_INFO(
- "warning: insufficient length of "
- "PLOGI payload (%i)\n",
- fsf_req->qtcb->bottom.support.els1_length);
- /* skip sanity check and assume wwpn is ok */
- } else {
- if (plogi->serv_param.wwpn != port->wwpn) {
- ZFCP_LOG_INFO("warning: d_id of port "
- "0x%016Lx changed during "
- "open\n", port->wwpn);
- atomic_clear_mask(
- ZFCP_STATUS_PORT_DID_DID,
- &port->status);
- } else {
- port->wwnn = plogi->serv_param.wwnn;
- zfcp_plogi_evaluate(port, plogi);
- }
+ plogi = (struct fsf_plogi *) req->qtcb->bottom.support.els;
+ if (req->qtcb->bottom.support.els1_length >= sizeof(*plogi)) {
+ if (plogi->serv_param.wwpn != port->wwpn)
+ atomic_clear_mask(ZFCP_STATUS_PORT_DID_DID,
+ &port->status);
+ else {
+ port->wwnn = plogi->serv_param.wwnn;
+ zfcp_fc_plogi_evaluate(port, plogi);
}
}
break;
-
case FSF_UNKNOWN_OP_SUBTYPE:
- /* should never occure, subtype not set in zfcp_fsf_open_port */
- ZFCP_LOG_INFO("unknown operation subtype (adapter: %s, "
- "op_subtype=0x%x)\n",
- zfcp_get_busid_by_port(port),
- fsf_req->qtcb->bottom.support.operation_subtype);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
- default:
- ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
- "(debug info 0x%x)\n",
- header->fsf_status);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
-
- skip_fsfstatus:
- atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &port->status);
- return retval;
}
-/*
- * function: zfcp_fsf_close_port
- *
- * purpose: submit FSF command "close port"
- *
- * returns: address of initiated FSF request
- * NULL - request could not be initiated
+/**
+ * zfcp_fsf_open_port - create and send open port request
+ * @erp_action: pointer to struct zfcp_erp_action
+ * Returns: 0 on success, error otherwise
*/
-int
-zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
+int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req;
- unsigned long lock_flags;
- int retval = 0;
-
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(erp_action->adapter,
- FSF_QTCB_CLOSE_PORT,
- ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
- erp_action->adapter->pool.fsf_req_erp,
- &lock_flags, &fsf_req);
- if (retval < 0) {
- ZFCP_LOG_INFO("error: Could not create a close port request "
- "for port 0x%016Lx on adapter %s.\n",
- erp_action->port->wwpn,
- zfcp_get_busid_by_adapter(erp_action->adapter));
+ struct qdio_buffer_element *sbale;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_fsf_req *req;
+ int retval = -EIO;
+
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
+
+ req = zfcp_fsf_req_create(adapter,
+ FSF_QTCB_OPEN_PORT_WITH_DID,
+ ZFCP_REQ_AUTO_CLEANUP,
+ adapter->pool.fsf_req_erp);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
- atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status);
- fsf_req->data = (unsigned long) erp_action->port;
- fsf_req->erp_action = erp_action;
- fsf_req->qtcb->header.port_handle = erp_action->port->handle;
- fsf_req->erp_action = erp_action;
- erp_action->fsf_req = fsf_req;
+ req->handler = zfcp_fsf_open_port_handler;
+ req->qtcb->bottom.support.d_id = erp_action->port->d_id;
+ req->data = erp_action->port;
+ req->erp_action = erp_action;
+ erp_action->fsf_req = req;
- zfcp_erp_start_timer(fsf_req);
- retval = zfcp_fsf_req_send(fsf_req);
+ zfcp_fsf_start_erp_timer(req);
+ retval = zfcp_fsf_req_send(req);
if (retval) {
- ZFCP_LOG_INFO("error: Could not send a close port request for "
- "port 0x%016Lx on adapter %s.\n",
- erp_action->port->wwpn,
- zfcp_get_busid_by_adapter(erp_action->adapter));
- zfcp_fsf_req_free(fsf_req);
+ zfcp_fsf_req_free(req);
erp_action->fsf_req = NULL;
- goto out;
}
-
- ZFCP_LOG_TRACE("close port request initiated "
- "(adapter %s, port 0x%016Lx)\n",
- zfcp_get_busid_by_adapter(erp_action->adapter),
- erp_action->port->wwpn);
- out:
- write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
- lock_flags);
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
return retval;
}
-/*
- * function: zfcp_fsf_close_port_handler
- *
- * purpose: is called for finished Close Port FSF command
- *
- * returns:
- */
-static int
-zfcp_fsf_close_port_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_close_port_handler(struct zfcp_fsf_req *req)
{
- int retval = -EINVAL;
- struct zfcp_port *port;
-
- port = (struct zfcp_port *) fsf_req->data;
+ struct zfcp_port *port = req->data;
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- /* don't change port status in our bookkeeping */
- goto skip_fsfstatus;
- }
-
- /* evaluate FSF status in QTCB */
- switch (fsf_req->qtcb->header.fsf_status) {
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return;
+ switch (req->qtcb->header.fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID:
- ZFCP_LOG_INFO("Temporary port identifier 0x%x for port "
- "0x%016Lx on adapter %s invalid. This may happen "
- "occasionally.\n", port->handle,
- port->wwpn, zfcp_get_busid_by_port(port));
- ZFCP_LOG_DEBUG("status qualifier:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &fsf_req->qtcb->header.fsf_status_qual,
- sizeof (union fsf_status_qual));
- zfcp_erp_adapter_reopen(port->adapter, 0, 107, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_erp_adapter_reopen(port->adapter, 0, 107, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
case FSF_ADAPTER_STATUS_AVAILABLE:
- /* Note: FSF has actually closed the port in this case.
- * The status code is just daft. Fingers crossed for a change
- */
- retval = 0;
break;
-
case FSF_GOOD:
- ZFCP_LOG_TRACE("remote port 0x016%Lx on adapter %s closed, "
- "port handle 0x%x\n", port->wwpn,
- zfcp_get_busid_by_port(port), port->handle);
- zfcp_erp_modify_port_status(port, 33, fsf_req,
+ zfcp_erp_modify_port_status(port, 33, req,
ZFCP_STATUS_COMMON_OPEN,
ZFCP_CLEAR);
- retval = 0;
- break;
-
- default:
- ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
- "(debug info 0x%x)\n",
- fsf_req->qtcb->header.fsf_status);
break;
}
-
- skip_fsfstatus:
- atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &port->status);
- return retval;
}
-/*
- * function: zfcp_fsf_close_physical_port
- *
- * purpose: submit FSF command "close physical port"
- *
- * returns: address of initiated FSF request
- * NULL - request could not be initiated
+/**
+ * zfcp_fsf_close_port - create and send close port request
+ * @erp_action: pointer to struct zfcp_erp_action
+ * Returns: 0 on success, error otherwise
*/
-int
-zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
+int zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req;
- unsigned long lock_flags;
- int retval = 0;
-
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(erp_action->adapter,
- FSF_QTCB_CLOSE_PHYSICAL_PORT,
- ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
- erp_action->adapter->pool.fsf_req_erp,
- &lock_flags, &fsf_req);
- if (retval < 0) {
- ZFCP_LOG_INFO("error: Could not create close physical port "
- "request (adapter %s, port 0x%016Lx)\n",
- zfcp_get_busid_by_adapter(erp_action->adapter),
- erp_action->port->wwpn);
+ struct qdio_buffer_element *sbale;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_fsf_req *req;
+ int retval = -EIO;
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
+
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_CLOSE_PORT,
+ ZFCP_REQ_AUTO_CLEANUP,
+ adapter->pool.fsf_req_erp);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
- /* mark port as being closed */
- atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING,
- &erp_action->port->status);
- /* save a pointer to this port */
- fsf_req->data = (unsigned long) erp_action->port;
- fsf_req->qtcb->header.port_handle = erp_action->port->handle;
- fsf_req->erp_action = erp_action;
- erp_action->fsf_req = fsf_req;
-
- zfcp_erp_start_timer(fsf_req);
- retval = zfcp_fsf_req_send(fsf_req);
+ req->handler = zfcp_fsf_close_port_handler;
+ req->data = erp_action->port;
+ req->erp_action = erp_action;
+ req->qtcb->header.port_handle = erp_action->port->handle;
+ erp_action->fsf_req = req;
+
+ zfcp_fsf_start_erp_timer(req);
+ retval = zfcp_fsf_req_send(req);
if (retval) {
- ZFCP_LOG_INFO("error: Could not send close physical port "
- "request (adapter %s, port 0x%016Lx)\n",
- zfcp_get_busid_by_adapter(erp_action->adapter),
- erp_action->port->wwpn);
- zfcp_fsf_req_free(fsf_req);
+ zfcp_fsf_req_free(req);
erp_action->fsf_req = NULL;
+ }
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
+ return retval;
+}
+
+static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req)
+{
+ struct zfcp_wka_port *wka_port = req->data;
+ struct fsf_qtcb_header *header = &req->qtcb->header;
+
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+ wka_port->status = ZFCP_WKA_PORT_OFFLINE;
goto out;
}
- ZFCP_LOG_TRACE("close physical port request initiated "
- "(adapter %s, port 0x%016Lx)\n",
- zfcp_get_busid_by_adapter(erp_action->adapter),
- erp_action->port->wwpn);
- out:
- write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
- lock_flags);
+ switch (header->fsf_status) {
+ case FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "Opening WKA port 0x%x failed\n", wka_port->d_id);
+ case FSF_ADAPTER_STATUS_AVAILABLE:
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ case FSF_ACCESS_DENIED:
+ wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+ break;
+ case FSF_PORT_ALREADY_OPEN:
+ case FSF_GOOD:
+ wka_port->handle = header->port_handle;
+ wka_port->status = ZFCP_WKA_PORT_ONLINE;
+ }
+out:
+ wake_up(&wka_port->completion_wq);
+}
+
+/**
+ * zfcp_fsf_open_wka_port - create and send open wka-port request
+ * @wka_port: pointer to struct zfcp_wka_port
+ * Returns: 0 on success, error otherwise
+ */
+int zfcp_fsf_open_wka_port(struct zfcp_wka_port *wka_port)
+{
+ struct qdio_buffer_element *sbale;
+ struct zfcp_adapter *adapter = wka_port->adapter;
+ struct zfcp_fsf_req *req;
+ int retval = -EIO;
+
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
+
+ req = zfcp_fsf_req_create(adapter,
+ FSF_QTCB_OPEN_PORT_WITH_DID,
+ ZFCP_REQ_AUTO_CLEANUP,
+ adapter->pool.fsf_req_erp);
+ if (unlikely(IS_ERR(req))) {
+ retval = PTR_ERR(req);
+ goto out;
+ }
+
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ req->handler = zfcp_fsf_open_wka_port_handler;
+ req->qtcb->bottom.support.d_id = wka_port->d_id;
+ req->data = wka_port;
+
+ zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+ retval = zfcp_fsf_req_send(req);
+ if (retval)
+ zfcp_fsf_req_free(req);
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
return retval;
}
-/*
- * function: zfcp_fsf_close_physical_port_handler
- *
- * purpose: is called for finished Close Physical Port FSF command
- *
- * returns:
+static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req)
+{
+ struct zfcp_wka_port *wka_port = req->data;
+
+ 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, 84, req);
+ }
+
+ wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+ wake_up(&wka_port->completion_wq);
+}
+
+/**
+ * zfcp_fsf_close_wka_port - create and send close wka port request
+ * @erp_action: pointer to struct zfcp_erp_action
+ * Returns: 0 on success, error otherwise
*/
-static int
-zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req)
+int zfcp_fsf_close_wka_port(struct zfcp_wka_port *wka_port)
{
- int retval = -EINVAL;
- struct zfcp_port *port;
- struct zfcp_unit *unit;
- struct fsf_qtcb_header *header;
- u16 subtable, rule, counter;
+ struct qdio_buffer_element *sbale;
+ struct zfcp_adapter *adapter = wka_port->adapter;
+ struct zfcp_fsf_req *req;
+ int retval = -EIO;
- port = (struct zfcp_port *) fsf_req->data;
- header = &fsf_req->qtcb->header;
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- /* don't change port status in our bookkeeping */
- goto skip_fsfstatus;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_CLOSE_PORT,
+ ZFCP_REQ_AUTO_CLEANUP,
+ adapter->pool.fsf_req_erp);
+ if (unlikely(IS_ERR(req))) {
+ retval = PTR_ERR(req);
+ goto out;
}
- /* evaluate FSF status in QTCB */
- switch (header->fsf_status) {
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ req->handler = zfcp_fsf_close_wka_port_handler;
+ req->data = wka_port;
+ req->qtcb->header.port_handle = wka_port->handle;
+
+ zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+ retval = zfcp_fsf_req_send(req);
+ if (retval)
+ zfcp_fsf_req_free(req);
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
+ return retval;
+}
+
+static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
+{
+ struct zfcp_port *port = req->data;
+ struct fsf_qtcb_header *header = &req->qtcb->header;
+ struct zfcp_unit *unit;
+
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ goto skip_fsfstatus;
+ switch (header->fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID:
- ZFCP_LOG_INFO("Temporary port identifier 0x%x invalid"
- "(adapter %s, port 0x%016Lx). "
- "This may happen occasionally.\n",
- port->handle,
- zfcp_get_busid_by_port(port),
- port->wwpn);
- ZFCP_LOG_DEBUG("status qualifier:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &header->fsf_status_qual,
- sizeof (union fsf_status_qual));
- zfcp_erp_adapter_reopen(port->adapter, 0, 108, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_erp_adapter_reopen(port->adapter, 0, 108, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
case FSF_ACCESS_DENIED:
- ZFCP_LOG_NORMAL("Access denied, cannot close "
- "physical port 0x%016Lx on adapter %s\n",
- port->wwpn, zfcp_get_busid_by_port(port));
- for (counter = 0; counter < 2; counter++) {
- subtable = header->fsf_status_qual.halfword[counter * 2];
- rule = header->fsf_status_qual.halfword[counter * 2 + 1];
- switch (subtable) {
- case FSF_SQ_CFDC_SUBTABLE_OS:
- case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
- case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
- case FSF_SQ_CFDC_SUBTABLE_LUN:
- ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
- zfcp_act_subtable_type[subtable], rule);
- break;
- }
- }
- zfcp_erp_port_access_denied(port, 58, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_fsf_access_denied_port(req, port);
break;
-
case FSF_PORT_BOXED:
- ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter "
- "%s needs to be reopened but it was attempted "
- "to close it physically.\n",
- port->wwpn,
- zfcp_get_busid_by_port(port));
- zfcp_erp_port_boxed(port, 50, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
-
+ zfcp_erp_port_boxed(port, 50, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+ ZFCP_STATUS_FSFREQ_RETRY;
/* can't use generic zfcp_erp_modify_port_status because
* ZFCP_STATUS_COMMON_OPEN must not be reset for the port */
atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
@@ -2847,154 +1717,88 @@ zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req)
atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
&unit->status);
break;
-
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (header->fsf_status_qual.word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
- /* This will now be escalated by ERP */
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
+ /* fall through */
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- /* ERP strategy will escalate */
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
- default:
- ZFCP_LOG_NORMAL
- ("bug: Wrong status qualifier 0x%x arrived.\n",
- header->fsf_status_qual.word[0]);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
break;
-
case FSF_GOOD:
- ZFCP_LOG_DEBUG("Remote port 0x%016Lx via adapter %s "
- "physically closed, port handle 0x%x\n",
- port->wwpn,
- zfcp_get_busid_by_port(port), port->handle);
/* can't use generic zfcp_erp_modify_port_status because
* ZFCP_STATUS_COMMON_OPEN must not be reset for the port
*/
atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
list_for_each_entry(unit, &port->unit_list_head, list)
- atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
- retval = 0;
- break;
-
- default:
- ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
- "(debug info 0x%x)\n",
- header->fsf_status);
+ atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
+ &unit->status);
break;
}
-
- skip_fsfstatus:
+skip_fsfstatus:
atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, &port->status);
- return retval;
}
-/*
- * function: zfcp_fsf_open_unit
- *
- * purpose:
- *
- * returns:
- *
- * assumptions: This routine does not check whether the associated
- * remote port has already been opened. This should be
- * done by calling routines. Otherwise some status
- * may be presented by FSF
+/**
+ * zfcp_fsf_close_physical_port - close physical port
+ * @erp_action: pointer to struct zfcp_erp_action
+ * Returns: 0 on success
*/
-int
-zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
+int zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req;
- unsigned long lock_flags;
- int retval = 0;
-
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(erp_action->adapter,
- FSF_QTCB_OPEN_LUN,
- ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
- erp_action->adapter->pool.fsf_req_erp,
- &lock_flags, &fsf_req);
- if (retval < 0) {
- ZFCP_LOG_INFO("error: Could not create open unit request for "
- "unit 0x%016Lx on port 0x%016Lx on adapter %s.\n",
- erp_action->unit->fcp_lun,
- erp_action->unit->port->wwpn,
- zfcp_get_busid_by_adapter(erp_action->adapter));
+ struct qdio_buffer_element *sbale;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_fsf_req *req;
+ int retval = -EIO;
+
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
+
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_CLOSE_PHYSICAL_PORT,
+ ZFCP_REQ_AUTO_CLEANUP,
+ adapter->pool.fsf_req_erp);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
- sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
- sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ req->data = erp_action->port;
+ req->qtcb->header.port_handle = erp_action->port->handle;
+ req->erp_action = erp_action;
+ req->handler = zfcp_fsf_close_physical_port_handler;
+ erp_action->fsf_req = req;
+ atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING,
+ &erp_action->port->status);
- fsf_req->qtcb->header.port_handle = erp_action->port->handle;
- fsf_req->qtcb->bottom.support.fcp_lun = erp_action->unit->fcp_lun;
- if (!(erp_action->adapter->connection_features & FSF_FEATURE_NPIV_MODE))
- fsf_req->qtcb->bottom.support.option =
- FSF_OPEN_LUN_SUPPRESS_BOXING;
- atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status);
- fsf_req->data = (unsigned long) erp_action->unit;
- fsf_req->erp_action = erp_action;
- erp_action->fsf_req = fsf_req;
-
- zfcp_erp_start_timer(fsf_req);
- retval = zfcp_fsf_req_send(erp_action->fsf_req);
+ zfcp_fsf_start_erp_timer(req);
+ retval = zfcp_fsf_req_send(req);
if (retval) {
- ZFCP_LOG_INFO("error: Could not send an open unit request "
- "on the adapter %s, port 0x%016Lx for "
- "unit 0x%016Lx\n",
- zfcp_get_busid_by_adapter(erp_action->adapter),
- erp_action->port->wwpn,
- erp_action->unit->fcp_lun);
- zfcp_fsf_req_free(fsf_req);
+ zfcp_fsf_req_free(req);
erp_action->fsf_req = NULL;
- goto out;
}
-
- ZFCP_LOG_TRACE("Open LUN request initiated (adapter %s, "
- "port 0x%016Lx, unit 0x%016Lx)\n",
- zfcp_get_busid_by_adapter(erp_action->adapter),
- erp_action->port->wwpn, erp_action->unit->fcp_lun);
- out:
- write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
- lock_flags);
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
return retval;
}
-/*
- * function: zfcp_fsf_open_unit_handler
- *
- * purpose: is called for finished Open LUN command
- *
- * returns:
- */
-static int
-zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
{
- int retval = -EINVAL;
- struct zfcp_adapter *adapter;
- struct zfcp_unit *unit;
- struct fsf_qtcb_header *header;
- struct fsf_qtcb_bottom_support *bottom;
- struct fsf_queue_designator *queue_designator;
- u16 subtable, rule, counter;
+ struct zfcp_adapter *adapter = req->adapter;
+ struct zfcp_unit *unit = req->data;
+ struct fsf_qtcb_header *header = &req->qtcb->header;
+ struct fsf_qtcb_bottom_support *bottom = &req->qtcb->bottom.support;
+ struct fsf_queue_designator *queue_designator =
+ &header->fsf_status_qual.fsf_queue_designator;
int exclusive, readwrite;
- unit = (struct zfcp_unit *) fsf_req->data;
-
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- /* don't change unit status in our bookkeeping */
- goto skip_fsfstatus;
- }
-
- adapter = fsf_req->adapter;
- header = &fsf_req->qtcb->header;
- bottom = &fsf_req->qtcb->bottom.support;
- queue_designator = &header->fsf_status_qual.fsf_queue_designator;
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return;
atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
ZFCP_STATUS_COMMON_ACCESS_BOXED |
@@ -3002,155 +1806,64 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req)
ZFCP_STATUS_UNIT_READONLY,
&unit->status);
- /* evaluate FSF status in QTCB */
switch (header->fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID:
- ZFCP_LOG_INFO("Temporary port identifier 0x%x "
- "for port 0x%016Lx on adapter %s invalid "
- "This may happen occasionally\n",
- unit->port->handle,
- unit->port->wwpn, zfcp_get_busid_by_unit(unit));
- ZFCP_LOG_DEBUG("status qualifier:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &header->fsf_status_qual,
- sizeof (union fsf_status_qual));
- zfcp_erp_adapter_reopen(unit->port->adapter, 0, 109, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
+ zfcp_erp_adapter_reopen(unit->port->adapter, 0, 109, req);
+ /* fall through */
case FSF_LUN_ALREADY_OPEN:
- ZFCP_LOG_NORMAL("bug: Attempted to open unit 0x%016Lx on "
- "remote port 0x%016Lx on adapter %s twice.\n",
- unit->fcp_lun,
- unit->port->wwpn, zfcp_get_busid_by_unit(unit));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
case FSF_ACCESS_DENIED:
- ZFCP_LOG_NORMAL("Access denied, cannot open unit 0x%016Lx on "
- "remote port 0x%016Lx on adapter %s\n",
- unit->fcp_lun, unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- for (counter = 0; counter < 2; counter++) {
- subtable = header->fsf_status_qual.halfword[counter * 2];
- rule = header->fsf_status_qual.halfword[counter * 2 + 1];
- switch (subtable) {
- case FSF_SQ_CFDC_SUBTABLE_OS:
- case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
- case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
- case FSF_SQ_CFDC_SUBTABLE_LUN:
- ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
- zfcp_act_subtable_type[subtable], rule);
- break;
- }
- }
- zfcp_erp_unit_access_denied(unit, 59, fsf_req);
+ zfcp_fsf_access_denied_unit(req, unit);
atomic_clear_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status);
- atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status);
break;
-
case FSF_PORT_BOXED:
- ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter %s "
- "needs to be reopened\n",
- unit->port->wwpn, zfcp_get_busid_by_unit(unit));
- zfcp_erp_port_boxed(unit->port, 51, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
+ zfcp_erp_port_boxed(unit->port, 51, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+ ZFCP_STATUS_FSFREQ_RETRY;
break;
-
case FSF_LUN_SHARING_VIOLATION:
- if (header->fsf_status_qual.word[0] != 0) {
- ZFCP_LOG_NORMAL("FCP-LUN 0x%Lx at the remote port "
- "with WWPN 0x%Lx "
- "connected to the adapter %s "
- "is already in use in LPAR%d, CSS%d\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit),
- queue_designator->hla,
- queue_designator->cssid);
- } else {
- subtable = header->fsf_status_qual.halfword[4];
- rule = header->fsf_status_qual.halfword[5];
- switch (subtable) {
- case FSF_SQ_CFDC_SUBTABLE_OS:
- case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
- case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
- case FSF_SQ_CFDC_SUBTABLE_LUN:
- ZFCP_LOG_NORMAL("Access to FCP-LUN 0x%Lx at the "
- "remote port with WWPN 0x%Lx "
- "connected to the adapter %s "
- "is denied (%s rule %d)\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit),
- zfcp_act_subtable_type[subtable],
- rule);
- break;
- }
- }
- ZFCP_LOG_DEBUG("status qualifier:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &header->fsf_status_qual,
- sizeof (union fsf_status_qual));
- zfcp_erp_unit_access_denied(unit, 60, fsf_req);
+ if (header->fsf_status_qual.word[0])
+ dev_warn(&adapter->ccw_device->dev,
+ "LUN 0x%Lx on port 0x%Lx is already in "
+ "use by CSS%d, MIF Image ID %x\n",
+ (unsigned long long)unit->fcp_lun,
+ (unsigned long long)unit->port->wwpn,
+ queue_designator->cssid,
+ queue_designator->hla);
+ else
+ zfcp_act_eval_err(adapter,
+ header->fsf_status_qual.word[2]);
+ zfcp_erp_unit_access_denied(unit, 60, req);
atomic_clear_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status);
atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
case FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED:
- ZFCP_LOG_INFO("error: The adapter ran out of resources. "
- "There is no handle (temporary port identifier) "
- "available for unit 0x%016Lx on port 0x%016Lx "
- "on adapter %s\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- zfcp_erp_unit_failed(unit, 34, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ dev_warn(&adapter->ccw_device->dev,
+ "No handle is available for LUN "
+ "0x%016Lx on port 0x%016Lx\n",
+ (unsigned long long)unit->fcp_lun,
+ (unsigned long long)unit->port->wwpn);
+ zfcp_erp_unit_failed(unit, 34, req);
+ /* fall through */
+ case FSF_INVALID_COMMAND_OPTION:
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (header->fsf_status_qual.word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
- /* Re-establish link to port */
zfcp_test_link(unit->port);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
+ /* fall through */
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- /* ERP strategy will escalate */
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
- default:
- ZFCP_LOG_NORMAL
- ("bug: Wrong status qualifier 0x%x arrived.\n",
- header->fsf_status_qual.word[0]);
}
break;
- case FSF_INVALID_COMMAND_OPTION:
- ZFCP_LOG_NORMAL(
- "Invalid option 0x%x has been specified "
- "in QTCB bottom sent to the adapter %s\n",
- bottom->option,
- zfcp_get_busid_by_adapter(adapter));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval = -EINVAL;
- break;
-
case FSF_GOOD:
- /* save LUN handle assigned by FSF */
unit->handle = header->lun_handle;
- ZFCP_LOG_TRACE("unit 0x%016Lx on remote port 0x%016Lx on "
- "adapter %s opened, port handle 0x%x\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit),
- unit->handle);
- /* mark unit as open */
atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
if (!(adapter->connection_features & FSF_FEATURE_NPIV_MODE) &&
@@ -3168,1528 +1881,638 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req)
if (!readwrite) {
atomic_set_mask(ZFCP_STATUS_UNIT_READONLY,
&unit->status);
- ZFCP_LOG_NORMAL("read-only access for unit "
- "(adapter %s, wwpn=0x%016Lx, "
- "fcp_lun=0x%016Lx)\n",
- zfcp_get_busid_by_unit(unit),
- unit->port->wwpn,
- unit->fcp_lun);
+ dev_info(&adapter->ccw_device->dev,
+ "SCSI device at LUN 0x%016Lx on port "
+ "0x%016Lx opened read-only\n",
+ (unsigned long long)unit->fcp_lun,
+ (unsigned long long)unit->port->wwpn);
}
if (exclusive && !readwrite) {
- ZFCP_LOG_NORMAL("exclusive access of read-only "
- "unit not supported\n");
- zfcp_erp_unit_failed(unit, 35, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- zfcp_erp_unit_shutdown(unit, 0, 80, fsf_req);
+ dev_err(&adapter->ccw_device->dev,
+ "Exclusive read-only access not "
+ "supported (unit 0x%016Lx, "
+ "port 0x%016Lx)\n",
+ (unsigned long long)unit->fcp_lun,
+ (unsigned long long)unit->port->wwpn);
+ zfcp_erp_unit_failed(unit, 35, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_erp_unit_shutdown(unit, 0, 80, req);
} else if (!exclusive && readwrite) {
- ZFCP_LOG_NORMAL("shared access of read-write "
- "unit not supported\n");
- zfcp_erp_unit_failed(unit, 36, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- zfcp_erp_unit_shutdown(unit, 0, 81, fsf_req);
+ dev_err(&adapter->ccw_device->dev,
+ "Shared read-write access not "
+ "supported (unit 0x%016Lx, port "
+ "0x%016Lx\n)",
+ (unsigned long long)unit->fcp_lun,
+ (unsigned long long)unit->port->wwpn);
+ zfcp_erp_unit_failed(unit, 36, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_erp_unit_shutdown(unit, 0, 81, req);
}
}
-
- retval = 0;
- break;
-
- default:
- ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
- "(debug info 0x%x)\n",
- header->fsf_status);
break;
}
-
- skip_fsfstatus:
- atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &unit->status);
- return retval;
}
-/*
- * function: zfcp_fsf_close_unit
- *
- * purpose:
- *
- * returns: address of fsf_req - request successfully initiated
- * NULL -
- *
- * assumptions: This routine does not check whether the associated
- * remote port/lun has already been opened. This should be
- * done by calling routines. Otherwise some status
- * may be presented by FSF
+/**
+ * zfcp_fsf_open_unit - open unit
+ * @erp_action: pointer to struct zfcp_erp_action
+ * Returns: 0 on success, error otherwise
*/
-int
-zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
+int zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req;
- unsigned long lock_flags;
- int retval = 0;
-
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(erp_action->adapter,
- FSF_QTCB_CLOSE_LUN,
- ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
- erp_action->adapter->pool.fsf_req_erp,
- &lock_flags, &fsf_req);
- if (retval < 0) {
- ZFCP_LOG_INFO("error: Could not create close unit request for "
- "unit 0x%016Lx on port 0x%016Lx on adapter %s.\n",
- erp_action->unit->fcp_lun,
- erp_action->port->wwpn,
- zfcp_get_busid_by_adapter(erp_action->adapter));
+ struct qdio_buffer_element *sbale;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_fsf_req *req;
+ int retval = -EIO;
+
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
+
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_OPEN_LUN,
+ ZFCP_REQ_AUTO_CLEANUP,
+ adapter->pool.fsf_req_erp);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
- fsf_req->qtcb->header.port_handle = erp_action->port->handle;
- fsf_req->qtcb->header.lun_handle = erp_action->unit->handle;
- atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->unit->status);
- fsf_req->data = (unsigned long) erp_action->unit;
- fsf_req->erp_action = erp_action;
- erp_action->fsf_req = fsf_req;
+ req->qtcb->header.port_handle = erp_action->port->handle;
+ req->qtcb->bottom.support.fcp_lun = erp_action->unit->fcp_lun;
+ req->handler = zfcp_fsf_open_unit_handler;
+ req->data = erp_action->unit;
+ req->erp_action = erp_action;
+ erp_action->fsf_req = req;
- zfcp_erp_start_timer(fsf_req);
- retval = zfcp_fsf_req_send(erp_action->fsf_req);
+ if (!(adapter->connection_features & FSF_FEATURE_NPIV_MODE))
+ req->qtcb->bottom.support.option = FSF_OPEN_LUN_SUPPRESS_BOXING;
+
+ zfcp_fsf_start_erp_timer(req);
+ retval = zfcp_fsf_req_send(req);
if (retval) {
- ZFCP_LOG_INFO("error: Could not send a close unit request for "
- "unit 0x%016Lx on port 0x%016Lx onadapter %s.\n",
- erp_action->unit->fcp_lun,
- erp_action->port->wwpn,
- zfcp_get_busid_by_adapter(erp_action->adapter));
- zfcp_fsf_req_free(fsf_req);
+ zfcp_fsf_req_free(req);
erp_action->fsf_req = NULL;
- goto out;
}
-
- ZFCP_LOG_TRACE("Close LUN request initiated (adapter %s, "
- "port 0x%016Lx, unit 0x%016Lx)\n",
- zfcp_get_busid_by_adapter(erp_action->adapter),
- erp_action->port->wwpn, erp_action->unit->fcp_lun);
- out:
- write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
- lock_flags);
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
return retval;
}
-/*
- * function: zfcp_fsf_close_unit_handler
- *
- * purpose: is called for finished Close LUN FSF command
- *
- * returns:
- */
-static int
-zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *req)
{
- int retval = -EINVAL;
- struct zfcp_unit *unit;
+ struct zfcp_unit *unit = req->data;
- unit = (struct zfcp_unit *) fsf_req->data;
-
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- /* don't change unit status in our bookkeeping */
- goto skip_fsfstatus;
- }
-
- /* evaluate FSF status in QTCB */
- switch (fsf_req->qtcb->header.fsf_status) {
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return;
+ switch (req->qtcb->header.fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID:
- ZFCP_LOG_INFO("Temporary port identifier 0x%x for port "
- "0x%016Lx on adapter %s invalid. This may "
- "happen in rare circumstances\n",
- unit->port->handle,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- ZFCP_LOG_DEBUG("status qualifier:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &fsf_req->qtcb->header.fsf_status_qual,
- sizeof (union fsf_status_qual));
- zfcp_erp_adapter_reopen(unit->port->adapter, 0, 110, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_erp_adapter_reopen(unit->port->adapter, 0, 110, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
case FSF_LUN_HANDLE_NOT_VALID:
- ZFCP_LOG_INFO("Temporary LUN identifier 0x%x of unit "
- "0x%016Lx on port 0x%016Lx on adapter %s is "
- "invalid. This may happen occasionally.\n",
- unit->handle,
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- ZFCP_LOG_DEBUG("Status qualifier data:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &fsf_req->qtcb->header.fsf_status_qual,
- sizeof (union fsf_status_qual));
- zfcp_erp_port_reopen(unit->port, 0, 111, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ zfcp_erp_port_reopen(unit->port, 0, 111, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
case FSF_PORT_BOXED:
- ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter %s "
- "needs to be reopened\n",
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- zfcp_erp_port_boxed(unit->port, 52, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
+ zfcp_erp_port_boxed(unit->port, 52, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+ ZFCP_STATUS_FSFREQ_RETRY;
break;
-
case FSF_ADAPTER_STATUS_AVAILABLE:
- switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+ switch (req->qtcb->header.fsf_status_qual.word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
- /* re-establish link to port */
zfcp_test_link(unit->port);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
+ /* fall through */
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- /* ERP strategy will escalate */
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
- default:
- ZFCP_LOG_NORMAL
- ("bug: Wrong status qualifier 0x%x arrived.\n",
- fsf_req->qtcb->header.fsf_status_qual.word[0]);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
break;
-
case FSF_GOOD:
- ZFCP_LOG_TRACE("unit 0x%016Lx on port 0x%016Lx on adapter %s "
- "closed, port handle 0x%x\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit),
- unit->handle);
- /* mark unit as closed */
atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
- retval = 0;
- break;
-
- default:
- ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
- "(debug info 0x%x)\n",
- fsf_req->qtcb->header.fsf_status);
break;
}
-
- skip_fsfstatus:
- atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &unit->status);
- return retval;
}
/**
- * zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command)
- * @adapter: adapter where scsi command is issued
- * @unit: unit where command is sent to
- * @scsi_cmnd: scsi command to be sent
- * @timer: timer to be started when request is initiated
- * @req_flags: flags for fsf_request
+ * zfcp_fsf_close_unit - close zfcp unit
+ * @erp_action: pointer to struct zfcp_unit
+ * Returns: 0 on success, error otherwise
*/
-int
-zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
- struct zfcp_unit *unit,
- struct scsi_cmnd * scsi_cmnd,
- int use_timer, int req_flags)
+int zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
{
- struct zfcp_fsf_req *fsf_req = NULL;
- struct fcp_cmnd_iu *fcp_cmnd_iu;
- unsigned int sbtype;
- unsigned long lock_flags;
- int real_bytes = 0;
- int retval = 0;
- int mask;
-
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
- adapter->pool.fsf_req_scsi,
- &lock_flags, &fsf_req);
- if (unlikely(retval < 0)) {
- ZFCP_LOG_DEBUG("error: Could not create FCP command request "
- "for unit 0x%016Lx on port 0x%016Lx on "
- "adapter %s\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_adapter(adapter));
- goto failed_req_create;
- }
-
- if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &unit->status))) {
- retval = -EBUSY;
- goto unit_blocked;
- }
-
- zfcp_unit_get(unit);
- fsf_req->unit = unit;
-
- /* associate FSF request with SCSI request (for look up on abort) */
- scsi_cmnd->host_scribble = (unsigned char *) fsf_req->req_id;
-
- /* associate SCSI command with FSF request */
- fsf_req->data = (unsigned long) scsi_cmnd;
-
- /* set handles of unit and its parent port in QTCB */
- fsf_req->qtcb->header.lun_handle = unit->handle;
- fsf_req->qtcb->header.port_handle = unit->port->handle;
-
- /* FSF does not define the structure of the FCP_CMND IU */
- fcp_cmnd_iu = (struct fcp_cmnd_iu *)
- &(fsf_req->qtcb->bottom.io.fcp_cmnd);
-
- /*
- * set depending on data direction:
- * data direction bits in SBALE (SB Type)
- * data direction bits in QTCB
- * data direction bits in FCP_CMND IU
- */
- switch (scsi_cmnd->sc_data_direction) {
- case DMA_NONE:
- fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
- /*
- * FIXME(qdio):
- * what is the correct type for commands
- * without 'real' data buffers?
- */
- sbtype = SBAL_FLAGS0_TYPE_READ;
- break;
- case DMA_FROM_DEVICE:
- fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
- sbtype = SBAL_FLAGS0_TYPE_READ;
- fcp_cmnd_iu->rddata = 1;
- break;
- case DMA_TO_DEVICE:
- fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE;
- sbtype = SBAL_FLAGS0_TYPE_WRITE;
- fcp_cmnd_iu->wddata = 1;
- break;
- case DMA_BIDIRECTIONAL:
- default:
- /*
- * dummy, catch this condition earlier
- * in zfcp_scsi_queuecommand
- */
- goto failed_scsi_cmnd;
- }
-
- /* set FC service class in QTCB (3 per default) */
- fsf_req->qtcb->bottom.io.service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT;
-
- /* set FCP_LUN in FCP_CMND IU in QTCB */
- fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
-
- mask = ZFCP_STATUS_UNIT_READONLY | ZFCP_STATUS_UNIT_SHARED;
-
- /* set task attributes in FCP_CMND IU in QTCB */
- if (likely((scsi_cmnd->device->simple_tags) ||
- (atomic_test_mask(mask, &unit->status))))
- fcp_cmnd_iu->task_attribute = SIMPLE_Q;
- else
- fcp_cmnd_iu->task_attribute = UNTAGGED;
-
- /* set additional length of FCP_CDB in FCP_CMND IU in QTCB, if needed */
- if (unlikely(scsi_cmnd->cmd_len > FCP_CDB_LENGTH)) {
- fcp_cmnd_iu->add_fcp_cdb_length
- = (scsi_cmnd->cmd_len - FCP_CDB_LENGTH) >> 2;
- ZFCP_LOG_TRACE("SCSI CDB length is 0x%x, "
- "additional FCP_CDB length is 0x%x "
- "(shifted right 2 bits)\n",
- scsi_cmnd->cmd_len,
- fcp_cmnd_iu->add_fcp_cdb_length);
- }
- /*
- * copy SCSI CDB (including additional length, if any) to
- * FCP_CDB in FCP_CMND IU in QTCB
- */
- memcpy(fcp_cmnd_iu->fcp_cdb, scsi_cmnd->cmnd, scsi_cmnd->cmd_len);
-
- /* FCP CMND IU length in QTCB */
- fsf_req->qtcb->bottom.io.fcp_cmnd_length =
- sizeof (struct fcp_cmnd_iu) +
- fcp_cmnd_iu->add_fcp_cdb_length + sizeof (fcp_dl_t);
+ struct qdio_buffer_element *sbale;
+ struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_fsf_req *req;
+ int retval = -EIO;
- /* generate SBALEs from data buffer */
- real_bytes = zfcp_qdio_sbals_from_scsicmnd(fsf_req, sbtype, scsi_cmnd);
- if (unlikely(real_bytes < 0)) {
- if (fsf_req->sbal_number < ZFCP_MAX_SBALS_PER_REQ) {
- ZFCP_LOG_DEBUG(
- "Data did not fit into available buffer(s), "
- "waiting for more...\n");
- retval = -EIO;
- } else {
- ZFCP_LOG_NORMAL("error: No truncation implemented but "
- "required. Shutting down unit "
- "(adapter %s, port 0x%016Lx, "
- "unit 0x%016Lx)\n",
- zfcp_get_busid_by_unit(unit),
- unit->port->wwpn,
- unit->fcp_lun);
- zfcp_erp_unit_shutdown(unit, 0, 131, fsf_req);
- retval = -EINVAL;
- }
- goto no_fit;
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_CLOSE_LUN,
+ ZFCP_REQ_AUTO_CLEANUP,
+ adapter->pool.fsf_req_erp);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
+ goto out;
}
- /* set length of FCP data length in FCP_CMND IU in QTCB */
- zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes);
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
- ZFCP_LOG_DEBUG("Sending SCSI command:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) scsi_cmnd->cmnd, scsi_cmnd->cmd_len);
+ req->qtcb->header.port_handle = erp_action->port->handle;
+ req->qtcb->header.lun_handle = erp_action->unit->handle;
+ req->handler = zfcp_fsf_close_unit_handler;
+ req->data = erp_action->unit;
+ req->erp_action = erp_action;
+ erp_action->fsf_req = req;
- if (use_timer)
- zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
-
- retval = zfcp_fsf_req_send(fsf_req);
- if (unlikely(retval < 0)) {
- ZFCP_LOG_INFO("error: Could not send FCP command request "
- "on adapter %s, port 0x%016Lx, unit 0x%016Lx\n",
- zfcp_get_busid_by_adapter(adapter),
- unit->port->wwpn,
- unit->fcp_lun);
- goto send_failed;
+ zfcp_fsf_start_erp_timer(req);
+ retval = zfcp_fsf_req_send(req);
+ if (retval) {
+ zfcp_fsf_req_free(req);
+ erp_action->fsf_req = NULL;
}
-
- ZFCP_LOG_TRACE("Send FCP Command initiated (adapter %s, "
- "port 0x%016Lx, unit 0x%016Lx)\n",
- zfcp_get_busid_by_adapter(adapter),
- unit->port->wwpn,
- unit->fcp_lun);
- goto success;
-
- send_failed:
- no_fit:
- failed_scsi_cmnd:
- zfcp_unit_put(unit);
- unit_blocked:
- zfcp_fsf_req_free(fsf_req);
- fsf_req = NULL;
- scsi_cmnd->host_scribble = NULL;
- success:
- failed_req_create:
- write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
return retval;
}
-struct zfcp_fsf_req *
-zfcp_fsf_send_fcp_command_task_management(struct zfcp_adapter *adapter,
- struct zfcp_unit *unit,
- u8 tm_flags, int req_flags)
+static void zfcp_fsf_update_lat(struct fsf_latency_record *lat_rec, u32 lat)
{
- struct zfcp_fsf_req *fsf_req = NULL;
- int retval = 0;
- struct fcp_cmnd_iu *fcp_cmnd_iu;
- unsigned long lock_flags;
- volatile struct qdio_buffer_element *sbale;
-
- /* setup new FSF request */
- retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
- adapter->pool.fsf_req_scsi,
- &lock_flags, &fsf_req);
- if (retval < 0) {
- ZFCP_LOG_INFO("error: Could not create FCP command (task "
- "management) request for adapter %s, port "
- " 0x%016Lx, unit 0x%016Lx.\n",
- zfcp_get_busid_by_adapter(adapter),
- unit->port->wwpn, unit->fcp_lun);
- goto out;
- }
-
- if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &unit->status)))
- goto unit_blocked;
-
- /*
- * Used to decide on proper handler in the return path,
- * could be either zfcp_fsf_send_fcp_command_task_handler or
- * zfcp_fsf_send_fcp_command_task_management_handler */
-
- fsf_req->status |= ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT;
-
- /*
- * hold a pointer to the unit being target of this
- * task management request
- */
- fsf_req->data = (unsigned long) unit;
-
- /* set FSF related fields in QTCB */
- fsf_req->qtcb->header.lun_handle = unit->handle;
- fsf_req->qtcb->header.port_handle = unit->port->handle;
- fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
- fsf_req->qtcb->bottom.io.service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT;
- fsf_req->qtcb->bottom.io.fcp_cmnd_length =
- sizeof (struct fcp_cmnd_iu) + sizeof (fcp_dl_t);
-
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
- sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
- sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-
- /* set FCP related fields in FCP_CMND IU in QTCB */
- fcp_cmnd_iu = (struct fcp_cmnd_iu *)
- &(fsf_req->qtcb->bottom.io.fcp_cmnd);
- fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
- fcp_cmnd_iu->task_management_flags = tm_flags;
-
- zfcp_fsf_start_timer(fsf_req, ZFCP_SCSI_ER_TIMEOUT);
- retval = zfcp_fsf_req_send(fsf_req);
- if (!retval)
- goto out;
-
- unit_blocked:
- zfcp_fsf_req_free(fsf_req);
- fsf_req = NULL;
-
- out:
- write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
- return fsf_req;
+ lat_rec->sum += lat;
+ lat_rec->min = min(lat_rec->min, lat);
+ lat_rec->max = max(lat_rec->max, lat);
}
-/*
- * function: zfcp_fsf_send_fcp_command_handler
- *
- * purpose: is called for finished Send FCP Command
- *
- * returns:
- */
-static int
-zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_req_latency(struct zfcp_fsf_req *req)
{
- int retval = -EINVAL;
- struct zfcp_unit *unit;
- struct fsf_qtcb_header *header;
- u16 subtable, rule, counter;
-
- header = &fsf_req->qtcb->header;
-
- if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT))
- unit = (struct zfcp_unit *) fsf_req->data;
- else
- unit = fsf_req->unit;
-
- if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
- /* go directly to calls of special handlers */
- goto skip_fsfstatus;
- }
-
- /* evaluate FSF status in QTCB */
- switch (header->fsf_status) {
-
- case FSF_PORT_HANDLE_NOT_VALID:
- ZFCP_LOG_INFO("Temporary port identifier 0x%x for port "
- "0x%016Lx on adapter %s invalid\n",
- unit->port->handle,
- unit->port->wwpn, zfcp_get_busid_by_unit(unit));
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &header->fsf_status_qual,
- sizeof (union fsf_status_qual));
- zfcp_erp_adapter_reopen(unit->port->adapter, 0, 112, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
- case FSF_LUN_HANDLE_NOT_VALID:
- ZFCP_LOG_INFO("Temporary LUN identifier 0x%x for unit "
- "0x%016Lx on port 0x%016Lx on adapter %s is "
- "invalid. This may happen occasionally.\n",
- unit->handle,
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- ZFCP_LOG_NORMAL("Status qualifier data:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
- (char *) &header->fsf_status_qual,
- sizeof (union fsf_status_qual));
- zfcp_erp_port_reopen(unit->port, 0, 113, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
- case FSF_HANDLE_MISMATCH:
- ZFCP_LOG_NORMAL("bug: The port handle 0x%x has changed "
- "unexpectedly. (adapter %s, port 0x%016Lx, "
- "unit 0x%016Lx)\n",
- unit->port->handle,
- zfcp_get_busid_by_unit(unit),
- unit->port->wwpn,
- unit->fcp_lun);
- ZFCP_LOG_NORMAL("status qualifier:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
- (char *) &header->fsf_status_qual,
- sizeof (union fsf_status_qual));
- zfcp_erp_adapter_reopen(unit->port->adapter, 0, 114, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
- case FSF_SERVICE_CLASS_NOT_SUPPORTED:
- ZFCP_LOG_INFO("error: adapter %s does not support fc "
- "class %d.\n",
- zfcp_get_busid_by_unit(unit),
- ZFCP_FC_SERVICE_CLASS_DEFAULT);
- /* stop operation for this adapter */
- zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 132, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
- case FSF_FCPLUN_NOT_VALID:
- ZFCP_LOG_NORMAL("bug: unit 0x%016Lx on port 0x%016Lx on "
- "adapter %s does not have correct unit "
- "handle 0x%x\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit),
- unit->handle);
- ZFCP_LOG_DEBUG("status qualifier:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &header->fsf_status_qual,
- sizeof (union fsf_status_qual));
- zfcp_erp_port_reopen(unit->port, 0, 115, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
- case FSF_ACCESS_DENIED:
- ZFCP_LOG_NORMAL("Access denied, cannot send FCP command to "
- "unit 0x%016Lx on port 0x%016Lx on "
- "adapter %s\n", unit->fcp_lun, unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- for (counter = 0; counter < 2; counter++) {
- subtable = header->fsf_status_qual.halfword[counter * 2];
- rule = header->fsf_status_qual.halfword[counter * 2 + 1];
- switch (subtable) {
- case FSF_SQ_CFDC_SUBTABLE_OS:
- case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
- case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
- case FSF_SQ_CFDC_SUBTABLE_LUN:
- ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
- zfcp_act_subtable_type[subtable], rule);
- break;
- }
- }
- zfcp_erp_unit_access_denied(unit, 61, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
- case FSF_DIRECTION_INDICATOR_NOT_VALID:
- ZFCP_LOG_INFO("bug: Invalid data direction given for unit "
- "0x%016Lx on port 0x%016Lx on adapter %s "
- "(debug info %d)\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit),
- fsf_req->qtcb->bottom.io.data_direction);
- /* stop operation for this adapter */
- zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 133, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
-
- case FSF_CMND_LENGTH_NOT_VALID:
- ZFCP_LOG_NORMAL
- ("bug: An invalid control-data-block length field "
- "was found in a command for unit 0x%016Lx on port "
- "0x%016Lx on adapter %s " "(debug info %d)\n",
- unit->fcp_lun, unit->port->wwpn,
- zfcp_get_busid_by_unit(unit),
- fsf_req->qtcb->bottom.io.fcp_cmnd_length);
- /* stop operation for this adapter */
- zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 134, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
+ struct fsf_qual_latency_info *lat_inf;
+ struct latency_cont *lat;
+ struct zfcp_unit *unit = req->unit;
+ unsigned long flags;
- case FSF_PORT_BOXED:
- ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter %s "
- "needs to be reopened\n",
- unit->port->wwpn, zfcp_get_busid_by_unit(unit));
- zfcp_erp_port_boxed(unit->port, 53, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
- break;
+ lat_inf = &req->qtcb->prefix.prot_status_qual.latency_info;
- case FSF_LUN_BOXED:
- ZFCP_LOG_NORMAL("unit needs to be reopened (adapter %s, "
- "wwpn=0x%016Lx, fcp_lun=0x%016Lx)\n",
- zfcp_get_busid_by_unit(unit),
- unit->port->wwpn, unit->fcp_lun);
- zfcp_erp_unit_boxed(unit, 54, fsf_req);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
- | ZFCP_STATUS_FSFREQ_RETRY;
- break;
-
- case FSF_ADAPTER_STATUS_AVAILABLE:
- switch (header->fsf_status_qual.word[0]) {
- case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
- /* re-establish link to port */
- zfcp_test_link(unit->port);
- break;
- case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- /* FIXME(hw) need proper specs for proper action */
- /* let scsi stack deal with retries and escalation */
- break;
- default:
- ZFCP_LOG_NORMAL
- ("Unknown status qualifier 0x%x arrived.\n",
- header->fsf_status_qual.word[0]);
- break;
- }
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+ switch (req->qtcb->bottom.io.data_direction) {
+ case FSF_DATADIR_READ:
+ lat = &unit->latencies.read;
break;
-
- case FSF_GOOD:
+ case FSF_DATADIR_WRITE:
+ lat = &unit->latencies.write;
break;
-
- case FSF_FCP_RSP_AVAILABLE:
+ case FSF_DATADIR_CMND:
+ lat = &unit->latencies.cmd;
break;
+ default:
+ return;
}
- skip_fsfstatus:
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) {
- retval =
- zfcp_fsf_send_fcp_command_task_management_handler(fsf_req);
- } else {
- retval = zfcp_fsf_send_fcp_command_task_handler(fsf_req);
- fsf_req->unit = NULL;
- zfcp_unit_put(unit);
- }
- return retval;
+ spin_lock_irqsave(&unit->latencies.lock, flags);
+ zfcp_fsf_update_lat(&lat->channel, lat_inf->channel_lat);
+ zfcp_fsf_update_lat(&lat->fabric, lat_inf->fabric_lat);
+ lat->counter++;
+ spin_unlock_irqrestore(&unit->latencies.lock, flags);
}
-/*
- * function: zfcp_fsf_send_fcp_command_task_handler
- *
- * purpose: evaluates FCP_RSP IU
- *
- * returns:
- */
-static int
-zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
{
- int retval = 0;
- struct scsi_cmnd *scpnt;
+ struct scsi_cmnd *scpnt = req->data;
struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
- &(fsf_req->qtcb->bottom.io.fcp_rsp);
- struct fcp_cmnd_iu *fcp_cmnd_iu = (struct fcp_cmnd_iu *)
- &(fsf_req->qtcb->bottom.io.fcp_cmnd);
+ &(req->qtcb->bottom.io.fcp_rsp);
u32 sns_len;
- char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu);
+ char *fcp_rsp_info = (unsigned char *) &fcp_rsp_iu[1];
unsigned long flags;
- struct zfcp_unit *unit = fsf_req->unit;
-
- read_lock_irqsave(&fsf_req->adapter->abort_lock, flags);
- scpnt = (struct scsi_cmnd *) fsf_req->data;
- if (unlikely(!scpnt)) {
- ZFCP_LOG_DEBUG
- ("Command with fsf_req %p is not associated to "
- "a scsi command anymore. Aborted?\n", fsf_req);
- goto out;
- }
- if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTED)) {
- /* FIXME: (design) mid-layer should handle DID_ABORT like
- * DID_SOFT_ERROR by retrying the request for devices
- * that allow retries.
- */
- ZFCP_LOG_DEBUG("Setting DID_SOFT_ERROR and SUGGEST_RETRY\n");
- set_host_byte(&scpnt->result, DID_SOFT_ERROR);
- set_driver_byte(&scpnt->result, SUGGEST_RETRY);
+
+ if (unlikely(!scpnt))
+ return;
+
+ read_lock_irqsave(&req->adapter->abort_lock, flags);
+
+ if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ABORTED)) {
+ set_host_byte(scpnt, DID_SOFT_ERROR);
+ set_driver_byte(scpnt, SUGGEST_RETRY);
goto skip_fsfstatus;
}
- if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
- ZFCP_LOG_DEBUG("Setting DID_ERROR\n");
- set_host_byte(&scpnt->result, DID_ERROR);
+ if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
+ set_host_byte(scpnt, DID_ERROR);
goto skip_fsfstatus;
}
- /* set message byte of result in SCSI command */
- scpnt->result |= COMMAND_COMPLETE << 8;
+ set_msg_byte(scpnt, COMMAND_COMPLETE);
- /*
- * copy SCSI status code of FCP_STATUS of FCP_RSP IU to status byte
- * of result in SCSI command
- */
scpnt->result |= fcp_rsp_iu->scsi_status;
- if (unlikely(fcp_rsp_iu->scsi_status)) {
- /* DEBUG */
- ZFCP_LOG_DEBUG("status for SCSI Command:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- scpnt->cmnd, scpnt->cmd_len);
- ZFCP_LOG_DEBUG("SCSI status code 0x%x\n",
- fcp_rsp_iu->scsi_status);
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (void *) fcp_rsp_iu, sizeof (struct fcp_rsp_iu));
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu),
- fcp_rsp_iu->fcp_sns_len);
- }
- /* check FCP_RSP_INFO */
+ if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)
+ zfcp_fsf_req_latency(req);
+
if (unlikely(fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)) {
- ZFCP_LOG_DEBUG("rsp_len is valid\n");
- switch (fcp_rsp_info[3]) {
- case RSP_CODE_GOOD:
- /* ok, continue */
- ZFCP_LOG_TRACE("no failure or Task Management "
- "Function complete\n");
- set_host_byte(&scpnt->result, DID_OK);
- break;
- case RSP_CODE_LENGTH_MISMATCH:
- /* hardware bug */
- ZFCP_LOG_NORMAL("bug: FCP response code indictates "
- "that the fibrechannel protocol data "
- "length differs from the burst length. "
- "The problem occured on unit 0x%016Lx "
- "on port 0x%016Lx on adapter %s",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- /* dump SCSI CDB as prepared by zfcp */
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &fsf_req->qtcb->
- bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
- set_host_byte(&scpnt->result, DID_ERROR);
- goto skip_fsfstatus;
- case RSP_CODE_FIELD_INVALID:
- /* driver or hardware bug */
- ZFCP_LOG_NORMAL("bug: FCP response code indictates "
- "that the fibrechannel protocol data "
- "fields were incorrectly set up. "
- "The problem occured on the unit "
- "0x%016Lx on port 0x%016Lx on "
- "adapter %s",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- /* dump SCSI CDB as prepared by zfcp */
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &fsf_req->qtcb->
- bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
- set_host_byte(&scpnt->result, DID_ERROR);
- goto skip_fsfstatus;
- case RSP_CODE_RO_MISMATCH:
- /* hardware bug */
- ZFCP_LOG_NORMAL("bug: The FCP response code indicates "
- "that conflicting values for the "
- "fibrechannel payload offset from the "
- "header were found. "
- "The problem occured on unit 0x%016Lx "
- "on port 0x%016Lx on adapter %s.\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- /* dump SCSI CDB as prepared by zfcp */
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &fsf_req->qtcb->
- bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
- set_host_byte(&scpnt->result, DID_ERROR);
- goto skip_fsfstatus;
- default:
- ZFCP_LOG_NORMAL("bug: An invalid FCP response "
- "code was detected for a command. "
- "The problem occured on the unit "
- "0x%016Lx on port 0x%016Lx on "
- "adapter %s (debug info 0x%x)\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit),
- fcp_rsp_info[3]);
- /* dump SCSI CDB as prepared by zfcp */
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
- (char *) &fsf_req->qtcb->
- bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
- set_host_byte(&scpnt->result, DID_ERROR);
+ if (fcp_rsp_info[3] == RSP_CODE_GOOD)
+ set_host_byte(scpnt, DID_OK);
+ else {
+ set_host_byte(scpnt, DID_ERROR);
goto skip_fsfstatus;
}
}
- /* check for sense data */
if (unlikely(fcp_rsp_iu->validity.bits.fcp_sns_len_valid)) {
- sns_len = FSF_FCP_RSP_SIZE -
- sizeof (struct fcp_rsp_iu) + fcp_rsp_iu->fcp_rsp_len;
- ZFCP_LOG_TRACE("room for %i bytes sense data in QTCB\n",
- sns_len);
+ sns_len = FSF_FCP_RSP_SIZE - sizeof(struct fcp_rsp_iu) +
+ fcp_rsp_iu->fcp_rsp_len;
sns_len = min(sns_len, (u32) SCSI_SENSE_BUFFERSIZE);
- ZFCP_LOG_TRACE("room for %i bytes sense data in SCSI command\n",
- SCSI_SENSE_BUFFERSIZE);
sns_len = min(sns_len, fcp_rsp_iu->fcp_sns_len);
- ZFCP_LOG_TRACE("scpnt->result =0x%x, command was:\n",
- scpnt->result);
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
- scpnt->cmnd, scpnt->cmd_len);
- ZFCP_LOG_TRACE("%i bytes sense data provided by FCP\n",
- fcp_rsp_iu->fcp_sns_len);
memcpy(scpnt->sense_buffer,
zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu), sns_len);
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
- (void *)scpnt->sense_buffer, sns_len);
}
- /* check for overrun */
- if (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_over)) {
- ZFCP_LOG_INFO("A data overrun was detected for a command. "
- "unit 0x%016Lx, port 0x%016Lx, adapter %s. "
- "The response data length is "
- "%d, the original length was %d.\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit),
- fcp_rsp_iu->fcp_resid,
- (int) zfcp_get_fcp_dl(fcp_cmnd_iu));
- }
-
- /* check for underrun */
if (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_under)) {
- ZFCP_LOG_INFO("A data underrun was detected for a command. "
- "unit 0x%016Lx, port 0x%016Lx, adapter %s. "
- "The response data length is "
- "%d, the original length was %d.\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit),
- fcp_rsp_iu->fcp_resid,
- (int) zfcp_get_fcp_dl(fcp_cmnd_iu));
-
scsi_set_resid(scpnt, fcp_rsp_iu->fcp_resid);
if (scsi_bufflen(scpnt) - scsi_get_resid(scpnt) <
scpnt->underflow)
- set_host_byte(&scpnt->result, DID_ERROR);
+ set_host_byte(scpnt, DID_ERROR);
}
-
- skip_fsfstatus:
- ZFCP_LOG_DEBUG("scpnt->result =0x%x\n", scpnt->result);
-
+skip_fsfstatus:
if (scpnt->result != 0)
- zfcp_scsi_dbf_event_result("erro", 3, fsf_req->adapter, scpnt, fsf_req);
+ zfcp_scsi_dbf_event_result("erro", 3, req->adapter, scpnt, req);
else if (scpnt->retries > 0)
- zfcp_scsi_dbf_event_result("retr", 4, fsf_req->adapter, scpnt, fsf_req);
+ zfcp_scsi_dbf_event_result("retr", 4, req->adapter, scpnt, req);
else
- zfcp_scsi_dbf_event_result("norm", 6, fsf_req->adapter, scpnt, fsf_req);
+ zfcp_scsi_dbf_event_result("norm", 6, req->adapter, scpnt, req);
- /* cleanup pointer (need this especially for abort) */
scpnt->host_scribble = NULL;
-
- /* always call back */
(scpnt->scsi_done) (scpnt);
-
/*
* We must hold this lock until scsi_done has been called.
* Otherwise we may call scsi_done after abort regarding this
* command has completed.
* Note: scsi_done must not block!
*/
- out:
- read_unlock_irqrestore(&fsf_req->adapter->abort_lock, flags);
- return retval;
+ read_unlock_irqrestore(&req->adapter->abort_lock, flags);
}
-/*
- * function: zfcp_fsf_send_fcp_command_task_management_handler
- *
- * purpose: evaluates FCP_RSP IU
- *
- * returns:
- */
-static int
-zfcp_fsf_send_fcp_command_task_management_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_send_fcp_ctm_handler(struct zfcp_fsf_req *req)
{
- int retval = 0;
struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
- &(fsf_req->qtcb->bottom.io.fcp_rsp);
- char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu);
- struct zfcp_unit *unit = (struct zfcp_unit *) fsf_req->data;
+ &(req->qtcb->bottom.io.fcp_rsp);
+ char *fcp_rsp_info = (unsigned char *) &fcp_rsp_iu[1];
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
- goto skip_fsfstatus;
- }
-
- /* check FCP_RSP_INFO */
- switch (fcp_rsp_info[3]) {
- case RSP_CODE_GOOD:
- /* ok, continue */
- ZFCP_LOG_DEBUG("no failure or Task Management "
- "Function complete\n");
- break;
- case RSP_CODE_TASKMAN_UNSUPP:
- ZFCP_LOG_NORMAL("bug: A reuested task management function "
- "is not supported on the target device "
- "unit 0x%016Lx, port 0x%016Lx, adapter %s\n ",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP;
- break;
- case RSP_CODE_TASKMAN_FAILED:
- ZFCP_LOG_NORMAL("bug: A reuested task management function "
- "failed to complete successfully. "
- "unit 0x%016Lx, port 0x%016Lx, adapter %s.\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
- break;
- default:
- ZFCP_LOG_NORMAL("bug: An invalid FCP response "
- "code was detected for a command. "
- "unit 0x%016Lx, port 0x%016Lx, adapter %s "
- "(debug info 0x%x)\n",
- unit->fcp_lun,
- unit->port->wwpn,
- zfcp_get_busid_by_unit(unit),
- fcp_rsp_info[3]);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
- }
-
- skip_fsfstatus:
- return retval;
+ if ((fcp_rsp_info[3] != RSP_CODE_GOOD) ||
+ (req->status & ZFCP_STATUS_FSFREQ_ERROR))
+ req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
}
-/*
- * function: zfcp_fsf_control_file
- *
- * purpose: Initiator of the control file upload/download FSF requests
- *
- * returns: 0 - FSF request is successfuly created and queued
- * -EOPNOTSUPP - The FCP adapter does not have Control File support
- * -EINVAL - Invalid direction specified
- * -ENOMEM - Insufficient memory
- * -EPERM - Cannot create FSF request or place it in QDIO queue
- */
-int
-zfcp_fsf_control_file(struct zfcp_adapter *adapter,
- struct zfcp_fsf_req **fsf_req_ptr,
- u32 fsf_command,
- u32 option,
- struct zfcp_sg_list *sg_list)
+static void zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *req)
{
- struct zfcp_fsf_req *fsf_req;
- struct fsf_qtcb_bottom_support *bottom;
- volatile struct qdio_buffer_element *sbale;
- unsigned long lock_flags;
- int req_flags = 0;
- int direction;
- int retval = 0;
-
- if (!(adapter->adapter_features & FSF_FEATURE_CFDC)) {
- ZFCP_LOG_INFO("cfdc not supported (adapter %s)\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = -EOPNOTSUPP;
- goto out;
- }
-
- switch (fsf_command) {
-
- case FSF_QTCB_DOWNLOAD_CONTROL_FILE:
- direction = SBAL_FLAGS0_TYPE_WRITE;
- if ((option != FSF_CFDC_OPTION_FULL_ACCESS) &&
- (option != FSF_CFDC_OPTION_RESTRICTED_ACCESS))
- req_flags = ZFCP_WAIT_FOR_SBAL;
- break;
-
- case FSF_QTCB_UPLOAD_CONTROL_FILE:
- direction = SBAL_FLAGS0_TYPE_READ;
- break;
-
- default:
- ZFCP_LOG_INFO("Invalid FSF command code 0x%08x\n", fsf_command);
- retval = -EINVAL;
- goto out;
- }
-
- retval = zfcp_fsf_req_create(adapter, fsf_command, req_flags,
- NULL, &lock_flags, &fsf_req);
- if (retval < 0) {
- ZFCP_LOG_INFO("error: Could not create FSF request for the "
- "adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = -EPERM;
- goto unlock_queue_lock;
- }
-
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
- sbale[0].flags |= direction;
-
- bottom = &fsf_req->qtcb->bottom.support;
- bottom->operation_subtype = FSF_CFDC_OPERATION_SUBTYPE;
- bottom->option = option;
-
- if (sg_list->count > 0) {
- int bytes;
-
- bytes = zfcp_qdio_sbals_from_sg(fsf_req, direction,
- sg_list->sg, sg_list->count,
- ZFCP_MAX_SBALS_PER_REQ);
- if (bytes != ZFCP_CFDC_MAX_CONTROL_FILE_SIZE) {
- ZFCP_LOG_INFO(
- "error: Could not create sufficient number of "
- "SBALS for an FSF request to the adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = -ENOMEM;
- goto free_fsf_req;
- }
- } else
- sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-
- zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
- retval = zfcp_fsf_req_send(fsf_req);
- if (retval < 0) {
- ZFCP_LOG_INFO("initiation of cfdc up/download failed"
- "(adapter %s)\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = -EPERM;
- goto free_fsf_req;
- }
- write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-
- ZFCP_LOG_NORMAL("Control file %s FSF request has been sent to the "
- "adapter %s\n",
- fsf_command == FSF_QTCB_DOWNLOAD_CONTROL_FILE ?
- "download" : "upload",
- zfcp_get_busid_by_adapter(adapter));
-
- wait_event(fsf_req->completion_wq,
- fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
-
- *fsf_req_ptr = fsf_req;
- goto out;
-
- free_fsf_req:
- zfcp_fsf_req_free(fsf_req);
- unlock_queue_lock:
- write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
- out:
- return retval;
-}
-
+ struct zfcp_unit *unit;
+ struct fsf_qtcb_header *header = &req->qtcb->header;
-/*
- * function: zfcp_fsf_control_file_handler
- *
- * purpose: Handler of the control file upload/download FSF requests
- *
- * returns: 0 - FSF request successfuly processed
- * -EAGAIN - Operation has to be repeated because of a temporary problem
- * -EACCES - There is no permission to execute an operation
- * -EPERM - The control file is not in a right format
- * -EIO - There is a problem with the FCP adapter
- * -EINVAL - Invalid operation
- * -EFAULT - User space memory I/O operation fault
- */
-static int
-zfcp_fsf_control_file_handler(struct zfcp_fsf_req *fsf_req)
-{
- struct zfcp_adapter *adapter = fsf_req->adapter;
- struct fsf_qtcb_header *header = &fsf_req->qtcb->header;
- struct fsf_qtcb_bottom_support *bottom = &fsf_req->qtcb->bottom.support;
- int retval = 0;
+ if (unlikely(req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT))
+ unit = req->data;
+ else
+ unit = req->unit;
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- retval = -EINVAL;
+ if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR))
goto skip_fsfstatus;
- }
switch (header->fsf_status) {
-
- case FSF_GOOD:
- ZFCP_LOG_NORMAL(
- "The FSF request has been successfully completed "
- "on the adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
-
- case FSF_OPERATION_PARTIALLY_SUCCESSFUL:
- if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE) {
- switch (header->fsf_status_qual.word[0]) {
-
- case FSF_SQ_CFDC_HARDENED_ON_SE:
- ZFCP_LOG_NORMAL(
- "CFDC on the adapter %s has being "
- "hardened on primary and secondary SE\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
-
- case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE:
- ZFCP_LOG_NORMAL(
- "CFDC of the adapter %s could not "
- "be saved on the SE\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
-
- case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2:
- ZFCP_LOG_NORMAL(
- "CFDC of the adapter %s could not "
- "be copied to the secondary SE\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
-
- default:
- ZFCP_LOG_NORMAL(
- "CFDC could not be hardened "
- "on the adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- }
- }
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval = -EAGAIN;
- break;
-
- case FSF_AUTHORIZATION_FAILURE:
- ZFCP_LOG_NORMAL(
- "Adapter %s does not accept privileged commands\n",
- zfcp_get_busid_by_adapter(adapter));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval = -EACCES;
+ case FSF_HANDLE_MISMATCH:
+ case FSF_PORT_HANDLE_NOT_VALID:
+ zfcp_erp_adapter_reopen(unit->port->adapter, 0, 112, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
- case FSF_CFDC_ERROR_DETECTED:
- ZFCP_LOG_NORMAL(
- "Error at position %d in the CFDC, "
- "CFDC is discarded by the adapter %s\n",
- header->fsf_status_qual.word[0],
- zfcp_get_busid_by_adapter(adapter));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval = -EPERM;
+ case FSF_FCPLUN_NOT_VALID:
+ case FSF_LUN_HANDLE_NOT_VALID:
+ zfcp_erp_port_reopen(unit->port, 0, 113, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
- case FSF_CONTROL_FILE_UPDATE_ERROR:
- ZFCP_LOG_NORMAL(
- "Adapter %s cannot harden the control file, "
- "file is discarded\n",
- zfcp_get_busid_by_adapter(adapter));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval = -EIO;
+ case FSF_SERVICE_CLASS_NOT_SUPPORTED:
+ zfcp_fsf_class_not_supp(req);
break;
-
- case FSF_CONTROL_FILE_TOO_LARGE:
- ZFCP_LOG_NORMAL(
- "Control file is too large, file is discarded "
- "by the adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval = -EIO;
+ case FSF_ACCESS_DENIED:
+ zfcp_fsf_access_denied_unit(req, unit);
break;
-
- case FSF_ACCESS_CONFLICT_DETECTED:
- if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE)
- ZFCP_LOG_NORMAL(
- "CFDC has been discarded by the adapter %s, "
- "because activation would impact "
- "%d active connection(s)\n",
- zfcp_get_busid_by_adapter(adapter),
- header->fsf_status_qual.word[0]);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval = -EIO;
+ case FSF_DIRECTION_INDICATOR_NOT_VALID:
+ dev_err(&req->adapter->ccw_device->dev,
+ "Incorrect direction %d, unit 0x%016Lx on port "
+ "0x%016Lx closed\n",
+ req->qtcb->bottom.io.data_direction,
+ (unsigned long long)unit->fcp_lun,
+ (unsigned long long)unit->port->wwpn);
+ zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 133, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
- case FSF_CONFLICTS_OVERRULED:
- if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE)
- ZFCP_LOG_NORMAL(
- "CFDC has been activated on the adapter %s, "
- "but activation has impacted "
- "%d active connection(s)\n",
- zfcp_get_busid_by_adapter(adapter),
- header->fsf_status_qual.word[0]);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval = -EIO;
+ case FSF_CMND_LENGTH_NOT_VALID:
+ dev_err(&req->adapter->ccw_device->dev,
+ "Incorrect CDB length %d, unit 0x%016Lx on "
+ "port 0x%016Lx closed\n",
+ req->qtcb->bottom.io.fcp_cmnd_length,
+ (unsigned long long)unit->fcp_lun,
+ (unsigned long long)unit->port->wwpn);
+ zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 134, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
-
- case FSF_UNKNOWN_OP_SUBTYPE:
- ZFCP_LOG_NORMAL("unknown operation subtype (adapter: %s, "
- "op_subtype=0x%x)\n",
- zfcp_get_busid_by_adapter(adapter),
- bottom->operation_subtype);
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval = -EINVAL;
+ case FSF_PORT_BOXED:
+ zfcp_erp_port_boxed(unit->port, 53, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+ ZFCP_STATUS_FSFREQ_RETRY;
break;
-
- case FSF_INVALID_COMMAND_OPTION:
- ZFCP_LOG_NORMAL(
- "Invalid option 0x%x has been specified "
- "in QTCB bottom sent to the adapter %s\n",
- bottom->option,
- zfcp_get_busid_by_adapter(adapter));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval = -EINVAL;
+ case FSF_LUN_BOXED:
+ zfcp_erp_unit_boxed(unit, 54, req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+ ZFCP_STATUS_FSFREQ_RETRY;
break;
-
- default:
- ZFCP_LOG_NORMAL(
- "bug: An unknown/unexpected FSF status 0x%08x "
- "was presented on the adapter %s\n",
- header->fsf_status,
- zfcp_get_busid_by_adapter(adapter));
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- retval = -EINVAL;
+ case FSF_ADAPTER_STATUS_AVAILABLE:
+ if (header->fsf_status_qual.word[0] ==
+ FSF_SQ_INVOKE_LINK_TEST_PROCEDURE)
+ zfcp_test_link(unit->port);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
-
skip_fsfstatus:
- return retval;
+ if (req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT)
+ zfcp_fsf_send_fcp_ctm_handler(req);
+ else {
+ zfcp_fsf_send_fcp_command_task_handler(req);
+ req->unit = NULL;
+ zfcp_unit_put(unit);
+ }
}
-static inline int
-zfcp_fsf_req_sbal_check(unsigned long *flags,
- struct zfcp_qdio_queue *queue, int needed)
+static void zfcp_set_fcp_dl(struct fcp_cmnd_iu *fcp_cmd, u32 fcp_dl)
{
- write_lock_irqsave(&queue->queue_lock, *flags);
- if (likely(atomic_read(&queue->free_count) >= needed))
- return 1;
- write_unlock_irqrestore(&queue->queue_lock, *flags);
- return 0;
-}
+ u32 *fcp_dl_ptr;
-/*
- * set qtcb pointer in fsf_req and initialize QTCB
- */
-static void
-zfcp_fsf_req_qtcb_init(struct zfcp_fsf_req *fsf_req)
-{
- if (likely(fsf_req->qtcb != NULL)) {
- fsf_req->qtcb->prefix.req_seq_no =
- fsf_req->adapter->fsf_req_seq_no;
- fsf_req->qtcb->prefix.req_id = fsf_req->req_id;
- fsf_req->qtcb->prefix.ulp_info = ZFCP_ULP_INFO_VERSION;
- fsf_req->qtcb->prefix.qtcb_type =
- fsf_qtcb_type[fsf_req->fsf_command];
- fsf_req->qtcb->prefix.qtcb_version = ZFCP_QTCB_VERSION;
- fsf_req->qtcb->header.req_handle = fsf_req->req_id;
- fsf_req->qtcb->header.fsf_command = fsf_req->fsf_command;
- }
+ /*
+ * fcp_dl_addr = start address of fcp_cmnd structure +
+ * size of fixed part + size of dynamically sized add_dcp_cdb field
+ * SEE FCP-2 documentation
+ */
+ fcp_dl_ptr = (u32 *) ((unsigned char *) &fcp_cmd[1] +
+ (fcp_cmd->add_fcp_cdb_length << 2));
+ *fcp_dl_ptr = fcp_dl;
}
/**
- * zfcp_fsf_req_sbal_get - try to get one SBAL in the request queue
- * @adapter: adapter for which request queue is examined
- * @req_flags: flags indicating whether to wait for needed SBAL or not
- * @lock_flags: lock_flags if queue_lock is taken
- * Return: 0 on success, otherwise -EIO, or -ERESTARTSYS
- * Locks: lock adapter->request_queue->queue_lock on success
- */
-static int
-zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter, int req_flags,
- unsigned long *lock_flags)
-{
- long ret;
- struct zfcp_qdio_queue *req_queue = &adapter->request_queue;
-
- if (unlikely(req_flags & ZFCP_WAIT_FOR_SBAL)) {
- ret = wait_event_interruptible_timeout(adapter->request_wq,
- zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1),
- ZFCP_SBAL_TIMEOUT);
- if (ret < 0)
- return ret;
- if (!ret)
- return -EIO;
- } else if (!zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1))
- return -EIO;
-
- return 0;
-}
-
-/*
- * function: zfcp_fsf_req_create
- *
- * purpose: create an FSF request at the specified adapter and
- * setup common fields
- *
- * returns: -ENOMEM if there was insufficient memory for a request
- * -EIO if no qdio buffers could be allocate to the request
- * -EINVAL/-EPERM on bug conditions in req_dequeue
- * 0 in success
- *
- * note: The created request is returned by reference.
- *
- * locks: lock of concerned request queue must not be held,
- * but is held on completion (write, irqsave)
+ * zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command)
+ * @adapter: adapter where scsi command is issued
+ * @unit: unit where command is sent to
+ * @scsi_cmnd: scsi command to be sent
+ * @timer: timer to be started when request is initiated
+ * @req_flags: flags for fsf_request
*/
-int
-zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags,
- mempool_t *pool, unsigned long *lock_flags,
- struct zfcp_fsf_req **fsf_req_p)
+int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
+ struct zfcp_unit *unit,
+ struct scsi_cmnd *scsi_cmnd,
+ int use_timer, int req_flags)
{
- volatile struct qdio_buffer_element *sbale;
- struct zfcp_fsf_req *fsf_req = NULL;
- int ret = 0;
- struct zfcp_qdio_queue *req_queue = &adapter->request_queue;
-
- /* allocate new FSF request */
- fsf_req = zfcp_fsf_req_alloc(pool, req_flags);
- if (unlikely(NULL == fsf_req)) {
- ZFCP_LOG_DEBUG("error: Could not put an FSF request into "
- "the outbound (send) queue.\n");
- ret = -ENOMEM;
- goto failed_fsf_req;
- }
-
- fsf_req->adapter = adapter;
- fsf_req->fsf_command = fsf_cmd;
- INIT_LIST_HEAD(&fsf_req->list);
- init_timer(&fsf_req->timer);
+ struct zfcp_fsf_req *req;
+ struct fcp_cmnd_iu *fcp_cmnd_iu;
+ unsigned int sbtype;
+ int real_bytes, retval = -EIO;
- /* initialize waitqueue which may be used to wait on
- this request completion */
- init_waitqueue_head(&fsf_req->completion_wq);
+ if (unlikely(!(atomic_read(&unit->status) &
+ ZFCP_STATUS_COMMON_UNBLOCKED)))
+ return -EBUSY;
- ret = zfcp_fsf_req_sbal_get(adapter, req_flags, lock_flags);
- if (ret < 0)
- goto failed_sbals;
+ spin_lock(&adapter->req_q_lock);
+ if (!zfcp_fsf_sbal_available(adapter))
+ goto out;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
+ adapter->pool.fsf_req_scsi);
+ if (IS_ERR(req)) {
+ retval = PTR_ERR(req);
+ goto out;
+ }
- /* this is serialized (we are holding req_queue-lock of adapter) */
- if (adapter->req_no == 0)
- adapter->req_no++;
- fsf_req->req_id = adapter->req_no++;
+ zfcp_unit_get(unit);
+ req->unit = unit;
+ req->data = scsi_cmnd;
+ req->handler = zfcp_fsf_send_fcp_command_handler;
+ req->qtcb->header.lun_handle = unit->handle;
+ req->qtcb->header.port_handle = unit->port->handle;
+ req->qtcb->bottom.io.service_class = FSF_CLASS_3;
- zfcp_fsf_req_qtcb_init(fsf_req);
+ scsi_cmnd->host_scribble = (unsigned char *) req->req_id;
+ fcp_cmnd_iu = (struct fcp_cmnd_iu *) &(req->qtcb->bottom.io.fcp_cmnd);
+ fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
/*
- * We hold queue_lock here. Check if QDIOUP is set and let request fail
- * if it is not set (see also *_open_qdio and *_close_qdio).
+ * set depending on data direction:
+ * data direction bits in SBALE (SB Type)
+ * data direction bits in QTCB
+ * data direction bits in FCP_CMND IU
*/
-
- if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
- write_unlock_irqrestore(&req_queue->queue_lock, *lock_flags);
- ret = -EIO;
- goto failed_sbals;
+ switch (scsi_cmnd->sc_data_direction) {
+ case DMA_NONE:
+ req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
+ sbtype = SBAL_FLAGS0_TYPE_READ;
+ break;
+ case DMA_FROM_DEVICE:
+ req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
+ sbtype = SBAL_FLAGS0_TYPE_READ;
+ fcp_cmnd_iu->rddata = 1;
+ break;
+ case DMA_TO_DEVICE:
+ req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE;
+ sbtype = SBAL_FLAGS0_TYPE_WRITE;
+ fcp_cmnd_iu->wddata = 1;
+ break;
+ case DMA_BIDIRECTIONAL:
+ default:
+ retval = -EIO;
+ goto failed_scsi_cmnd;
}
- if (fsf_req->qtcb) {
- fsf_req->seq_no = adapter->fsf_req_seq_no;
- fsf_req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no;
- }
- fsf_req->sbal_number = 1;
- fsf_req->sbal_first = req_queue->free_index;
- fsf_req->sbal_curr = req_queue->free_index;
- fsf_req->sbale_curr = 1;
+ if (likely((scsi_cmnd->device->simple_tags) ||
+ ((atomic_read(&unit->status) & ZFCP_STATUS_UNIT_READONLY) &&
+ (atomic_read(&unit->status) & ZFCP_STATUS_UNIT_SHARED))))
+ fcp_cmnd_iu->task_attribute = SIMPLE_Q;
+ else
+ fcp_cmnd_iu->task_attribute = UNTAGGED;
- if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP)) {
- fsf_req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- }
+ if (unlikely(scsi_cmnd->cmd_len > FCP_CDB_LENGTH))
+ fcp_cmnd_iu->add_fcp_cdb_length =
+ (scsi_cmnd->cmd_len - FCP_CDB_LENGTH) >> 2;
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ memcpy(fcp_cmnd_iu->fcp_cdb, scsi_cmnd->cmnd, scsi_cmnd->cmd_len);
- /* setup common SBALE fields */
- sbale[0].addr = (void *) fsf_req->req_id;
- sbale[0].flags |= SBAL_FLAGS0_COMMAND;
- if (likely(fsf_req->qtcb != NULL)) {
- sbale[1].addr = (void *) fsf_req->qtcb;
- sbale[1].length = sizeof(struct fsf_qtcb);
+ req->qtcb->bottom.io.fcp_cmnd_length = sizeof(struct fcp_cmnd_iu) +
+ fcp_cmnd_iu->add_fcp_cdb_length + sizeof(u32);
+
+ real_bytes = zfcp_qdio_sbals_from_sg(req, sbtype,
+ scsi_sglist(scsi_cmnd),
+ FSF_MAX_SBALS_PER_REQ);
+ if (unlikely(real_bytes < 0)) {
+ if (req->sbal_number < FSF_MAX_SBALS_PER_REQ)
+ retval = -EIO;
+ else {
+ dev_err(&adapter->ccw_device->dev,
+ "Oversize data package, unit 0x%016Lx "
+ "on port 0x%016Lx closed\n",
+ (unsigned long long)unit->fcp_lun,
+ (unsigned long long)unit->port->wwpn);
+ zfcp_erp_unit_shutdown(unit, 0, 131, req);
+ retval = -EINVAL;
+ }
+ goto failed_scsi_cmnd;
}
- ZFCP_LOG_TRACE("got %i free BUFFERs starting at index %i\n",
- fsf_req->sbal_number, fsf_req->sbal_first);
+ zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes);
- goto success;
+ if (use_timer)
+ zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
- failed_sbals:
-/* dequeue new FSF request previously enqueued */
- zfcp_fsf_req_free(fsf_req);
- fsf_req = NULL;
+ retval = zfcp_fsf_req_send(req);
+ if (unlikely(retval))
+ goto failed_scsi_cmnd;
- failed_fsf_req:
- write_lock_irqsave(&req_queue->queue_lock, *lock_flags);
- success:
- *fsf_req_p = fsf_req;
- return ret;
+ goto out;
+
+failed_scsi_cmnd:
+ zfcp_unit_put(unit);
+ zfcp_fsf_req_free(req);
+ scsi_cmnd->host_scribble = NULL;
+out:
+ spin_unlock(&adapter->req_q_lock);
+ return retval;
}
-/*
- * function: zfcp_fsf_req_send
- *
- * purpose: start transfer of FSF request via QDIO
- *
- * returns: 0 - request transfer succesfully started
- * !0 - start of request transfer failed
+/**
+ * zfcp_fsf_send_fcp_ctm - send SCSI task management command
+ * @adapter: pointer to struct zfcp-adapter
+ * @unit: pointer to struct zfcp_unit
+ * @tm_flags: unsigned byte for task management flags
+ * @req_flags: int request flags
+ * Returns: on success pointer to struct fsf_req, NULL otherwise
*/
-static int zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req)
+struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_adapter *adapter,
+ struct zfcp_unit *unit,
+ u8 tm_flags, int req_flags)
{
- struct zfcp_adapter *adapter;
- struct zfcp_qdio_queue *req_queue;
- volatile struct qdio_buffer_element *sbale;
- int inc_seq_no;
- int new_distance_from_int;
- int retval = 0;
+ struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *req = NULL;
+ struct fcp_cmnd_iu *fcp_cmnd_iu;
+
+ if (unlikely(!(atomic_read(&unit->status) &
+ ZFCP_STATUS_COMMON_UNBLOCKED)))
+ return NULL;
+
+ spin_lock(&adapter->req_q_lock);
+ if (!zfcp_fsf_sbal_available(adapter))
+ goto out;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
+ adapter->pool.fsf_req_scsi);
+ if (IS_ERR(req))
+ goto out;
- adapter = fsf_req->adapter;
- req_queue = &adapter->request_queue,
+ req->status |= ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT;
+ req->data = unit;
+ req->handler = zfcp_fsf_send_fcp_command_handler;
+ req->qtcb->header.lun_handle = unit->handle;
+ req->qtcb->header.port_handle = unit->port->handle;
+ req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
+ req->qtcb->bottom.io.service_class = FSF_CLASS_3;
+ req->qtcb->bottom.io.fcp_cmnd_length = sizeof(struct fcp_cmnd_iu) +
+ sizeof(u32);
+
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+ fcp_cmnd_iu = (struct fcp_cmnd_iu *) &req->qtcb->bottom.io.fcp_cmnd;
+ fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
+ fcp_cmnd_iu->task_management_flags = tm_flags;
- /* FIXME(debug): remove it later */
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_first, 0);
- ZFCP_LOG_DEBUG("SBALE0 flags=0x%x\n", sbale[0].flags);
- ZFCP_LOG_TRACE("HEX DUMP OF SBALE1 PAYLOAD:\n");
- ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) sbale[1].addr,
- sbale[1].length);
+ zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT);
+ if (!zfcp_fsf_req_send(req))
+ goto out;
- /* put allocated FSF request into hash table */
- spin_lock(&adapter->req_list_lock);
- zfcp_reqlist_add(adapter, fsf_req);
- spin_unlock(&adapter->req_list_lock);
+ zfcp_fsf_req_free(req);
+ req = NULL;
+out:
+ spin_unlock(&adapter->req_q_lock);
+ return req;
+}
- inc_seq_no = (fsf_req->qtcb != NULL);
+static void zfcp_fsf_control_file_handler(struct zfcp_fsf_req *req)
+{
+ if (req->qtcb->header.fsf_status != FSF_GOOD)
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+}
- ZFCP_LOG_TRACE("request queue of adapter %s: "
- "next free SBAL is %i, %i free SBALs\n",
- zfcp_get_busid_by_adapter(adapter),
- req_queue->free_index,
- atomic_read(&req_queue->free_count));
+/**
+ * zfcp_fsf_control_file - control file upload/download
+ * @adapter: pointer to struct zfcp_adapter
+ * @fsf_cfdc: pointer to struct zfcp_fsf_cfdc
+ * Returns: on success pointer to struct zfcp_fsf_req, NULL otherwise
+ */
+struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter,
+ struct zfcp_fsf_cfdc *fsf_cfdc)
+{
+ struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *req = NULL;
+ struct fsf_qtcb_bottom_support *bottom;
+ int direction, retval = -EIO, bytes;
- ZFCP_LOG_DEBUG("calling do_QDIO adapter %s, flags=0x%x, queue_no=%i, "
- "index_in_queue=%i, count=%i, buffers=%p\n",
- zfcp_get_busid_by_adapter(adapter),
- QDIO_FLAG_SYNC_OUTPUT,
- 0, fsf_req->sbal_first, fsf_req->sbal_number,
- &req_queue->buffer[fsf_req->sbal_first]);
+ if (!(adapter->adapter_features & FSF_FEATURE_CFDC))
+ return ERR_PTR(-EOPNOTSUPP);
- /*
- * adjust the number of free SBALs in request queue as well as
- * position of first one
- */
- atomic_sub(fsf_req->sbal_number, &req_queue->free_count);
- ZFCP_LOG_TRACE("free_count=%d\n", atomic_read(&req_queue->free_count));
- req_queue->free_index += fsf_req->sbal_number; /* increase */
- req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q; /* wrap if needed */
- new_distance_from_int = zfcp_qdio_determine_pci(req_queue, fsf_req);
+ switch (fsf_cfdc->command) {
+ case FSF_QTCB_DOWNLOAD_CONTROL_FILE:
+ direction = SBAL_FLAGS0_TYPE_WRITE;
+ break;
+ case FSF_QTCB_UPLOAD_CONTROL_FILE:
+ direction = SBAL_FLAGS0_TYPE_READ;
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
- fsf_req->issued = get_clock();
+ spin_lock_bh(&adapter->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
- retval = do_QDIO(adapter->ccw_device,
- QDIO_FLAG_SYNC_OUTPUT,
- 0, fsf_req->sbal_first, fsf_req->sbal_number, NULL);
+ req = zfcp_fsf_req_create(adapter, fsf_cfdc->command, 0, NULL);
+ if (IS_ERR(req)) {
+ retval = -EPERM;
+ goto out;
+ }
- if (unlikely(retval)) {
- /* Queues are down..... */
- retval = -EIO;
- del_timer(&fsf_req->timer);
- spin_lock(&adapter->req_list_lock);
- zfcp_reqlist_remove(adapter, fsf_req);
- spin_unlock(&adapter->req_list_lock);
- /* undo changes in request queue made for this request */
- zfcp_qdio_zero_sbals(req_queue->buffer,
- fsf_req->sbal_first, fsf_req->sbal_number);
- atomic_add(fsf_req->sbal_number, &req_queue->free_count);
- req_queue->free_index -= fsf_req->sbal_number;
- req_queue->free_index += QDIO_MAX_BUFFERS_PER_Q;
- req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q; /* wrap */
- zfcp_erp_adapter_reopen(adapter, 0, 116, fsf_req);
- } else {
- req_queue->distance_from_int = new_distance_from_int;
- /*
- * increase FSF sequence counter -
- * this must only be done for request successfully enqueued to
- * QDIO this rejected requests may be cleaned up by calling
- * routines resulting in missing sequence counter values
- * otherwise,
- */
+ req->handler = zfcp_fsf_control_file_handler;
+
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].flags |= direction;
- /* Don't increase for unsolicited status */
- if (inc_seq_no)
- adapter->fsf_req_seq_no++;
+ bottom = &req->qtcb->bottom.support;
+ bottom->operation_subtype = FSF_CFDC_OPERATION_SUBTYPE;
+ bottom->option = fsf_cfdc->option;
- /* count FSF requests pending */
- atomic_inc(&adapter->reqs_active);
+ bytes = zfcp_qdio_sbals_from_sg(req, direction, fsf_cfdc->sg,
+ FSF_MAX_SBALS_PER_REQ);
+ if (bytes != ZFCP_CFDC_MAX_SIZE) {
+ retval = -ENOMEM;
+ zfcp_fsf_req_free(req);
+ goto out;
}
- return retval;
-}
-#undef ZFCP_LOG_AREA
+ zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+ retval = zfcp_fsf_req_send(req);
+out:
+ spin_unlock_bh(&adapter->req_q_lock);
+
+ if (!retval) {
+ wait_event(req->completion_wq,
+ req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ return req;
+ }
+ return ERR_PTR(retval);
+}
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index 099970b2700..fd3a88777ac 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -1,27 +1,16 @@
/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
*
- * (C) Copyright IBM Corp. 2002, 2006
+ * Interface to the FSF support functions.
*
- * 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 Corporation 2002, 2008
*/
#ifndef FSF_H
#define FSF_H
+#include <linux/pfn.h>
+
#define FSF_QTCB_CURRENT_VERSION 0x00000001
/* FSF commands */
@@ -82,13 +71,6 @@
#define FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED 0x00000041
#define FSF_ELS_COMMAND_REJECTED 0x00000050
#define FSF_GENERIC_COMMAND_REJECTED 0x00000051
-#define FSF_OPERATION_PARTIALLY_SUCCESSFUL 0x00000052
-#define FSF_AUTHORIZATION_FAILURE 0x00000053
-#define FSF_CFDC_ERROR_DETECTED 0x00000054
-#define FSF_CONTROL_FILE_UPDATE_ERROR 0x00000055
-#define FSF_CONTROL_FILE_TOO_LARGE 0x00000056
-#define FSF_ACCESS_CONFLICT_DETECTED 0x00000057
-#define FSF_CONFLICTS_OVERRULED 0x00000058
#define FSF_PORT_BOXED 0x00000059
#define FSF_LUN_BOXED 0x0000005A
#define FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE 0x0000005B
@@ -96,9 +78,7 @@
#define FSF_REQUEST_SIZE_TOO_LARGE 0x00000061
#define FSF_RESPONSE_SIZE_TOO_LARGE 0x00000062
#define FSF_SBAL_MISMATCH 0x00000063
-#define FSF_OPEN_PORT_WITHOUT_PRLI 0x00000064
#define FSF_ADAPTER_STATUS_AVAILABLE 0x000000AD
-#define FSF_FCP_RSP_AVAILABLE 0x000000AF
#define FSF_UNKNOWN_COMMAND 0x000000E2
#define FSF_UNKNOWN_OP_SUBTYPE 0x000000E3
#define FSF_INVALID_COMMAND_OPTION 0x000000E5
@@ -113,20 +93,9 @@
#define FSF_SQ_RETRY_IF_POSSIBLE 0x02
#define FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED 0x03
#define FSF_SQ_INVOKE_LINK_TEST_PROCEDURE 0x04
-#define FSF_SQ_ULP_PROGRAMMING_ERROR 0x05
#define FSF_SQ_COMMAND_ABORTED 0x06
#define FSF_SQ_NO_RETRY_POSSIBLE 0x07
-/* FSF status qualifier for CFDC commands */
-#define FSF_SQ_CFDC_HARDENED_ON_SE 0x00000000
-#define FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE 0x00000001
-#define FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2 0x00000002
-/* CFDC subtable codes */
-#define FSF_SQ_CFDC_SUBTABLE_OS 0x0001
-#define FSF_SQ_CFDC_SUBTABLE_PORT_WWPN 0x0002
-#define FSF_SQ_CFDC_SUBTABLE_PORT_DID 0x0003
-#define FSF_SQ_CFDC_SUBTABLE_LUN 0x0004
-
/* FSF status qualifier (most significant 4 bytes), local link down */
#define FSF_PSQ_LINK_NO_LIGHT 0x00000004
#define FSF_PSQ_LINK_WRAP_PLUG 0x00000008
@@ -156,7 +125,6 @@
#define FSF_STATUS_READ_LINK_UP 0x00000006
#define FSF_STATUS_READ_NOTIFICATION_LOST 0x00000009
#define FSF_STATUS_READ_CFDC_UPDATED 0x0000000A
-#define FSF_STATUS_READ_CFDC_HARDENED 0x0000000B
#define FSF_STATUS_READ_FEATURE_UPDATE_ALERT 0x0000000C
/* status subtypes in status read buffer */
@@ -170,20 +138,9 @@
/* status subtypes for unsolicited status notification lost */
#define FSF_STATUS_READ_SUB_INCOMING_ELS 0x00000001
-#define FSF_STATUS_READ_SUB_SENSE_DATA 0x00000002
-#define FSF_STATUS_READ_SUB_LINK_STATUS 0x00000004
-#define FSF_STATUS_READ_SUB_PORT_CLOSED 0x00000008
-#define FSF_STATUS_READ_SUB_BIT_ERROR_THRESHOLD 0x00000010
#define FSF_STATUS_READ_SUB_ACT_UPDATED 0x00000020
-#define FSF_STATUS_READ_SUB_ACT_HARDENED 0x00000040
-#define FSF_STATUS_READ_SUB_FEATURE_UPDATE_ALERT 0x00000080
-
-/* status subtypes for CFDC */
-#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE 0x00000002
-#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2 0x0000000F
/* topologie that is detected by the adapter */
-#define FSF_TOPO_ERROR 0x00000000
#define FSF_TOPO_P2P 0x00000001
#define FSF_TOPO_FABRIC 0x00000002
#define FSF_TOPO_AL 0x00000003
@@ -191,17 +148,13 @@
/* data direction for FCP commands */
#define FSF_DATADIR_WRITE 0x00000001
#define FSF_DATADIR_READ 0x00000002
-#define FSF_DATADIR_READ_WRITE 0x00000003
#define FSF_DATADIR_CMND 0x00000004
/* fc service class */
-#define FSF_CLASS_1 0x00000001
-#define FSF_CLASS_2 0x00000002
#define FSF_CLASS_3 0x00000003
/* SBAL chaining */
#define FSF_MAX_SBALS_PER_REQ 36
-#define FSF_MAX_SBALS_PER_ELS_REQ 2
/* logging space behind QTCB */
#define FSF_QTCB_LOG_SIZE 1024
@@ -211,53 +164,29 @@
#define FSF_FEATURE_LUN_SHARING 0x00000004
#define FSF_FEATURE_NOTIFICATION_LOST 0x00000008
#define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010
-#define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020
#define FSF_FEATURE_UPDATE_ALERT 0x00000100
#define FSF_FEATURE_MEASUREMENT_DATA 0x00000200
/* host connection features */
#define FSF_FEATURE_NPIV_MODE 0x00000001
-#define FSF_FEATURE_VM_ASSIGNED_WWPN 0x00000002
/* option */
#define FSF_OPEN_LUN_SUPPRESS_BOXING 0x00000001
-#define FSF_OPEN_LUN_REPLICATE_SENSE 0x00000002
-
-/* adapter types */
-#define FSF_ADAPTER_TYPE_FICON 0x00000001
-#define FSF_ADAPTER_TYPE_FICON_EXPRESS 0x00000002
-
-/* port types */
-#define FSF_HBA_PORTTYPE_UNKNOWN 0x00000001
-#define FSF_HBA_PORTTYPE_NOTPRESENT 0x00000003
-#define FSF_HBA_PORTTYPE_NPORT 0x00000005
-#define FSF_HBA_PORTTYPE_PTP 0x00000021
-/* following are not defined and used by FSF Spec
- but are additionally defined by FC-HBA */
-#define FSF_HBA_PORTTYPE_OTHER 0x00000002
-#define FSF_HBA_PORTTYPE_NOTPRESENT 0x00000003
-#define FSF_HBA_PORTTYPE_NLPORT 0x00000006
-#define FSF_HBA_PORTTYPE_FLPORT 0x00000007
-#define FSF_HBA_PORTTYPE_FPORT 0x00000008
-#define FSF_HBA_PORTTYPE_LPORT 0x00000020
-
-/* port states */
-#define FSF_HBA_PORTSTATE_UNKNOWN 0x00000001
-#define FSF_HBA_PORTSTATE_ONLINE 0x00000002
-#define FSF_HBA_PORTSTATE_OFFLINE 0x00000003
-#define FSF_HBA_PORTSTATE_LINKDOWN 0x00000006
-#define FSF_HBA_PORTSTATE_ERROR 0x00000007
-
-/* IO states of adapter */
-#define FSF_IOSTAT_NPORT_RJT 0x00000004
-#define FSF_IOSTAT_FABRIC_RJT 0x00000005
-#define FSF_IOSTAT_LS_RJT 0x00000009
/* open LUN access flags*/
-#define FSF_UNIT_ACCESS_OPEN_LUN_ALLOWED 0x01000000
#define FSF_UNIT_ACCESS_EXCLUSIVE 0x02000000
#define FSF_UNIT_ACCESS_OUTBOUND_TRANSFER 0x10000000
+/* FSF interface for CFDC */
+#define ZFCP_CFDC_MAX_SIZE 127 * 1024
+#define ZFCP_CFDC_PAGES PFN_UP(ZFCP_CFDC_MAX_SIZE)
+
+struct zfcp_fsf_cfdc {
+ struct scatterlist sg[ZFCP_CFDC_PAGES];
+ u32 command;
+ u32 option;
+};
+
struct fsf_queue_designator {
u8 cssid;
u8 chpid;
@@ -266,11 +195,6 @@ struct fsf_queue_designator {
u32 res1;
} __attribute__ ((packed));
-struct fsf_port_closed_payload {
- struct fsf_queue_designator queue_designator;
- u32 port_handle;
-} __attribute__ ((packed));
-
struct fsf_bit_error_payload {
u32 res1;
u32 link_failure_error_count;
@@ -288,6 +212,18 @@ struct fsf_bit_error_payload {
u32 current_transmit_b2b_credit;
} __attribute__ ((packed));
+struct fsf_link_down_info {
+ u32 error_code;
+ u32 res1;
+ u8 res2[2];
+ u8 primary_status;
+ u8 ioerr_code;
+ u8 action_code;
+ u8 reason_code;
+ u8 explanation_code;
+ u8 vendor_specific_code;
+} __attribute__ ((packed));
+
struct fsf_status_read_buffer {
u32 status_type;
u32 status_subtype;
@@ -298,7 +234,12 @@ struct fsf_status_read_buffer {
u32 class;
u64 fcp_lun;
u8 res3[24];
- u8 payload[FSF_STATUS_READ_PAYLOAD_SIZE];
+ union {
+ u8 data[FSF_STATUS_READ_PAYLOAD_SIZE];
+ u32 word[FSF_STATUS_READ_PAYLOAD_SIZE/sizeof(u32)];
+ struct fsf_link_down_info link_down_info;
+ struct fsf_bit_error_payload bit_error;
+ } payload;
} __attribute__ ((packed));
struct fsf_qual_version_error {
@@ -311,23 +252,19 @@ struct fsf_qual_sequence_error {
u32 res1[3];
} __attribute__ ((packed));
-struct fsf_link_down_info {
- u32 error_code;
- u32 res1;
- u8 res2[2];
- u8 primary_status;
- u8 ioerr_code;
- u8 action_code;
- u8 reason_code;
- u8 explanation_code;
- u8 vendor_specific_code;
+struct fsf_qual_latency_info {
+ u32 channel_lat;
+ u32 fabric_lat;
+ u8 res1[8];
} __attribute__ ((packed));
union fsf_prot_status_qual {
+ u32 word[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u32)];
u64 doubleword[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u64)];
struct fsf_qual_version_error version_error;
struct fsf_qual_sequence_error sequence_error;
struct fsf_link_down_info link_down_info;
+ struct fsf_qual_latency_info latency_info;
} __attribute__ ((packed));
struct fsf_qtcb_prefix {
@@ -437,7 +374,9 @@ struct fsf_qtcb_bottom_config {
u32 fc_link_speed;
u32 adapter_type;
u32 peer_d_id;
- u8 res2[12];
+ u8 res1[2];
+ u16 timer_interval;
+ u8 res2[8];
u32 s_id;
struct fsf_nport_serv_param nport_serv_param;
u8 reserved_nport_serv_param[16];
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
index 8ca5f074c68..3e05080e62d 100644
--- a/drivers/s390/scsi/zfcp_qdio.c
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -1,241 +1,101 @@
/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
*
- * (C) Copyright IBM Corp. 2002, 2006
+ * Setup and helper functions to access QDIO.
*
- * 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 Corporation 2002, 2008
*/
#include "zfcp_ext.h"
-static void zfcp_qdio_sbal_limit(struct zfcp_fsf_req *, int);
-static inline volatile struct qdio_buffer_element *zfcp_qdio_sbale_get
- (struct zfcp_qdio_queue *, int, int);
-static inline volatile struct qdio_buffer_element *zfcp_qdio_sbale_resp
- (struct zfcp_fsf_req *, int, int);
-static volatile struct qdio_buffer_element *zfcp_qdio_sbal_chain
- (struct zfcp_fsf_req *, unsigned long);
-static volatile struct qdio_buffer_element *zfcp_qdio_sbale_next
- (struct zfcp_fsf_req *, unsigned long);
-static int zfcp_qdio_sbals_zero(struct zfcp_qdio_queue *, int, int);
-static inline int zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *);
-static void zfcp_qdio_sbale_fill
- (struct zfcp_fsf_req *, unsigned long, void *, int);
-static int zfcp_qdio_sbals_from_segment
- (struct zfcp_fsf_req *, unsigned long, void *, unsigned long);
-
-static qdio_handler_t zfcp_qdio_request_handler;
-static qdio_handler_t zfcp_qdio_response_handler;
-static int zfcp_qdio_handler_error_check(struct zfcp_adapter *,
- unsigned int, unsigned int, unsigned int, int, int);
-
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_QDIO
+/* FIXME(tune): free space should be one max. SBAL chain plus what? */
+#define ZFCP_QDIO_PCI_INTERVAL (QDIO_MAX_BUFFERS_PER_Q \
+ - (FSF_MAX_SBALS_PER_REQ + 4))
+#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer))
-/*
- * Frees BUFFER memory for each of the pointers of the struct qdio_buffer array
- * in the adapter struct sbuf is the pointer array.
- *
- * locks: must only be called with zfcp_data.config_sema taken
- */
-static void
-zfcp_qdio_buffers_dequeue(struct qdio_buffer **sbuf)
-{
- int pos;
-
- for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE)
- free_page((unsigned long) sbuf[pos]);
-}
-
-/*
- * Allocates BUFFER memory to each of the pointers of the qdio_buffer_t
- * array in the adapter struct.
- * Cur_buf is the pointer array
- *
- * returns: zero on success else -ENOMEM
- * locks: must only be called with zfcp_data.config_sema taken
- */
-static int
-zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbuf)
+static int zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbal)
{
int pos;
for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE) {
- sbuf[pos] = (struct qdio_buffer *) get_zeroed_page(GFP_KERNEL);
- if (!sbuf[pos]) {
- zfcp_qdio_buffers_dequeue(sbuf);
+ sbal[pos] = (struct qdio_buffer *) get_zeroed_page(GFP_KERNEL);
+ if (!sbal[pos])
return -ENOMEM;
- }
}
for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos++)
if (pos % QBUFF_PER_PAGE)
- sbuf[pos] = sbuf[pos - 1] + 1;
+ sbal[pos] = sbal[pos - 1] + 1;
return 0;
}
-/* locks: must only be called with zfcp_data.config_sema taken */
-int
-zfcp_qdio_allocate_queues(struct zfcp_adapter *adapter)
+static struct qdio_buffer_element *
+zfcp_qdio_sbale(struct zfcp_qdio_queue *q, int sbal_idx, int sbale_idx)
{
- int ret;
-
- ret = zfcp_qdio_buffers_enqueue(adapter->request_queue.buffer);
- if (ret)
- return ret;
- return zfcp_qdio_buffers_enqueue(adapter->response_queue.buffer);
+ return &q->sbal[sbal_idx]->element[sbale_idx];
}
-/* locks: must only be called with zfcp_data.config_sema taken */
-void
-zfcp_qdio_free_queues(struct zfcp_adapter *adapter)
+/**
+ * zfcp_qdio_free - free memory used by request- and resposne queue
+ * @adapter: pointer to the zfcp_adapter structure
+ */
+void zfcp_qdio_free(struct zfcp_adapter *adapter)
{
- ZFCP_LOG_TRACE("freeing request_queue buffers\n");
- zfcp_qdio_buffers_dequeue(adapter->request_queue.buffer);
+ struct qdio_buffer **sbal_req, **sbal_resp;
+ int p;
- ZFCP_LOG_TRACE("freeing response_queue buffers\n");
- zfcp_qdio_buffers_dequeue(adapter->response_queue.buffer);
-}
+ if (adapter->ccw_device)
+ qdio_free(adapter->ccw_device);
-int
-zfcp_qdio_allocate(struct zfcp_adapter *adapter)
-{
- struct qdio_initialize *init_data;
+ sbal_req = adapter->req_q.sbal;
+ sbal_resp = adapter->resp_q.sbal;
- init_data = &adapter->qdio_init_data;
+ for (p = 0; p < QDIO_MAX_BUFFERS_PER_Q; p += QBUFF_PER_PAGE) {
+ free_page((unsigned long) sbal_req[p]);
+ free_page((unsigned long) sbal_resp[p]);
+ }
+}
- init_data->cdev = adapter->ccw_device;
- init_data->q_format = QDIO_SCSI_QFMT;
- memcpy(init_data->adapter_name, zfcp_get_busid_by_adapter(adapter), 8);
- ASCEBC(init_data->adapter_name, 8);
- init_data->qib_param_field_format = 0;
- init_data->qib_param_field = NULL;
- init_data->input_slib_elements = NULL;
- init_data->output_slib_elements = NULL;
- init_data->min_input_threshold = ZFCP_MIN_INPUT_THRESHOLD;
- init_data->max_input_threshold = ZFCP_MAX_INPUT_THRESHOLD;
- init_data->min_output_threshold = ZFCP_MIN_OUTPUT_THRESHOLD;
- init_data->max_output_threshold = ZFCP_MAX_OUTPUT_THRESHOLD;
- init_data->no_input_qs = 1;
- init_data->no_output_qs = 1;
- init_data->input_handler = zfcp_qdio_response_handler;
- init_data->output_handler = zfcp_qdio_request_handler;
- init_data->int_parm = (unsigned long) adapter;
- init_data->flags = QDIO_INBOUND_0COPY_SBALS |
- QDIO_OUTBOUND_0COPY_SBALS | QDIO_USE_OUTBOUND_PCIS;
- init_data->input_sbal_addr_array =
- (void **) (adapter->response_queue.buffer);
- init_data->output_sbal_addr_array =
- (void **) (adapter->request_queue.buffer);
+static void zfcp_qdio_handler_error(struct zfcp_adapter *adapter, u8 id)
+{
+ dev_warn(&adapter->ccw_device->dev, "A QDIO problem occurred\n");
- return qdio_allocate(init_data);
+ zfcp_erp_adapter_reopen(adapter,
+ ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
+ ZFCP_STATUS_COMMON_ERP_FAILED, id, NULL);
}
-/*
- * function: zfcp_qdio_handler_error_check
- *
- * purpose: called by the response handler to determine error condition
- *
- * returns: error flag
- *
- */
-static int
-zfcp_qdio_handler_error_check(struct zfcp_adapter *adapter, unsigned int status,
- unsigned int qdio_error, unsigned int siga_error,
- int first_element, int elements_processed)
+static void zfcp_qdio_zero_sbals(struct qdio_buffer *sbal[], int first, int cnt)
{
- int retval = 0;
-
- if (unlikely(status & QDIO_STATUS_LOOK_FOR_ERROR)) {
- retval = -EIO;
-
- ZFCP_LOG_INFO("QDIO problem occurred (status=0x%x, "
- "qdio_error=0x%x, siga_error=0x%x)\n",
- status, qdio_error, siga_error);
-
- zfcp_hba_dbf_event_qdio(adapter, status, qdio_error, siga_error,
- first_element, elements_processed);
- /*
- * Restarting IO on the failed adapter from scratch.
- * Since we have been using this adapter, it is save to assume
- * that it is not failed but recoverable. The card seems to
- * report link-up events by self-initiated queue shutdown.
- * That is why we need to clear the link-down flag
- * which is set again in case we have missed by a mile.
- */
- zfcp_erp_adapter_reopen(adapter,
- ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
- ZFCP_STATUS_COMMON_ERP_FAILED, 140,
- NULL);
+ int i, sbal_idx;
+
+ for (i = first; i < first + cnt; i++) {
+ sbal_idx = i % QDIO_MAX_BUFFERS_PER_Q;
+ memset(sbal[sbal_idx], 0, sizeof(struct qdio_buffer));
}
- return retval;
}
-/*
- * function: zfcp_qdio_request_handler
- *
- * purpose: is called by QDIO layer for completed SBALs in request queue
- *
- * returns: (void)
- */
-static void
-zfcp_qdio_request_handler(struct ccw_device *ccw_device,
- unsigned int status,
- unsigned int qdio_error,
- unsigned int siga_error,
- unsigned int queue_number,
- int first_element,
- int elements_processed,
- unsigned long int_parm)
+static void zfcp_qdio_int_req(struct ccw_device *cdev, unsigned int qdio_err,
+ int queue_no, int first, int count,
+ unsigned long parm)
{
- struct zfcp_adapter *adapter;
- struct zfcp_qdio_queue *queue;
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *) parm;
+ struct zfcp_qdio_queue *queue = &adapter->req_q;
- adapter = (struct zfcp_adapter *) int_parm;
- queue = &adapter->request_queue;
-
- ZFCP_LOG_DEBUG("adapter %s, first=%d, elements_processed=%d\n",
- zfcp_get_busid_by_adapter(adapter),
- first_element, elements_processed);
-
- if (unlikely(zfcp_qdio_handler_error_check(adapter, status, qdio_error,
- siga_error, first_element,
- elements_processed)))
- goto out;
- /*
- * we stored address of struct zfcp_adapter data structure
- * associated with irq in int_parm
- */
+ if (unlikely(qdio_err)) {
+ zfcp_hba_dbf_event_qdio(adapter, qdio_err, first, count);
+ zfcp_qdio_handler_error(adapter, 140);
+ return;
+ }
/* cleanup all SBALs being program-owned now */
- zfcp_qdio_zero_sbals(queue->buffer, first_element, elements_processed);
+ zfcp_qdio_zero_sbals(queue->sbal, first, count);
- /* increase free space in outbound queue */
- atomic_add(elements_processed, &queue->free_count);
- ZFCP_LOG_DEBUG("free_count=%d\n", atomic_read(&queue->free_count));
+ atomic_add(count, &queue->count);
wake_up(&adapter->request_wq);
- ZFCP_LOG_DEBUG("elements_processed=%d, free count=%d\n",
- elements_processed, atomic_read(&queue->free_count));
- out:
- return;
}
-/**
- * zfcp_qdio_reqid_check - checks for valid reqids.
- */
static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter,
- unsigned long req_id)
+ unsigned long req_id, int sbal_idx)
{
struct zfcp_fsf_req *fsf_req;
unsigned long flags;
@@ -248,223 +108,132 @@ static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter,
* Unknown request means that we have potentially memory
* corruption and must stop the machine immediatly.
*/
- panic("error: unknown request id (%ld) on adapter %s.\n",
+ panic("error: unknown request id (%lx) on adapter %s.\n",
req_id, zfcp_get_busid_by_adapter(adapter));
zfcp_reqlist_remove(adapter, fsf_req);
- atomic_dec(&adapter->reqs_active);
spin_unlock_irqrestore(&adapter->req_list_lock, flags);
- /* finish the FSF request */
+ fsf_req->sbal_response = sbal_idx;
zfcp_fsf_req_complete(fsf_req);
}
-/*
- * function: zfcp_qdio_response_handler
- *
- * purpose: is called by QDIO layer for completed SBALs in response queue
- *
- * returns: (void)
- */
-static void
-zfcp_qdio_response_handler(struct ccw_device *ccw_device,
- unsigned int status,
- unsigned int qdio_error,
- unsigned int siga_error,
- unsigned int queue_number,
- int first_element,
- int elements_processed,
- unsigned long int_parm)
+static void zfcp_qdio_resp_put_back(struct zfcp_adapter *adapter, int processed)
{
- struct zfcp_adapter *adapter;
- struct zfcp_qdio_queue *queue;
- int buffer_index;
- int i;
- struct qdio_buffer *buffer;
- int retval = 0;
- u8 count;
- u8 start;
- volatile struct qdio_buffer_element *buffere = NULL;
- int buffere_index;
-
- adapter = (struct zfcp_adapter *) int_parm;
- queue = &adapter->response_queue;
-
- if (unlikely(zfcp_qdio_handler_error_check(adapter, status, qdio_error,
- siga_error, first_element,
- elements_processed)))
- goto out;
+ struct zfcp_qdio_queue *queue = &adapter->resp_q;
+ struct ccw_device *cdev = adapter->ccw_device;
+ u8 count, start = queue->first;
+ unsigned int retval;
- /*
- * we stored address of struct zfcp_adapter data structure
- * associated with irq in int_parm
- */
+ count = atomic_read(&queue->count) + processed;
+
+ retval = do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, start, count);
+
+ if (unlikely(retval)) {
+ atomic_set(&queue->count, count);
+ /* FIXME: Recover this with an adapter reopen? */
+ } else {
+ queue->first += count;
+ queue->first %= QDIO_MAX_BUFFERS_PER_Q;
+ atomic_set(&queue->count, 0);
+ }
+}
+
+static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err,
+ int queue_no, int first, int count,
+ unsigned long parm)
+{
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *) parm;
+ struct zfcp_qdio_queue *queue = &adapter->resp_q;
+ struct qdio_buffer_element *sbale;
+ int sbal_idx, sbale_idx, sbal_no;
+
+ if (unlikely(qdio_err)) {
+ zfcp_hba_dbf_event_qdio(adapter, qdio_err, first, count);
+ zfcp_qdio_handler_error(adapter, 147);
+ return;
+ }
- buffere = &(queue->buffer[first_element]->element[0]);
- ZFCP_LOG_DEBUG("first BUFFERE flags=0x%x\n", buffere->flags);
/*
* go through all SBALs from input queue currently
* returned by QDIO layer
*/
-
- for (i = 0; i < elements_processed; i++) {
-
- buffer_index = first_element + i;
- buffer_index %= QDIO_MAX_BUFFERS_PER_Q;
- buffer = queue->buffer[buffer_index];
+ for (sbal_no = 0; sbal_no < count; sbal_no++) {
+ sbal_idx = (first + sbal_no) % QDIO_MAX_BUFFERS_PER_Q;
/* go through all SBALEs of SBAL */
- for (buffere_index = 0;
- buffere_index < QDIO_MAX_ELEMENTS_PER_BUFFER;
- buffere_index++) {
-
- /* look for QDIO request identifiers in SB */
- buffere = &buffer->element[buffere_index];
+ for (sbale_idx = 0; sbale_idx < QDIO_MAX_ELEMENTS_PER_BUFFER;
+ sbale_idx++) {
+ sbale = zfcp_qdio_sbale(queue, sbal_idx, sbale_idx);
zfcp_qdio_reqid_check(adapter,
- (unsigned long) buffere->addr);
-
- /*
- * A single used SBALE per inbound SBALE has been
- * implemented by QDIO so far. Hope they will
- * do some optimisation. Will need to change to
- * unlikely() then.
- */
- if (likely(buffere->flags & SBAL_FLAGS_LAST_ENTRY))
+ (unsigned long) sbale->addr,
+ sbal_idx);
+ if (likely(sbale->flags & SBAL_FLAGS_LAST_ENTRY))
break;
};
- if (unlikely(!(buffere->flags & SBAL_FLAGS_LAST_ENTRY))) {
- ZFCP_LOG_NORMAL("bug: End of inbound data "
- "not marked!\n");
- }
+ if (unlikely(!(sbale->flags & SBAL_FLAGS_LAST_ENTRY)))
+ dev_warn(&adapter->ccw_device->dev,
+ "A QDIO protocol error occurred, "
+ "operations continue\n");
}
/*
* put range of SBALs back to response queue
* (including SBALs which have already been free before)
*/
- count = atomic_read(&queue->free_count) + elements_processed;
- start = queue->free_index;
-
- ZFCP_LOG_TRACE("calling do_QDIO on adapter %s (flags=0x%x, "
- "queue_no=%i, index_in_queue=%i, count=%i, "
- "buffers=0x%lx\n",
- zfcp_get_busid_by_adapter(adapter),
- QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
- 0, start, count, (unsigned long) &queue->buffer[start]);
-
- retval = do_QDIO(ccw_device,
- QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
- 0, start, count, NULL);
-
- if (unlikely(retval)) {
- atomic_set(&queue->free_count, count);
- ZFCP_LOG_DEBUG("clearing of inbound data regions failed, "
- "queues may be down "
- "(count=%d, start=%d, retval=%d)\n",
- count, start, retval);
- } else {
- queue->free_index += count;
- queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;
- atomic_set(&queue->free_count, 0);
- ZFCP_LOG_TRACE("%i buffers enqueued to response "
- "queue at position %i\n", count, start);
- }
- out:
- return;
+ zfcp_qdio_resp_put_back(adapter, count);
}
/**
- * zfcp_qdio_sbale_get - return pointer to SBALE of qdio_queue
- * @queue: queue from which SBALE should be returned
- * @sbal: specifies number of SBAL in queue
- * @sbale: specifes number of SBALE in SBAL
+ * zfcp_qdio_sbale_req - return ptr to SBALE of req_q for a struct zfcp_fsf_req
+ * @fsf_req: pointer to struct fsf_req
+ * Returns: pointer to qdio_buffer_element (SBALE) structure
*/
-static inline volatile struct qdio_buffer_element *
-zfcp_qdio_sbale_get(struct zfcp_qdio_queue *queue, int sbal, int sbale)
+struct qdio_buffer_element *zfcp_qdio_sbale_req(struct zfcp_fsf_req *req)
{
- return &queue->buffer[sbal]->element[sbale];
+ return zfcp_qdio_sbale(&req->adapter->req_q, req->sbal_last, 0);
}
/**
- * zfcp_qdio_sbale_req - return pointer to SBALE of request_queue for
- * a struct zfcp_fsf_req
+ * zfcp_qdio_sbale_curr - return curr SBALE on req_q for a struct zfcp_fsf_req
+ * @fsf_req: pointer to struct fsf_req
+ * Returns: pointer to qdio_buffer_element (SBALE) structure
*/
-volatile struct qdio_buffer_element *
-zfcp_qdio_sbale_req(struct zfcp_fsf_req *fsf_req, int sbal, int sbale)
+struct qdio_buffer_element *zfcp_qdio_sbale_curr(struct zfcp_fsf_req *req)
{
- return zfcp_qdio_sbale_get(&fsf_req->adapter->request_queue,
- sbal, sbale);
+ return zfcp_qdio_sbale(&req->adapter->req_q, req->sbal_last,
+ req->sbale_curr);
}
-/**
- * zfcp_qdio_sbale_resp - return pointer to SBALE of response_queue for
- * a struct zfcp_fsf_req
- */
-static inline volatile struct qdio_buffer_element *
-zfcp_qdio_sbale_resp(struct zfcp_fsf_req *fsf_req, int sbal, int sbale)
+static void zfcp_qdio_sbal_limit(struct zfcp_fsf_req *fsf_req, int max_sbals)
{
- return zfcp_qdio_sbale_get(&fsf_req->adapter->response_queue,
- sbal, sbale);
-}
-
-/**
- * zfcp_qdio_sbale_curr - return current SBALE on request_queue for
- * a struct zfcp_fsf_req
- */
-volatile struct qdio_buffer_element *
-zfcp_qdio_sbale_curr(struct zfcp_fsf_req *fsf_req)
-{
- return zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr,
- fsf_req->sbale_curr);
-}
-
-/**
- * zfcp_qdio_sbal_limit - determine maximum number of SBALs that can be used
- * on the request_queue for a struct zfcp_fsf_req
- * @fsf_req: the number of the last SBAL that can be used is stored herein
- * @max_sbals: used to pass an upper limit for the number of SBALs
- *
- * Note: We can assume at least one free SBAL in the request_queue when called.
- */
-static void
-zfcp_qdio_sbal_limit(struct zfcp_fsf_req *fsf_req, int max_sbals)
-{
- int count = atomic_read(&fsf_req->adapter->request_queue.free_count);
+ int count = atomic_read(&fsf_req->adapter->req_q.count);
count = min(count, max_sbals);
- fsf_req->sbal_last = fsf_req->sbal_first;
- fsf_req->sbal_last += (count - 1);
- fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
+ fsf_req->sbal_limit = (fsf_req->sbal_first + count - 1)
+ % QDIO_MAX_BUFFERS_PER_Q;
}
-/**
- * zfcp_qdio_sbal_chain - chain SBALs if more than one SBAL is needed for a
- * request
- * @fsf_req: zfcp_fsf_req to be processed
- * @sbtype: SBAL flags which have to be set in first SBALE of new SBAL
- *
- * This function changes sbal_curr, sbale_curr, sbal_number of fsf_req.
- */
-static volatile struct qdio_buffer_element *
+static struct qdio_buffer_element *
zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
{
- volatile struct qdio_buffer_element *sbale;
+ struct qdio_buffer_element *sbale;
/* set last entry flag in current SBALE of current SBAL */
sbale = zfcp_qdio_sbale_curr(fsf_req);
sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
/* don't exceed last allowed SBAL */
- if (fsf_req->sbal_curr == fsf_req->sbal_last)
+ if (fsf_req->sbal_last == fsf_req->sbal_limit)
return NULL;
/* set chaining flag in first SBALE of current SBAL */
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req);
sbale->flags |= SBAL_FLAGS0_MORE_SBALS;
/* calculate index of next SBAL */
- fsf_req->sbal_curr++;
- fsf_req->sbal_curr %= QDIO_MAX_BUFFERS_PER_Q;
+ fsf_req->sbal_last++;
+ fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
/* keep this requests number of SBALs up-to-date */
fsf_req->sbal_number++;
@@ -479,214 +248,241 @@ zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
return sbale;
}
-/**
- * zfcp_qdio_sbale_next - switch to next SBALE, chain SBALs if needed
- */
-static volatile struct qdio_buffer_element *
+static struct qdio_buffer_element *
zfcp_qdio_sbale_next(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
{
if (fsf_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL)
return zfcp_qdio_sbal_chain(fsf_req, sbtype);
-
fsf_req->sbale_curr++;
-
return zfcp_qdio_sbale_curr(fsf_req);
}
-/**
- * zfcp_qdio_sbals_zero - initialize SBALs between first and last in queue
- * with zero from
- */
-static int
-zfcp_qdio_sbals_zero(struct zfcp_qdio_queue *queue, int first, int last)
-{
- struct qdio_buffer **buf = queue->buffer;
- int curr = first;
- int count = 0;
-
- for(;;) {
- curr %= QDIO_MAX_BUFFERS_PER_Q;
- count++;
- memset(buf[curr], 0, sizeof(struct qdio_buffer));
- if (curr == last)
- break;
- curr++;
- }
- return count;
-}
-
-
-/**
- * zfcp_qdio_sbals_wipe - reset all changes in SBALs for an fsf_req
- */
-static inline int
-zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *fsf_req)
-{
- return zfcp_qdio_sbals_zero(&fsf_req->adapter->request_queue,
- fsf_req->sbal_first, fsf_req->sbal_curr);
-}
-
-
-/**
- * zfcp_qdio_sbale_fill - set address and length in current SBALE
- * on request_queue
- */
-static void
-zfcp_qdio_sbale_fill(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
- void *addr, int length)
+static void zfcp_qdio_undo_sbals(struct zfcp_fsf_req *fsf_req)
{
- volatile struct qdio_buffer_element *sbale;
-
- sbale = zfcp_qdio_sbale_curr(fsf_req);
- sbale->addr = addr;
- sbale->length = length;
+ struct qdio_buffer **sbal = fsf_req->adapter->req_q.sbal;
+ int first = fsf_req->sbal_first;
+ int last = fsf_req->sbal_last;
+ int count = (last - first + QDIO_MAX_BUFFERS_PER_Q) %
+ QDIO_MAX_BUFFERS_PER_Q + 1;
+ zfcp_qdio_zero_sbals(sbal, first, count);
}
-/**
- * zfcp_qdio_sbals_from_segment - map memory segment to SBALE(s)
- * @fsf_req: request to be processed
- * @sbtype: SBALE flags
- * @start_addr: address of memory segment
- * @total_length: length of memory segment
- *
- * Alignment and length of the segment determine how many SBALEs are needed
- * for the memory segment.
- */
-static int
-zfcp_qdio_sbals_from_segment(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
- void *start_addr, unsigned long total_length)
+static int zfcp_qdio_fill_sbals(struct zfcp_fsf_req *fsf_req,
+ unsigned int sbtype, void *start_addr,
+ unsigned int total_length)
{
+ struct qdio_buffer_element *sbale;
unsigned long remaining, length;
void *addr;
- /* split segment up heeding page boundaries */
+ /* split segment up */
for (addr = start_addr, remaining = total_length; remaining > 0;
addr += length, remaining -= length) {
- /* get next free SBALE for new piece */
- if (NULL == zfcp_qdio_sbale_next(fsf_req, sbtype)) {
- /* no SBALE left, clean up and leave */
- zfcp_qdio_sbals_wipe(fsf_req);
+ sbale = zfcp_qdio_sbale_next(fsf_req, sbtype);
+ if (!sbale) {
+ atomic_inc(&fsf_req->adapter->qdio_outb_full);
+ zfcp_qdio_undo_sbals(fsf_req);
return -EINVAL;
}
- /* calculate length of new piece */
+
+ /* new piece must not exceed next page boundary */
length = min(remaining,
- (PAGE_SIZE - ((unsigned long) addr &
+ (PAGE_SIZE - ((unsigned long)addr &
(PAGE_SIZE - 1))));
- /* fill current SBALE with calculated piece */
- zfcp_qdio_sbale_fill(fsf_req, sbtype, addr, length);
+ sbale->addr = addr;
+ sbale->length = length;
}
- return total_length;
+ return 0;
}
-
/**
* zfcp_qdio_sbals_from_sg - fill SBALs from scatter-gather list
* @fsf_req: request to be processed
* @sbtype: SBALE flags
* @sg: scatter-gather list
- * @sg_count: number of elements in scatter-gather list
* @max_sbals: upper bound for number of SBALs to be used
+ * Returns: number of bytes, or error (negativ)
*/
-int
-zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
- struct scatterlist *sgl, int sg_count, int max_sbals)
+int zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
+ struct scatterlist *sg, int max_sbals)
{
- int sg_index;
- struct scatterlist *sg_segment;
- int retval;
- volatile struct qdio_buffer_element *sbale;
- int bytes = 0;
+ struct qdio_buffer_element *sbale;
+ int retval, bytes = 0;
/* figure out last allowed SBAL */
zfcp_qdio_sbal_limit(fsf_req, max_sbals);
- /* set storage-block type for current SBAL */
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ /* set storage-block type for this request */
+ sbale = zfcp_qdio_sbale_req(fsf_req);
sbale->flags |= sbtype;
- /* process all segements of scatter-gather list */
- for_each_sg(sgl, sg_segment, sg_count, sg_index) {
- retval = zfcp_qdio_sbals_from_segment(
- fsf_req,
- sbtype,
- zfcp_sg_to_address(sg_segment),
- sg_segment->length);
- if (retval < 0) {
- bytes = retval;
- goto out;
- } else
- bytes += retval;
+ for (; sg; sg = sg_next(sg)) {
+ retval = zfcp_qdio_fill_sbals(fsf_req, sbtype, sg_virt(sg),
+ sg->length);
+ if (retval < 0)
+ return retval;
+ bytes += sg->length;
}
+
/* assume that no other SBALEs are to follow in the same SBAL */
sbale = zfcp_qdio_sbale_curr(fsf_req);
sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
-out:
+
return bytes;
}
+/**
+ * zfcp_qdio_send - set PCI flag in first SBALE and send req to QDIO
+ * @fsf_req: pointer to struct zfcp_fsf_req
+ * Returns: 0 on success, error otherwise
+ */
+int zfcp_qdio_send(struct zfcp_fsf_req *fsf_req)
+{
+ struct zfcp_adapter *adapter = fsf_req->adapter;
+ struct zfcp_qdio_queue *req_q = &adapter->req_q;
+ int first = fsf_req->sbal_first;
+ int count = fsf_req->sbal_number;
+ int retval, pci, pci_batch;
+ struct qdio_buffer_element *sbale;
+
+ /* acknowledgements for transferred buffers */
+ pci_batch = adapter->req_q_pci_batch + count;
+ if (unlikely(pci_batch >= ZFCP_QDIO_PCI_INTERVAL)) {
+ pci_batch %= ZFCP_QDIO_PCI_INTERVAL;
+ pci = first + count - (pci_batch + 1);
+ pci %= QDIO_MAX_BUFFERS_PER_Q;
+ sbale = zfcp_qdio_sbale(req_q, pci, 0);
+ sbale->flags |= SBAL_FLAGS0_PCI;
+ }
+
+ retval = do_QDIO(adapter->ccw_device, QDIO_FLAG_SYNC_OUTPUT, 0, first,
+ count);
+ if (unlikely(retval)) {
+ zfcp_qdio_zero_sbals(req_q->sbal, first, count);
+ return retval;
+ }
+
+ /* account for transferred buffers */
+ atomic_sub(count, &req_q->count);
+ req_q->first += count;
+ req_q->first %= QDIO_MAX_BUFFERS_PER_Q;
+ adapter->req_q_pci_batch = pci_batch;
+ return 0;
+}
/**
- * zfcp_qdio_sbals_from_scsicmnd - fill SBALs from scsi command
- * @fsf_req: request to be processed
- * @sbtype: SBALE flags
- * @scsi_cmnd: either scatter-gather list or buffer contained herein is used
- * to fill SBALs
+ * zfcp_qdio_allocate - allocate queue memory and initialize QDIO data
+ * @adapter: pointer to struct zfcp_adapter
+ * Returns: -ENOMEM on memory allocation error or return value from
+ * qdio_allocate
*/
-int
-zfcp_qdio_sbals_from_scsicmnd(struct zfcp_fsf_req *fsf_req,
- unsigned long sbtype, struct scsi_cmnd *scsi_cmnd)
+int zfcp_qdio_allocate(struct zfcp_adapter *adapter)
{
- return zfcp_qdio_sbals_from_sg(fsf_req, sbtype, scsi_sglist(scsi_cmnd),
- scsi_sg_count(scsi_cmnd),
- ZFCP_MAX_SBALS_PER_REQ);
+ struct qdio_initialize *init_data;
+
+ if (zfcp_qdio_buffers_enqueue(adapter->req_q.sbal) ||
+ zfcp_qdio_buffers_enqueue(adapter->resp_q.sbal))
+ return -ENOMEM;
+
+ init_data = &adapter->qdio_init_data;
+
+ init_data->cdev = adapter->ccw_device;
+ init_data->q_format = QDIO_ZFCP_QFMT;
+ memcpy(init_data->adapter_name, zfcp_get_busid_by_adapter(adapter), 8);
+ ASCEBC(init_data->adapter_name, 8);
+ init_data->qib_param_field_format = 0;
+ init_data->qib_param_field = NULL;
+ init_data->input_slib_elements = NULL;
+ init_data->output_slib_elements = NULL;
+ init_data->no_input_qs = 1;
+ init_data->no_output_qs = 1;
+ init_data->input_handler = zfcp_qdio_int_resp;
+ init_data->output_handler = zfcp_qdio_int_req;
+ init_data->int_parm = (unsigned long) adapter;
+ init_data->flags = QDIO_INBOUND_0COPY_SBALS |
+ QDIO_OUTBOUND_0COPY_SBALS | QDIO_USE_OUTBOUND_PCIS;
+ init_data->input_sbal_addr_array =
+ (void **) (adapter->resp_q.sbal);
+ init_data->output_sbal_addr_array =
+ (void **) (adapter->req_q.sbal);
+
+ return qdio_allocate(init_data);
}
/**
- * zfcp_qdio_determine_pci - set PCI flag in first SBALE on qdio queue if needed
+ * zfcp_close_qdio - close qdio queues for an adapter
*/
-int
-zfcp_qdio_determine_pci(struct zfcp_qdio_queue *req_queue,
- struct zfcp_fsf_req *fsf_req)
+void zfcp_qdio_close(struct zfcp_adapter *adapter)
{
- int new_distance_from_int;
- int pci_pos;
- volatile struct qdio_buffer_element *sbale;
-
- new_distance_from_int = req_queue->distance_from_int +
- fsf_req->sbal_number;
-
- if (unlikely(new_distance_from_int >= ZFCP_QDIO_PCI_INTERVAL)) {
- new_distance_from_int %= ZFCP_QDIO_PCI_INTERVAL;
- pci_pos = fsf_req->sbal_first;
- pci_pos += fsf_req->sbal_number;
- pci_pos -= new_distance_from_int;
- pci_pos -= 1;
- pci_pos %= QDIO_MAX_BUFFERS_PER_Q;
- sbale = zfcp_qdio_sbale_req(fsf_req, pci_pos, 0);
- sbale->flags |= SBAL_FLAGS0_PCI;
+ struct zfcp_qdio_queue *req_q;
+ int first, count;
+
+ if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))
+ return;
+
+ /* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */
+ req_q = &adapter->req_q;
+ spin_lock_bh(&adapter->req_q_lock);
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
+ spin_unlock_bh(&adapter->req_q_lock);
+
+ qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR);
+
+ /* cleanup used outbound sbals */
+ count = atomic_read(&req_q->count);
+ if (count < QDIO_MAX_BUFFERS_PER_Q) {
+ first = (req_q->first + count) % QDIO_MAX_BUFFERS_PER_Q;
+ count = QDIO_MAX_BUFFERS_PER_Q - count;
+ zfcp_qdio_zero_sbals(req_q->sbal, first, count);
}
- return new_distance_from_int;
+ req_q->first = 0;
+ atomic_set(&req_q->count, 0);
+ adapter->req_q_pci_batch = 0;
+ adapter->resp_q.first = 0;
+ atomic_set(&adapter->resp_q.count, 0);
}
-/*
- * function: zfcp_zero_sbals
- *
- * purpose: zeros specified range of SBALs
- *
- * returns:
+/**
+ * zfcp_qdio_open - prepare and initialize response queue
+ * @adapter: pointer to struct zfcp_adapter
+ * Returns: 0 on success, otherwise -EIO
*/
-void
-zfcp_qdio_zero_sbals(struct qdio_buffer *buf[], int first, int clean_count)
+int zfcp_qdio_open(struct zfcp_adapter *adapter)
{
- int cur_pos;
- int index;
-
- for (cur_pos = first; cur_pos < (first + clean_count); cur_pos++) {
- index = cur_pos % QDIO_MAX_BUFFERS_PER_Q;
- memset(buf[index], 0, sizeof (struct qdio_buffer));
- ZFCP_LOG_TRACE("zeroing BUFFER %d at address %p\n",
- index, buf[index]);
+ struct qdio_buffer_element *sbale;
+ int cc;
+
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)
+ return -EIO;
+
+ if (qdio_establish(&adapter->qdio_init_data))
+ goto failed_establish;
+
+ if (qdio_activate(adapter->ccw_device))
+ goto failed_qdio;
+
+ for (cc = 0; cc < QDIO_MAX_BUFFERS_PER_Q; cc++) {
+ sbale = &(adapter->resp_q.sbal[cc]->element[0]);
+ sbale->length = 0;
+ sbale->flags = SBAL_FLAGS_LAST_ENTRY;
+ sbale->addr = NULL;
}
-}
-#undef ZFCP_LOG_AREA
+ if (do_QDIO(adapter->ccw_device, QDIO_FLAG_SYNC_INPUT, 0, 0,
+ QDIO_MAX_BUFFERS_PER_Q))
+ goto failed_qdio;
+
+ /* set index of first avalable SBALS / number of available SBALS */
+ adapter->req_q.first = 0;
+ atomic_set(&adapter->req_q.count, QDIO_MAX_BUFFERS_PER_Q);
+ adapter->req_q_pci_batch = 0;
+
+ return 0;
+
+failed_qdio:
+ qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR);
+failed_establish:
+ dev_err(&adapter->ccw_device->dev,
+ "Setting up the QDIO connection to the FCP adapter failed\n");
+ return -EIO;
+}
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 01687559dc0..ca8f85f3dad 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -1,220 +1,51 @@
/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
*
- * (C) Copyright IBM Corp. 2002, 2006
+ * Interface to Linux SCSI midlayer.
*
- * 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 Corporation 2002, 2008
*/
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_SCSI
-
#include "zfcp_ext.h"
#include <asm/atomic.h>
-static void zfcp_scsi_slave_destroy(struct scsi_device *sdp);
-static int zfcp_scsi_slave_alloc(struct scsi_device *sdp);
-static int zfcp_scsi_slave_configure(struct scsi_device *sdp);
-static int zfcp_scsi_queuecommand(struct scsi_cmnd *,
- void (*done) (struct scsi_cmnd *));
-static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *);
-static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *);
-static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *);
-static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *);
-static int zfcp_task_management_function(struct zfcp_unit *, u8,
- struct scsi_cmnd *);
-
-static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int,
- unsigned int, unsigned int);
-
-static struct device_attribute *zfcp_sysfs_sdev_attrs[];
-static struct device_attribute *zfcp_a_stats_attrs[];
-
-struct zfcp_data zfcp_data = {
- .scsi_host_template = {
- .name = ZFCP_NAME,
- .module = THIS_MODULE,
- .proc_name = "zfcp",
- .slave_alloc = zfcp_scsi_slave_alloc,
- .slave_configure = zfcp_scsi_slave_configure,
- .slave_destroy = zfcp_scsi_slave_destroy,
- .queuecommand = zfcp_scsi_queuecommand,
- .eh_abort_handler = zfcp_scsi_eh_abort_handler,
- .eh_device_reset_handler = zfcp_scsi_eh_device_reset_handler,
- .eh_target_reset_handler = zfcp_scsi_eh_target_reset_handler,
- .eh_host_reset_handler = zfcp_scsi_eh_host_reset_handler,
- .can_queue = 4096,
- .this_id = -1,
- .sg_tablesize = ZFCP_MAX_SBALES_PER_REQ,
- .cmd_per_lun = 1,
- .use_clustering = 1,
- .sdev_attrs = zfcp_sysfs_sdev_attrs,
- .max_sectors = ZFCP_MAX_SECTORS,
- .shost_attrs = zfcp_a_stats_attrs,
- },
- .driver_version = ZFCP_VERSION,
-};
-
-/* Find start of Response Information in FCP response unit*/
-char *
-zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
-{
- char *fcp_rsp_info_ptr;
-
- fcp_rsp_info_ptr =
- (unsigned char *) fcp_rsp_iu + (sizeof (struct fcp_rsp_iu));
-
- return fcp_rsp_info_ptr;
-}
-
/* Find start of Sense Information in FCP response unit*/
-char *
-zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
+char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
{
char *fcp_sns_info_ptr;
- fcp_sns_info_ptr =
- (unsigned char *) fcp_rsp_iu + (sizeof (struct fcp_rsp_iu));
+ fcp_sns_info_ptr = (unsigned char *) &fcp_rsp_iu[1];
if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)
- fcp_sns_info_ptr = (char *) fcp_sns_info_ptr +
- fcp_rsp_iu->fcp_rsp_len;
+ fcp_sns_info_ptr += fcp_rsp_iu->fcp_rsp_len;
return fcp_sns_info_ptr;
}
-static fcp_dl_t *
-zfcp_get_fcp_dl_ptr(struct fcp_cmnd_iu * fcp_cmd)
-{
- int additional_length = fcp_cmd->add_fcp_cdb_length << 2;
- fcp_dl_t *fcp_dl_addr;
-
- fcp_dl_addr = (fcp_dl_t *)
- ((unsigned char *) fcp_cmd +
- sizeof (struct fcp_cmnd_iu) + additional_length);
- /*
- * fcp_dl_addr = start address of fcp_cmnd structure +
- * size of fixed part + size of dynamically sized add_dcp_cdb field
- * SEE FCP-2 documentation
- */
- return fcp_dl_addr;
-}
-
-fcp_dl_t
-zfcp_get_fcp_dl(struct fcp_cmnd_iu * fcp_cmd)
-{
- return *zfcp_get_fcp_dl_ptr(fcp_cmd);
-}
-
-void
-zfcp_set_fcp_dl(struct fcp_cmnd_iu *fcp_cmd, fcp_dl_t fcp_dl)
-{
- *zfcp_get_fcp_dl_ptr(fcp_cmd) = fcp_dl;
-}
-
-/*
- * note: it's a bit-or operation not an assignment
- * regarding the specified byte
- */
-static inline void
-set_byte(int *result, char status, char pos)
-{
- *result |= status << (pos * 8);
-}
-
-void
-set_host_byte(int *result, char status)
-{
- set_byte(result, status, 2);
-}
-
-void
-set_driver_byte(int *result, char status)
-{
- set_byte(result, status, 3);
-}
-
-static int
-zfcp_scsi_slave_alloc(struct scsi_device *sdp)
-{
- struct zfcp_adapter *adapter;
- struct zfcp_unit *unit;
- unsigned long flags;
- int retval = -ENXIO;
-
- adapter = (struct zfcp_adapter *) sdp->host->hostdata[0];
- if (!adapter)
- goto out;
-
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- unit = zfcp_unit_lookup(adapter, sdp->channel, sdp->id, sdp->lun);
- if (unit && atomic_test_mask(ZFCP_STATUS_UNIT_REGISTERED,
- &unit->status)) {
- sdp->hostdata = unit;
- unit->device = sdp;
- zfcp_unit_get(unit);
- retval = 0;
- }
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
- out:
- return retval;
-}
-
-/**
- * zfcp_scsi_slave_destroy - called when scsi device is removed
- *
- * Remove reference to associated scsi device for an zfcp_unit.
- * Mark zfcp_unit as failed. The scsi device might be deleted via sysfs
- * or a scan for this device might have failed.
- */
static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
{
struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
-
+ WARN_ON(!unit);
if (unit) {
atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status);
sdpnt->hostdata = NULL;
unit->device = NULL;
zfcp_erp_unit_failed(unit, 12, NULL);
zfcp_unit_put(unit);
- } else
- ZFCP_LOG_NORMAL("bug: no unit associated with SCSI device at "
- "address %p\n", sdpnt);
+ }
}
-/*
- * called from scsi midlayer to allow finetuning of a device.
- */
-static int
-zfcp_scsi_slave_configure(struct scsi_device *sdp)
+static int zfcp_scsi_slave_configure(struct scsi_device *sdp)
{
if (sdp->tagged_supported)
- scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, ZFCP_CMND_PER_LUN);
+ scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, 32);
else
scsi_adjust_queue_depth(sdp, 0, 1);
return 0;
}
-/**
- * zfcp_scsi_command_fail - set result in scsi_cmnd and call scsi_done function
- * @scpnt: pointer to struct scsi_cmnd where result is set
- * @result: result to be set in scpnt (e.g. DID_ERROR)
- */
-static void
-zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result)
+static void zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result)
{
- set_host_byte(&scpnt->result, result);
+ set_host_byte(scpnt, result);
if ((scpnt->device != NULL) && (scpnt->device->host != NULL))
zfcp_scsi_dbf_event_result("fail", 4,
(struct zfcp_adapter*) scpnt->device->host->hostdata[0],
@@ -223,163 +54,98 @@ zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result)
scpnt->scsi_done(scpnt);
}
-/**
- * zfcp_scsi_command_async - worker for zfcp_scsi_queuecommand and
- * zfcp_scsi_command_sync
- * @adapter: adapter where scsi command is issued
- * @unit: unit to which scsi command is sent
- * @scpnt: scsi command to be sent
- * @timer: timer to be started if request is successfully initiated
- *
- * Note: In scsi_done function must be set in scpnt.
- */
-int
-zfcp_scsi_command_async(struct zfcp_adapter *adapter, struct zfcp_unit *unit,
- struct scsi_cmnd *scpnt, int use_timer)
+static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
+ void (*done) (struct scsi_cmnd *))
{
- int tmp;
- int retval;
+ struct zfcp_unit *unit;
+ struct zfcp_adapter *adapter;
+ int status;
+ int ret;
- retval = 0;
+ /* reset the status for this request */
+ scpnt->result = 0;
+ scpnt->host_scribble = NULL;
+ scpnt->scsi_done = done;
+
+ /*
+ * figure out adapter and target device
+ * (stored there by zfcp_scsi_slave_alloc)
+ */
+ adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
+ unit = scpnt->device->hostdata;
- BUG_ON((adapter == NULL) || (adapter != unit->port->adapter));
- BUG_ON(scpnt->scsi_done == NULL);
+ BUG_ON(!adapter || (adapter != unit->port->adapter));
+ BUG_ON(!scpnt->scsi_done);
- if (unlikely(NULL == unit)) {
+ if (unlikely(!unit)) {
zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT);
- goto out;
+ return 0;
}
- if (unlikely(
- atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status) ||
- !atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status))) {
- ZFCP_LOG_DEBUG("stopping SCSI I/O on unit 0x%016Lx on port "
- "0x%016Lx on adapter %s\n",
- unit->fcp_lun, unit->port->wwpn,
- zfcp_get_busid_by_adapter(adapter));
+ status = atomic_read(&unit->status);
+ if (unlikely((status & ZFCP_STATUS_COMMON_ERP_FAILED) ||
+ !(status & ZFCP_STATUS_COMMON_RUNNING))) {
zfcp_scsi_command_fail(scpnt, DID_ERROR);
- goto out;
+ return 0;;
}
- tmp = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, use_timer,
+ ret = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, 0,
ZFCP_REQ_AUTO_CLEANUP);
- if (unlikely(tmp == -EBUSY)) {
- ZFCP_LOG_DEBUG("adapter %s not ready or unit 0x%016Lx "
- "on port 0x%016Lx in recovery\n",
- zfcp_get_busid_by_unit(unit),
- unit->fcp_lun, unit->port->wwpn);
+ if (unlikely(ret == -EBUSY))
zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT);
- goto out;
- }
-
- if (unlikely(tmp < 0)) {
- ZFCP_LOG_DEBUG("error: initiation of Send FCP Cmnd failed\n");
- retval = SCSI_MLQUEUE_HOST_BUSY;
- }
+ else if (unlikely(ret < 0))
+ return SCSI_MLQUEUE_HOST_BUSY;
-out:
- return retval;
+ return ret;
}
-static void
-zfcp_scsi_command_sync_handler(struct scsi_cmnd *scpnt)
+static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *adapter,
+ int channel, unsigned int id,
+ unsigned int lun)
{
- struct completion *wait = (struct completion *) scpnt->SCp.ptr;
- complete(wait);
-}
-
-
-/**
- * zfcp_scsi_command_sync - send a SCSI command and wait for completion
- * @unit: unit where command is sent to
- * @scpnt: scsi command to be sent
- * @use_timer: indicates whether timer should be setup or not
- * Return: 0
- *
- * Errors are indicated in scpnt->result
- */
-int
-zfcp_scsi_command_sync(struct zfcp_unit *unit, struct scsi_cmnd *scpnt,
- int use_timer)
-{
- int ret;
- DECLARE_COMPLETION_ONSTACK(wait);
-
- scpnt->SCp.ptr = (void *) &wait; /* silent re-use */
- scpnt->scsi_done = zfcp_scsi_command_sync_handler;
- ret = zfcp_scsi_command_async(unit->port->adapter, unit, scpnt,
- use_timer);
- if (ret == 0)
- wait_for_completion(&wait);
+ struct zfcp_port *port;
+ struct zfcp_unit *unit;
+ int scsi_lun;
- scpnt->SCp.ptr = NULL;
+ list_for_each_entry(port, &adapter->port_list_head, list) {
+ if (!port->rport || (id != port->rport->scsi_target_id))
+ continue;
+ list_for_each_entry(unit, &port->unit_list_head, list) {
+ scsi_lun = scsilun_to_int(
+ (struct scsi_lun *)&unit->fcp_lun);
+ if (lun == scsi_lun)
+ return unit;
+ }
+ }
- return 0;
+ return NULL;
}
-/*
- * function: zfcp_scsi_queuecommand
- *
- * purpose: enqueues a SCSI command to the specified target device
- *
- * returns: 0 - success, SCSI command enqueued
- * !0 - failure
- */
-static int
-zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
- void (*done) (struct scsi_cmnd *))
+static int zfcp_scsi_slave_alloc(struct scsi_device *sdp)
{
- struct zfcp_unit *unit;
struct zfcp_adapter *adapter;
+ struct zfcp_unit *unit;
+ unsigned long flags;
+ int retval = -ENXIO;
- /* reset the status for this request */
- scpnt->result = 0;
- scpnt->host_scribble = NULL;
- scpnt->scsi_done = done;
-
- /*
- * figure out adapter and target device
- * (stored there by zfcp_scsi_slave_alloc)
- */
- adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
- unit = (struct zfcp_unit *) scpnt->device->hostdata;
-
- return zfcp_scsi_command_async(adapter, unit, scpnt, 0);
-}
-
-static struct zfcp_unit *
-zfcp_unit_lookup(struct zfcp_adapter *adapter, int channel, unsigned int id,
- unsigned int lun)
-{
- struct zfcp_port *port;
- struct zfcp_unit *unit, *retval = NULL;
+ adapter = (struct zfcp_adapter *) sdp->host->hostdata[0];
+ if (!adapter)
+ goto out;
- list_for_each_entry(port, &adapter->port_list_head, list) {
- if (!port->rport || (id != port->rport->scsi_target_id))
- continue;
- list_for_each_entry(unit, &port->unit_list_head, list)
- if (lun == unit->scsi_lun) {
- retval = unit;
- goto out;
- }
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ unit = zfcp_unit_lookup(adapter, sdp->channel, sdp->id, sdp->lun);
+ if (unit &&
+ (atomic_read(&unit->status) & ZFCP_STATUS_UNIT_REGISTERED)) {
+ sdp->hostdata = unit;
+ unit->device = sdp;
+ zfcp_unit_get(unit);
+ retval = 0;
}
- out:
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+out:
return retval;
}
-/**
- * zfcp_scsi_eh_abort_handler - abort the specified SCSI command
- * @scpnt: pointer to scsi_cmnd to be aborted
- * Return: SUCCESS - command has been aborted and cleaned up in internal
- * bookkeeping, SCSI stack won't be called for aborted command
- * FAILED - otherwise
- *
- * We do not need to care for a SCSI command which completes normally
- * but late during this abort routine runs. We are allowed to return
- * late commands to the SCSI stack. It tracks the state of commands and
- * will handle late commands. (Usually, the normal completion of late
- * commands is ignored with respect to the running abort operation.)
- */
static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
{
struct Scsi_Host *scsi_host;
@@ -387,44 +153,36 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
struct zfcp_unit *unit;
struct zfcp_fsf_req *fsf_req;
unsigned long flags;
- unsigned long old_req_id;
+ unsigned long old_req_id = (unsigned long) scpnt->host_scribble;
int retval = SUCCESS;
scsi_host = scpnt->device->host;
adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
- unit = (struct zfcp_unit *) scpnt->device->hostdata;
-
- ZFCP_LOG_INFO("aborting scsi_cmnd=%p on adapter %s\n",
- scpnt, zfcp_get_busid_by_adapter(adapter));
+ unit = scpnt->device->hostdata;
/* avoid race condition between late normal completion and abort */
write_lock_irqsave(&adapter->abort_lock, flags);
/* Check whether corresponding fsf_req is still pending */
spin_lock(&adapter->req_list_lock);
- fsf_req = zfcp_reqlist_find(adapter,
- (unsigned long) scpnt->host_scribble);
+ fsf_req = zfcp_reqlist_find(adapter, old_req_id);
spin_unlock(&adapter->req_list_lock);
if (!fsf_req) {
write_unlock_irqrestore(&adapter->abort_lock, flags);
zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, NULL, 0);
- retval = SUCCESS;
- goto out;
+ return retval;
}
- fsf_req->data = 0;
- fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTING;
- old_req_id = fsf_req->req_id;
+ fsf_req->data = NULL;
/* don't access old fsf_req after releasing the abort_lock */
write_unlock_irqrestore(&adapter->abort_lock, flags);
fsf_req = zfcp_fsf_abort_fcp_command(old_req_id, adapter, unit, 0);
if (!fsf_req) {
- ZFCP_LOG_INFO("error: initiation of Abort FCP Cmnd failed\n");
zfcp_scsi_dbf_event_abort("nres", adapter, scpnt, NULL,
old_req_id);
retval = FAILED;
- goto out;
+ return retval;
}
__wait_event(fsf_req->completion_wq,
@@ -432,66 +190,29 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) {
zfcp_scsi_dbf_event_abort("okay", adapter, scpnt, fsf_req, 0);
- retval = SUCCESS;
} else if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) {
zfcp_scsi_dbf_event_abort("lte2", adapter, scpnt, fsf_req, 0);
- retval = SUCCESS;
} else {
zfcp_scsi_dbf_event_abort("fail", adapter, scpnt, fsf_req, 0);
retval = FAILED;
}
zfcp_fsf_req_free(fsf_req);
- out:
- return retval;
-}
-static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt)
-{
- int retval;
- struct zfcp_unit *unit = scpnt->device->hostdata;
-
- if (!unit) {
- WARN_ON(1);
- return SUCCESS;
- }
- retval = zfcp_task_management_function(unit,
- FCP_LOGICAL_UNIT_RESET,
- scpnt);
- return retval ? FAILED : SUCCESS;
-}
-
-static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt)
-{
- int retval;
- struct zfcp_unit *unit = scpnt->device->hostdata;
-
- if (!unit) {
- WARN_ON(1);
- return SUCCESS;
- }
- retval = zfcp_task_management_function(unit, FCP_TARGET_RESET, scpnt);
- return retval ? FAILED : SUCCESS;
+ return retval;
}
-static int
-zfcp_task_management_function(struct zfcp_unit *unit, u8 tm_flags,
- struct scsi_cmnd *scpnt)
+static int zfcp_task_mgmt_function(struct zfcp_unit *unit, u8 tm_flags,
+ struct scsi_cmnd *scpnt)
{
struct zfcp_adapter *adapter = unit->port->adapter;
struct zfcp_fsf_req *fsf_req;
- int retval = 0;
+ int retval = SUCCESS;
/* issue task management function */
- fsf_req = zfcp_fsf_send_fcp_command_task_management
- (adapter, unit, tm_flags, 0);
+ fsf_req = zfcp_fsf_send_fcp_ctm(adapter, unit, tm_flags, 0);
if (!fsf_req) {
- ZFCP_LOG_INFO("error: creation of task management request "
- "failed for unit 0x%016Lx on port 0x%016Lx on "
- "adapter %s\n", unit->fcp_lun, unit->port->wwpn,
- zfcp_get_busid_by_adapter(adapter));
zfcp_scsi_dbf_event_devreset("nres", tm_flags, unit, scpnt);
- retval = -ENOMEM;
- goto out;
+ return FAILED;
}
__wait_event(fsf_req->completion_wq,
@@ -502,87 +223,90 @@ zfcp_task_management_function(struct zfcp_unit *unit, u8 tm_flags,
*/
if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
zfcp_scsi_dbf_event_devreset("fail", tm_flags, unit, scpnt);
- retval = -EIO;
+ retval = FAILED;
} else if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP) {
zfcp_scsi_dbf_event_devreset("nsup", tm_flags, unit, scpnt);
- retval = -ENOTSUPP;
+ retval = FAILED;
} else
zfcp_scsi_dbf_event_devreset("okay", tm_flags, unit, scpnt);
zfcp_fsf_req_free(fsf_req);
- out:
+
return retval;
}
-/**
- * zfcp_scsi_eh_host_reset_handler - handler for host reset
- */
+static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt)
+{
+ struct zfcp_unit *unit = scpnt->device->hostdata;
+
+ if (!unit) {
+ WARN_ON(1);
+ return SUCCESS;
+ }
+ return zfcp_task_mgmt_function(unit, FCP_LOGICAL_UNIT_RESET, scpnt);
+}
+
+static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt)
+{
+ struct zfcp_unit *unit = scpnt->device->hostdata;
+
+ if (!unit) {
+ WARN_ON(1);
+ return SUCCESS;
+ }
+ return zfcp_task_mgmt_function(unit, FCP_TARGET_RESET, scpnt);
+}
+
static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
{
struct zfcp_unit *unit;
struct zfcp_adapter *adapter;
- unit = (struct zfcp_unit*) scpnt->device->hostdata;
+ unit = scpnt->device->hostdata;
adapter = unit->port->adapter;
-
- ZFCP_LOG_NORMAL("host reset because of problems with "
- "unit 0x%016Lx on port 0x%016Lx, adapter %s\n",
- unit->fcp_lun, unit->port->wwpn,
- zfcp_get_busid_by_adapter(unit->port->adapter));
-
zfcp_erp_adapter_reopen(adapter, 0, 141, scpnt);
zfcp_erp_wait(adapter);
return SUCCESS;
}
-int
-zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
+int zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
{
- int retval = 0;
- static unsigned int unique_id = 0;
+ struct ccw_dev_id dev_id;
if (adapter->scsi_host)
- goto out;
+ return 0;
+ ccw_device_get_id(adapter->ccw_device, &dev_id);
/* register adapter as SCSI host with mid layer of SCSI stack */
adapter->scsi_host = scsi_host_alloc(&zfcp_data.scsi_host_template,
sizeof (struct zfcp_adapter *));
if (!adapter->scsi_host) {
- ZFCP_LOG_NORMAL("error: registration with SCSI stack failed "
- "for adapter %s ",
- zfcp_get_busid_by_adapter(adapter));
- retval = -EIO;
- goto out;
+ dev_err(&adapter->ccw_device->dev,
+ "Registering the FCP device with the "
+ "SCSI stack failed\n");
+ return -EIO;
}
- ZFCP_LOG_DEBUG("host registered, scsi_host=%p\n", adapter->scsi_host);
/* tell the SCSI stack some characteristics of this adapter */
adapter->scsi_host->max_id = 1;
adapter->scsi_host->max_lun = 1;
adapter->scsi_host->max_channel = 0;
- adapter->scsi_host->unique_id = unique_id++; /* FIXME */
- adapter->scsi_host->max_cmd_len = ZFCP_MAX_SCSI_CMND_LENGTH;
+ adapter->scsi_host->unique_id = dev_id.devno;
+ adapter->scsi_host->max_cmd_len = 255;
adapter->scsi_host->transportt = zfcp_data.scsi_transport_template;
- /*
- * save a pointer to our own adapter data structure within
- * hostdata field of SCSI host data structure
- */
adapter->scsi_host->hostdata[0] = (unsigned long) adapter;
if (scsi_add_host(adapter->scsi_host, &adapter->ccw_device->dev)) {
scsi_host_put(adapter->scsi_host);
- retval = -EIO;
- goto out;
+ return -EIO;
}
- atomic_set_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status);
- out:
- return retval;
+
+ return 0;
}
-void
-zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
+void zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
{
struct Scsi_Host *shost;
struct zfcp_port *port;
@@ -590,23 +314,21 @@ zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
shost = adapter->scsi_host;
if (!shost)
return;
+
read_lock_irq(&zfcp_data.config_lock);
list_for_each_entry(port, &adapter->port_list_head, list)
if (port->rport)
port->rport = NULL;
+
read_unlock_irq(&zfcp_data.config_lock);
fc_remove_host(shost);
scsi_remove_host(shost);
scsi_host_put(shost);
adapter->scsi_host = NULL;
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status);
return;
}
-/*
- * Support functions for FC transport class
- */
static struct fc_host_statistics*
zfcp_init_fc_host_stats(struct zfcp_adapter *adapter)
{
@@ -622,13 +344,12 @@ zfcp_init_fc_host_stats(struct zfcp_adapter *adapter)
return adapter->fc_stats;
}
-static void
-zfcp_adjust_fc_host_stats(struct fc_host_statistics *fc_stats,
- struct fsf_qtcb_bottom_port *data,
- struct fsf_qtcb_bottom_port *old)
+static void zfcp_adjust_fc_host_stats(struct fc_host_statistics *fc_stats,
+ struct fsf_qtcb_bottom_port *data,
+ struct fsf_qtcb_bottom_port *old)
{
- fc_stats->seconds_since_last_reset = data->seconds_since_last_reset -
- old->seconds_since_last_reset;
+ fc_stats->seconds_since_last_reset =
+ data->seconds_since_last_reset - old->seconds_since_last_reset;
fc_stats->tx_frames = data->tx_frames - old->tx_frames;
fc_stats->tx_words = data->tx_words - old->tx_words;
fc_stats->rx_frames = data->rx_frames - old->rx_frames;
@@ -639,26 +360,25 @@ zfcp_adjust_fc_host_stats(struct fc_host_statistics *fc_stats,
fc_stats->dumped_frames = data->dumped_frames - old->dumped_frames;
fc_stats->link_failure_count = data->link_failure - old->link_failure;
fc_stats->loss_of_sync_count = data->loss_of_sync - old->loss_of_sync;
- fc_stats->loss_of_signal_count = data->loss_of_signal -
- old->loss_of_signal;
- fc_stats->prim_seq_protocol_err_count = data->psp_error_counts -
- old->psp_error_counts;
- fc_stats->invalid_tx_word_count = data->invalid_tx_words -
- old->invalid_tx_words;
+ fc_stats->loss_of_signal_count =
+ data->loss_of_signal - old->loss_of_signal;
+ fc_stats->prim_seq_protocol_err_count =
+ data->psp_error_counts - old->psp_error_counts;
+ fc_stats->invalid_tx_word_count =
+ data->invalid_tx_words - old->invalid_tx_words;
fc_stats->invalid_crc_count = data->invalid_crcs - old->invalid_crcs;
- fc_stats->fcp_input_requests = data->input_requests -
- old->input_requests;
- fc_stats->fcp_output_requests = data->output_requests -
- old->output_requests;
- fc_stats->fcp_control_requests = data->control_requests -
- old->control_requests;
+ fc_stats->fcp_input_requests =
+ data->input_requests - old->input_requests;
+ fc_stats->fcp_output_requests =
+ data->output_requests - old->output_requests;
+ fc_stats->fcp_control_requests =
+ data->control_requests - old->control_requests;
fc_stats->fcp_input_megabytes = data->input_mb - old->input_mb;
fc_stats->fcp_output_megabytes = data->output_mb - old->output_mb;
}
-static void
-zfcp_set_fc_host_stats(struct fc_host_statistics *fc_stats,
- struct fsf_qtcb_bottom_port *data)
+static void zfcp_set_fc_host_stats(struct fc_host_statistics *fc_stats,
+ struct fsf_qtcb_bottom_port *data)
{
fc_stats->seconds_since_last_reset = data->seconds_since_last_reset;
fc_stats->tx_frames = data->tx_frames;
@@ -682,22 +402,14 @@ zfcp_set_fc_host_stats(struct fc_host_statistics *fc_stats,
fc_stats->fcp_output_megabytes = data->output_mb;
}
-/**
- * zfcp_get_fc_host_stats - provide fc_host_statistics for scsi_transport_fc
- *
- * assumption: scsi_transport_fc synchronizes calls of
- * get_fc_host_stats and reset_fc_host_stats
- * (XXX to be checked otherwise introduce locking)
- */
-static struct fc_host_statistics *
-zfcp_get_fc_host_stats(struct Scsi_Host *shost)
+static struct fc_host_statistics *zfcp_get_fc_host_stats(struct Scsi_Host *host)
{
struct zfcp_adapter *adapter;
struct fc_host_statistics *fc_stats;
struct fsf_qtcb_bottom_port *data;
int ret;
- adapter = (struct zfcp_adapter *)shost->hostdata[0];
+ adapter = (struct zfcp_adapter *)host->hostdata[0];
fc_stats = zfcp_init_fc_host_stats(adapter);
if (!fc_stats)
return NULL;
@@ -709,26 +421,25 @@ zfcp_get_fc_host_stats(struct Scsi_Host *shost)
ret = zfcp_fsf_exchange_port_data_sync(adapter, data);
if (ret) {
kfree(data);
- return NULL; /* XXX return zeroed fc_stats? */
+ return NULL;
}
if (adapter->stats_reset &&
((jiffies/HZ - adapter->stats_reset) <
- data->seconds_since_last_reset)) {
+ data->seconds_since_last_reset))
zfcp_adjust_fc_host_stats(fc_stats, data,
adapter->stats_reset_data);
- } else
+ else
zfcp_set_fc_host_stats(fc_stats, data);
kfree(data);
return fc_stats;
}
-static void
-zfcp_reset_fc_host_stats(struct Scsi_Host *shost)
+static void zfcp_reset_fc_host_stats(struct Scsi_Host *shost)
{
struct zfcp_adapter *adapter;
- struct fsf_qtcb_bottom_port *data, *old_data;
+ struct fsf_qtcb_bottom_port *data;
int ret;
adapter = (struct zfcp_adapter *)shost->hostdata[0];
@@ -737,17 +448,33 @@ zfcp_reset_fc_host_stats(struct Scsi_Host *shost)
return;
ret = zfcp_fsf_exchange_port_data_sync(adapter, data);
- if (ret) {
+ if (ret)
kfree(data);
- } else {
+ else {
adapter->stats_reset = jiffies/HZ;
- old_data = adapter->stats_reset_data;
+ kfree(adapter->stats_reset_data);
adapter->stats_reset_data = data; /* finally freed in
- adater_dequeue */
- kfree(old_data);
+ adapter_dequeue */
}
}
+static void zfcp_get_host_port_state(struct Scsi_Host *shost)
+{
+ struct zfcp_adapter *adapter =
+ (struct zfcp_adapter *)shost->hostdata[0];
+ int status = atomic_read(&adapter->status);
+
+ if ((status & ZFCP_STATUS_COMMON_RUNNING) &&
+ !(status & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED))
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ else if (status & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED)
+ fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+ else if (status & ZFCP_STATUS_COMMON_ERP_FAILED)
+ fc_host_port_state(shost) = FC_PORTSTATE_ERROR;
+ else
+ fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+}
+
static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
{
rport->dev_loss_tmo = timeout;
@@ -770,6 +497,8 @@ struct fc_function_template zfcp_transport_functions = {
.get_fc_host_stats = zfcp_get_fc_host_stats,
.reset_fc_host_stats = zfcp_reset_fc_host_stats,
.set_rport_dev_loss_tmo = zfcp_set_rport_dev_loss_tmo,
+ .get_host_port_state = zfcp_get_host_port_state,
+ .show_host_port_state = 1,
/* no functions registered for following dynamic attributes but
directly set by LLDD */
.show_host_port_type = 1,
@@ -778,149 +507,26 @@ struct fc_function_template zfcp_transport_functions = {
.disable_target_scan = 1,
};
-/**
- * ZFCP_DEFINE_SCSI_ATTR
- * @_name: name of show attribute
- * @_format: format string
- * @_value: value to print
- *
- * Generates attribute for a unit.
- */
-#define ZFCP_DEFINE_SCSI_ATTR(_name, _format, _value) \
-static ssize_t zfcp_sysfs_scsi_##_name##_show(struct device *dev, struct device_attribute *attr, \
- char *buf) \
-{ \
- struct scsi_device *sdev; \
- struct zfcp_unit *unit; \
- \
- sdev = to_scsi_device(dev); \
- unit = sdev->hostdata; \
- return sprintf(buf, _format, _value); \
-} \
- \
-static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_scsi_##_name##_show, NULL);
-
-ZFCP_DEFINE_SCSI_ATTR(hba_id, "%s\n", zfcp_get_busid_by_unit(unit));
-ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n", unit->port->wwpn);
-ZFCP_DEFINE_SCSI_ATTR(fcp_lun, "0x%016llx\n", unit->fcp_lun);
-
-static struct device_attribute *zfcp_sysfs_sdev_attrs[] = {
- &dev_attr_fcp_lun,
- &dev_attr_wwpn,
- &dev_attr_hba_id,
- NULL
-};
-
-static ssize_t zfcp_sysfs_adapter_util_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct Scsi_Host *scsi_host = dev_to_shost(dev);
- struct fsf_qtcb_bottom_port *qtcb_port;
- int retval;
- struct zfcp_adapter *adapter;
-
- adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
- if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
- return -EOPNOTSUPP;
-
- qtcb_port = kzalloc(sizeof(struct fsf_qtcb_bottom_port), GFP_KERNEL);
- if (!qtcb_port)
- return -ENOMEM;
-
- retval = zfcp_fsf_exchange_port_data_sync(adapter, qtcb_port);
- if (!retval)
- retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util,
- qtcb_port->cb_util, qtcb_port->a_util);
- kfree(qtcb_port);
- return retval;
-}
-
-static int zfcp_sysfs_adapter_ex_config(struct device *dev,
- struct fsf_statistics_info *stat_inf)
-{
- int retval;
- struct fsf_qtcb_bottom_config *qtcb_config;
- struct Scsi_Host *scsi_host = dev_to_shost(dev);
- struct zfcp_adapter *adapter;
-
- adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
- if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
- return -EOPNOTSUPP;
-
- qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config),
- GFP_KERNEL);
- if (!qtcb_config)
- return -ENOMEM;
-
- retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config);
- if (!retval)
- *stat_inf = qtcb_config->stat_info;
-
- kfree(qtcb_config);
- return retval;
-}
-
-static ssize_t zfcp_sysfs_adapter_request_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct fsf_statistics_info stat_info;
- int retval;
-
- retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info);
- if (retval)
- return retval;
-
- return sprintf(buf, "%llu %llu %llu\n",
- (unsigned long long) stat_info.input_req,
- (unsigned long long) stat_info.output_req,
- (unsigned long long) stat_info.control_req);
-}
-
-static ssize_t zfcp_sysfs_adapter_mb_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct fsf_statistics_info stat_info;
- int retval;
-
- retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info);
- if (retval)
- return retval;
-
- return sprintf(buf, "%llu %llu\n",
- (unsigned long long) stat_info.input_mb,
- (unsigned long long) stat_info.output_mb);
-}
-
-static ssize_t zfcp_sysfs_adapter_sec_active_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct fsf_statistics_info stat_info;
- int retval;
-
- retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info);
- if (retval)
- return retval;
-
- return sprintf(buf, "%llu\n",
- (unsigned long long) stat_info.seconds_act);
-}
-
-static DEVICE_ATTR(utilization, S_IRUGO, zfcp_sysfs_adapter_util_show, NULL);
-static DEVICE_ATTR(requests, S_IRUGO, zfcp_sysfs_adapter_request_show, NULL);
-static DEVICE_ATTR(megabytes, S_IRUGO, zfcp_sysfs_adapter_mb_show, NULL);
-static DEVICE_ATTR(seconds_active, S_IRUGO,
- zfcp_sysfs_adapter_sec_active_show, NULL);
-
-static struct device_attribute *zfcp_a_stats_attrs[] = {
- &dev_attr_utilization,
- &dev_attr_requests,
- &dev_attr_megabytes,
- &dev_attr_seconds_active,
- NULL
+struct zfcp_data zfcp_data = {
+ .scsi_host_template = {
+ .name = "zfcp",
+ .module = THIS_MODULE,
+ .proc_name = "zfcp",
+ .slave_alloc = zfcp_scsi_slave_alloc,
+ .slave_configure = zfcp_scsi_slave_configure,
+ .slave_destroy = zfcp_scsi_slave_destroy,
+ .queuecommand = zfcp_scsi_queuecommand,
+ .eh_abort_handler = zfcp_scsi_eh_abort_handler,
+ .eh_device_reset_handler = zfcp_scsi_eh_device_reset_handler,
+ .eh_target_reset_handler = zfcp_scsi_eh_target_reset_handler,
+ .eh_host_reset_handler = zfcp_scsi_eh_host_reset_handler,
+ .can_queue = 4096,
+ .this_id = -1,
+ .sg_tablesize = ZFCP_MAX_SBALES_PER_REQ,
+ .cmd_per_lun = 1,
+ .use_clustering = 1,
+ .sdev_attrs = zfcp_sysfs_sdev_attrs,
+ .max_sectors = (ZFCP_MAX_SBALES_PER_REQ * 8),
+ .shost_attrs = zfcp_sysfs_shost_attrs,
+ },
};
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
new file mode 100644
index 00000000000..ca9293ba176
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -0,0 +1,498 @@
+/*
+ * zfcp device driver
+ *
+ * sysfs attributes.
+ *
+ * Copyright IBM Corporation 2008
+ */
+
+#include "zfcp_ext.h"
+
+#define ZFCP_DEV_ATTR(_feat, _name, _mode, _show, _store) \
+struct device_attribute dev_attr_##_feat##_##_name = __ATTR(_name, _mode,\
+ _show, _store)
+#define ZFCP_DEFINE_ATTR(_feat_def, _feat, _name, _format, _value) \
+static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \
+ struct device_attribute *at,\
+ char *buf) \
+{ \
+ struct _feat_def *_feat = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, _format, _value); \
+} \
+static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \
+ zfcp_sysfs_##_feat##_##_name##_show, NULL);
+
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, status, "0x%08x\n",
+ atomic_read(&adapter->status));
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwnn, "0x%016llx\n",
+ (unsigned long long) adapter->peer_wwnn);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwpn, "0x%016llx\n",
+ (unsigned long long) adapter->peer_wwpn);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_d_id, "0x%06x\n",
+ adapter->peer_d_id);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, card_version, "0x%04x\n",
+ adapter->hydra_version);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, lic_version, "0x%08x\n",
+ adapter->fsf_lic_version);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, hardware_version, "0x%08x\n",
+ adapter->hardware_version);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, in_recovery, "%d\n",
+ (atomic_read(&adapter->status) &
+ ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
+
+ZFCP_DEFINE_ATTR(zfcp_port, port, status, "0x%08x\n",
+ atomic_read(&port->status));
+ZFCP_DEFINE_ATTR(zfcp_port, port, in_recovery, "%d\n",
+ (atomic_read(&port->status) &
+ ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
+ZFCP_DEFINE_ATTR(zfcp_port, port, access_denied, "%d\n",
+ (atomic_read(&port->status) &
+ ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0);
+
+ZFCP_DEFINE_ATTR(zfcp_unit, unit, status, "0x%08x\n",
+ atomic_read(&unit->status));
+ZFCP_DEFINE_ATTR(zfcp_unit, unit, in_recovery, "%d\n",
+ (atomic_read(&unit->status) &
+ ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
+ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_denied, "%d\n",
+ (atomic_read(&unit->status) &
+ ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0);
+ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_shared, "%d\n",
+ (atomic_read(&unit->status) &
+ ZFCP_STATUS_UNIT_SHARED) != 0);
+ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_readonly, "%d\n",
+ (atomic_read(&unit->status) &
+ ZFCP_STATUS_UNIT_READONLY) != 0);
+
+#define ZFCP_SYSFS_FAILED(_feat_def, _feat, _adapter, _mod_id, _reopen_id) \
+static ssize_t zfcp_sysfs_##_feat##_failed_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct _feat_def *_feat = dev_get_drvdata(dev); \
+ \
+ if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_ERP_FAILED) \
+ return sprintf(buf, "1\n"); \
+ else \
+ return sprintf(buf, "0\n"); \
+} \
+static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev, \
+ struct device_attribute *attr,\
+ const char *buf, size_t count)\
+{ \
+ struct _feat_def *_feat = dev_get_drvdata(dev); \
+ unsigned long val; \
+ int retval = 0; \
+ \
+ down(&zfcp_data.config_sema); \
+ if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_REMOVE) { \
+ retval = -EBUSY; \
+ goto out; \
+ } \
+ \
+ if (strict_strtoul(buf, 0, &val) || val != 0) { \
+ retval = -EINVAL; \
+ goto out; \
+ } \
+ \
+ zfcp_erp_modify_##_feat##_status(_feat, _mod_id, NULL, \
+ ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);\
+ zfcp_erp_##_feat##_reopen(_feat, ZFCP_STATUS_COMMON_ERP_FAILED, \
+ _reopen_id, NULL); \
+ zfcp_erp_wait(_adapter); \
+out: \
+ up(&zfcp_data.config_sema); \
+ return retval ? retval : (ssize_t) count; \
+} \
+static ZFCP_DEV_ATTR(_feat, failed, S_IWUSR | S_IRUGO, \
+ zfcp_sysfs_##_feat##_failed_show, \
+ zfcp_sysfs_##_feat##_failed_store);
+
+ZFCP_SYSFS_FAILED(zfcp_adapter, adapter, adapter, 44, 93);
+ZFCP_SYSFS_FAILED(zfcp_port, port, port->adapter, 45, 96);
+ZFCP_SYSFS_FAILED(zfcp_unit, unit, unit->port->adapter, 46, 97);
+
+static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct zfcp_adapter *adapter = dev_get_drvdata(dev);
+ int ret;
+
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE)
+ return -EBUSY;
+
+ ret = zfcp_scan_ports(adapter);
+ return ret ? ret : (ssize_t) count;
+}
+static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL,
+ zfcp_sysfs_port_rescan_store);
+
+static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct zfcp_adapter *adapter = dev_get_drvdata(dev);
+ struct zfcp_port *port;
+ u64 wwpn;
+ int retval = 0;
+ LIST_HEAD(port_remove_lh);
+
+ down(&zfcp_data.config_sema);
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE) {
+ retval = -EBUSY;
+ goto out;
+ }
+
+ if (strict_strtoull(buf, 0, (unsigned long long *) &wwpn)) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ write_lock_irq(&zfcp_data.config_lock);
+ port = zfcp_get_port_by_wwpn(adapter, wwpn);
+ if (port && (atomic_read(&port->refcount) == 0)) {
+ zfcp_port_get(port);
+ atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
+ list_move(&port->list, &port_remove_lh);
+ } else
+ port = NULL;
+ write_unlock_irq(&zfcp_data.config_lock);
+
+ if (!port) {
+ retval = -ENXIO;
+ goto out;
+ }
+
+ zfcp_erp_port_shutdown(port, 0, 92, NULL);
+ zfcp_erp_wait(adapter);
+ zfcp_port_put(port);
+ zfcp_port_dequeue(port);
+ out:
+ up(&zfcp_data.config_sema);
+ return retval ? retval : (ssize_t) count;
+}
+static ZFCP_DEV_ATTR(adapter, port_remove, S_IWUSR, NULL,
+ zfcp_sysfs_port_remove_store);
+
+static struct attribute *zfcp_adapter_attrs[] = {
+ &dev_attr_adapter_failed.attr,
+ &dev_attr_adapter_in_recovery.attr,
+ &dev_attr_adapter_port_remove.attr,
+ &dev_attr_adapter_port_rescan.attr,
+ &dev_attr_adapter_peer_wwnn.attr,
+ &dev_attr_adapter_peer_wwpn.attr,
+ &dev_attr_adapter_peer_d_id.attr,
+ &dev_attr_adapter_card_version.attr,
+ &dev_attr_adapter_lic_version.attr,
+ &dev_attr_adapter_status.attr,
+ &dev_attr_adapter_hardware_version.attr,
+ NULL
+};
+
+struct attribute_group zfcp_sysfs_adapter_attrs = {
+ .attrs = zfcp_adapter_attrs,
+};
+
+static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct zfcp_port *port = dev_get_drvdata(dev);
+ struct zfcp_unit *unit;
+ u64 fcp_lun;
+ int retval = -EINVAL;
+
+ down(&zfcp_data.config_sema);
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
+ retval = -EBUSY;
+ goto out;
+ }
+
+ if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
+ goto out;
+
+ unit = zfcp_unit_enqueue(port, fcp_lun);
+ if (IS_ERR(unit))
+ goto out;
+
+ retval = 0;
+
+ zfcp_erp_unit_reopen(unit, 0, 94, NULL);
+ zfcp_erp_wait(unit->port->adapter);
+ zfcp_unit_put(unit);
+out:
+ up(&zfcp_data.config_sema);
+ return retval ? retval : (ssize_t) count;
+}
+static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
+
+static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct zfcp_port *port = dev_get_drvdata(dev);
+ struct zfcp_unit *unit;
+ u64 fcp_lun;
+ int retval = 0;
+ LIST_HEAD(unit_remove_lh);
+
+ down(&zfcp_data.config_sema);
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
+ retval = -EBUSY;
+ goto out;
+ }
+
+ if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ write_lock_irq(&zfcp_data.config_lock);
+ unit = zfcp_get_unit_by_lun(port, fcp_lun);
+ if (unit && (atomic_read(&unit->refcount) == 0)) {
+ zfcp_unit_get(unit);
+ atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
+ list_move(&unit->list, &unit_remove_lh);
+ } else
+ unit = NULL;
+
+ write_unlock_irq(&zfcp_data.config_lock);
+
+ if (!unit) {
+ retval = -ENXIO;
+ goto out;
+ }
+
+ zfcp_erp_unit_shutdown(unit, 0, 95, NULL);
+ zfcp_erp_wait(unit->port->adapter);
+ zfcp_unit_put(unit);
+ zfcp_unit_dequeue(unit);
+out:
+ up(&zfcp_data.config_sema);
+ return retval ? retval : (ssize_t) count;
+}
+static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
+
+static struct attribute *zfcp_port_attrs[] = {
+ &dev_attr_unit_add.attr,
+ &dev_attr_unit_remove.attr,
+ &dev_attr_port_failed.attr,
+ &dev_attr_port_in_recovery.attr,
+ &dev_attr_port_status.attr,
+ &dev_attr_port_access_denied.attr,
+ NULL
+};
+
+/**
+ * zfcp_sysfs_port_attrs - sysfs attributes for all other ports
+ */
+struct attribute_group zfcp_sysfs_port_attrs = {
+ .attrs = zfcp_port_attrs,
+};
+
+static struct attribute *zfcp_unit_attrs[] = {
+ &dev_attr_unit_failed.attr,
+ &dev_attr_unit_in_recovery.attr,
+ &dev_attr_unit_status.attr,
+ &dev_attr_unit_access_denied.attr,
+ &dev_attr_unit_access_shared.attr,
+ &dev_attr_unit_access_readonly.attr,
+ NULL
+};
+
+struct attribute_group zfcp_sysfs_unit_attrs = {
+ .attrs = zfcp_unit_attrs,
+};
+
+#define ZFCP_DEFINE_LATENCY_ATTR(_name) \
+static ssize_t \
+zfcp_sysfs_unit_##_name##_latency_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) { \
+ struct scsi_device *sdev = to_scsi_device(dev); \
+ struct zfcp_unit *unit = sdev->hostdata; \
+ struct zfcp_latencies *lat = &unit->latencies; \
+ struct zfcp_adapter *adapter = unit->port->adapter; \
+ unsigned long flags; \
+ unsigned long long fsum, fmin, fmax, csum, cmin, cmax, cc; \
+ \
+ spin_lock_irqsave(&lat->lock, flags); \
+ fsum = lat->_name.fabric.sum * adapter->timer_ticks; \
+ fmin = lat->_name.fabric.min * adapter->timer_ticks; \
+ fmax = lat->_name.fabric.max * adapter->timer_ticks; \
+ csum = lat->_name.channel.sum * adapter->timer_ticks; \
+ cmin = lat->_name.channel.min * adapter->timer_ticks; \
+ cmax = lat->_name.channel.max * adapter->timer_ticks; \
+ cc = lat->_name.counter; \
+ spin_unlock_irqrestore(&lat->lock, flags); \
+ \
+ do_div(fsum, 1000); \
+ do_div(fmin, 1000); \
+ do_div(fmax, 1000); \
+ do_div(csum, 1000); \
+ do_div(cmin, 1000); \
+ do_div(cmax, 1000); \
+ \
+ return sprintf(buf, "%llu %llu %llu %llu %llu %llu %llu\n", \
+ fmin, fmax, fsum, cmin, cmax, csum, cc); \
+} \
+static ssize_t \
+zfcp_sysfs_unit_##_name##_latency_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct scsi_device *sdev = to_scsi_device(dev); \
+ struct zfcp_unit *unit = sdev->hostdata; \
+ struct zfcp_latencies *lat = &unit->latencies; \
+ unsigned long flags; \
+ \
+ spin_lock_irqsave(&lat->lock, flags); \
+ lat->_name.fabric.sum = 0; \
+ lat->_name.fabric.min = 0xFFFFFFFF; \
+ lat->_name.fabric.max = 0; \
+ lat->_name.channel.sum = 0; \
+ lat->_name.channel.min = 0xFFFFFFFF; \
+ lat->_name.channel.max = 0; \
+ lat->_name.counter = 0; \
+ spin_unlock_irqrestore(&lat->lock, flags); \
+ \
+ return (ssize_t) count; \
+} \
+static DEVICE_ATTR(_name##_latency, S_IWUSR | S_IRUGO, \
+ zfcp_sysfs_unit_##_name##_latency_show, \
+ zfcp_sysfs_unit_##_name##_latency_store);
+
+ZFCP_DEFINE_LATENCY_ATTR(read);
+ZFCP_DEFINE_LATENCY_ATTR(write);
+ZFCP_DEFINE_LATENCY_ATTR(cmd);
+
+#define ZFCP_DEFINE_SCSI_ATTR(_name, _format, _value) \
+static ssize_t zfcp_sysfs_scsi_##_name##_show(struct device *dev, \
+ struct device_attribute *attr,\
+ char *buf) \
+{ \
+ struct scsi_device *sdev = to_scsi_device(dev); \
+ struct zfcp_unit *unit = sdev->hostdata; \
+ \
+ return sprintf(buf, _format, _value); \
+} \
+static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_scsi_##_name##_show, NULL);
+
+ZFCP_DEFINE_SCSI_ATTR(hba_id, "%s\n",
+ dev_name(&unit->port->adapter->ccw_device->dev));
+ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n",
+ (unsigned long long) unit->port->wwpn);
+ZFCP_DEFINE_SCSI_ATTR(fcp_lun, "0x%016llx\n",
+ (unsigned long long) unit->fcp_lun);
+
+struct device_attribute *zfcp_sysfs_sdev_attrs[] = {
+ &dev_attr_fcp_lun,
+ &dev_attr_wwpn,
+ &dev_attr_hba_id,
+ &dev_attr_read_latency,
+ &dev_attr_write_latency,
+ &dev_attr_cmd_latency,
+ NULL
+};
+
+static ssize_t zfcp_sysfs_adapter_util_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *scsi_host = dev_to_shost(dev);
+ struct fsf_qtcb_bottom_port *qtcb_port;
+ struct zfcp_adapter *adapter;
+ int retval;
+
+ adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
+ if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
+ return -EOPNOTSUPP;
+
+ qtcb_port = kzalloc(sizeof(struct fsf_qtcb_bottom_port), GFP_KERNEL);
+ if (!qtcb_port)
+ return -ENOMEM;
+
+ retval = zfcp_fsf_exchange_port_data_sync(adapter, qtcb_port);
+ if (!retval)
+ retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util,
+ qtcb_port->cb_util, qtcb_port->a_util);
+ kfree(qtcb_port);
+ return retval;
+}
+static DEVICE_ATTR(utilization, S_IRUGO, zfcp_sysfs_adapter_util_show, NULL);
+
+static int zfcp_sysfs_adapter_ex_config(struct device *dev,
+ struct fsf_statistics_info *stat_inf)
+{
+ struct Scsi_Host *scsi_host = dev_to_shost(dev);
+ struct fsf_qtcb_bottom_config *qtcb_config;
+ struct zfcp_adapter *adapter;
+ int retval;
+
+ adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
+ if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
+ return -EOPNOTSUPP;
+
+ qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config),
+ GFP_KERNEL);
+ if (!qtcb_config)
+ return -ENOMEM;
+
+ retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config);
+ if (!retval)
+ *stat_inf = qtcb_config->stat_info;
+
+ kfree(qtcb_config);
+ return retval;
+}
+
+#define ZFCP_SHOST_ATTR(_name, _format, _arg...) \
+static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, \
+ struct device_attribute *attr,\
+ char *buf) \
+{ \
+ struct fsf_statistics_info stat_info; \
+ int retval; \
+ \
+ retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info); \
+ if (retval) \
+ return retval; \
+ \
+ return sprintf(buf, _format, ## _arg); \
+} \
+static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL);
+
+ZFCP_SHOST_ATTR(requests, "%llu %llu %llu\n",
+ (unsigned long long) stat_info.input_req,
+ (unsigned long long) stat_info.output_req,
+ (unsigned long long) stat_info.control_req);
+
+ZFCP_SHOST_ATTR(megabytes, "%llu %llu\n",
+ (unsigned long long) stat_info.input_mb,
+ (unsigned long long) stat_info.output_mb);
+
+ZFCP_SHOST_ATTR(seconds_active, "%llu\n",
+ (unsigned long long) stat_info.seconds_act);
+
+static ssize_t zfcp_sysfs_adapter_q_full_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *scsi_host = class_to_shost(dev);
+ struct zfcp_adapter *adapter =
+ (struct zfcp_adapter *) scsi_host->hostdata[0];
+
+ return sprintf(buf, "%d\n", atomic_read(&adapter->qdio_outb_full));
+}
+static DEVICE_ATTR(queue_full, S_IRUGO, zfcp_sysfs_adapter_q_full_show, NULL);
+
+struct device_attribute *zfcp_sysfs_shost_attrs[] = {
+ &dev_attr_utilization,
+ &dev_attr_requests,
+ &dev_attr_megabytes,
+ &dev_attr_seconds_active,
+ &dev_attr_queue_full,
+ NULL
+};
diff --git a/drivers/s390/scsi/zfcp_sysfs_adapter.c b/drivers/s390/scsi/zfcp_sysfs_adapter.c
deleted file mode 100644
index ccbba4dd3a7..00000000000
--- a/drivers/s390/scsi/zfcp_sysfs_adapter.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
- *
- * (C) Copyright IBM Corp. 2002, 2006
- *
- * 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.
- */
-
-#include "zfcp_ext.h"
-
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
-
-/**
- * ZFCP_DEFINE_ADAPTER_ATTR
- * @_name: name of show attribute
- * @_format: format string
- * @_value: value to print
- *
- * Generates attributes for an adapter.
- */
-#define ZFCP_DEFINE_ADAPTER_ATTR(_name, _format, _value) \
-static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, struct device_attribute *attr, \
- char *buf) \
-{ \
- struct zfcp_adapter *adapter; \
- \
- adapter = dev_get_drvdata(dev); \
- return sprintf(buf, _format, _value); \
-} \
- \
-static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL);
-
-ZFCP_DEFINE_ADAPTER_ATTR(status, "0x%08x\n", atomic_read(&adapter->status));
-ZFCP_DEFINE_ADAPTER_ATTR(peer_wwnn, "0x%016llx\n", adapter->peer_wwnn);
-ZFCP_DEFINE_ADAPTER_ATTR(peer_wwpn, "0x%016llx\n", adapter->peer_wwpn);
-ZFCP_DEFINE_ADAPTER_ATTR(peer_d_id, "0x%06x\n", adapter->peer_d_id);
-ZFCP_DEFINE_ADAPTER_ATTR(card_version, "0x%04x\n", adapter->hydra_version);
-ZFCP_DEFINE_ADAPTER_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version);
-ZFCP_DEFINE_ADAPTER_ATTR(hardware_version, "0x%08x\n",
- adapter->hardware_version);
-ZFCP_DEFINE_ADAPTER_ATTR(in_recovery, "%d\n", atomic_test_mask
- (ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status));
-
-/**
- * zfcp_sysfs_port_add_store - add a port to sysfs tree
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "port_add" attribute of an adapter.
- */
-static ssize_t
-zfcp_sysfs_port_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- wwn_t wwpn;
- char *endp;
- struct zfcp_adapter *adapter;
- struct zfcp_port *port;
- int retval = -EINVAL;
-
- down(&zfcp_data.config_sema);
-
- adapter = dev_get_drvdata(dev);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
- retval = -EBUSY;
- goto out;
- }
-
- wwpn = simple_strtoull(buf, &endp, 0);
- if ((endp + 1) < (buf + count))
- goto out;
-
- port = zfcp_port_enqueue(adapter, wwpn, 0, 0);
- if (!port)
- goto out;
-
- retval = 0;
-
- zfcp_erp_port_reopen(port, 0, 91, NULL);
- zfcp_erp_wait(port->adapter);
- zfcp_port_put(port);
- out:
- up(&zfcp_data.config_sema);
- return retval ? retval : (ssize_t) count;
-}
-
-static DEVICE_ATTR(port_add, S_IWUSR, NULL, zfcp_sysfs_port_add_store);
-
-/**
- * zfcp_sysfs_port_remove_store - remove a port from sysfs tree
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "port_remove" attribute of an adapter.
- */
-static ssize_t
-zfcp_sysfs_port_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct zfcp_adapter *adapter;
- struct zfcp_port *port;
- wwn_t wwpn;
- char *endp;
- int retval = 0;
-
- down(&zfcp_data.config_sema);
-
- adapter = dev_get_drvdata(dev);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
- retval = -EBUSY;
- goto out;
- }
-
- wwpn = simple_strtoull(buf, &endp, 0);
- if ((endp + 1) < (buf + count)) {
- retval = -EINVAL;
- goto out;
- }
-
- write_lock_irq(&zfcp_data.config_lock);
- port = zfcp_get_port_by_wwpn(adapter, wwpn);
- if (port && (atomic_read(&port->refcount) == 0)) {
- zfcp_port_get(port);
- atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
- list_move(&port->list, &adapter->port_remove_lh);
- }
- else {
- port = NULL;
- }
- write_unlock_irq(&zfcp_data.config_lock);
-
- if (!port) {
- retval = -ENXIO;
- goto out;
- }
-
- zfcp_erp_port_shutdown(port, 0, 92, NULL);
- zfcp_erp_wait(adapter);
- zfcp_port_put(port);
- zfcp_port_dequeue(port);
- out:
- up(&zfcp_data.config_sema);
- return retval ? retval : (ssize_t) count;
-}
-
-static DEVICE_ATTR(port_remove, S_IWUSR, NULL, zfcp_sysfs_port_remove_store);
-
-/**
- * zfcp_sysfs_adapter_failed_store - failed state of adapter
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "failed" attribute of an adapter.
- * If a "0" gets written to "failed", error recovery will be
- * started for the belonging adapter.
- */
-static ssize_t
-zfcp_sysfs_adapter_failed_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct zfcp_adapter *adapter;
- unsigned int val;
- char *endp;
- int retval = 0;
-
- down(&zfcp_data.config_sema);
-
- adapter = dev_get_drvdata(dev);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
- retval = -EBUSY;
- goto out;
- }
-
- val = simple_strtoul(buf, &endp, 0);
- if (((endp + 1) < (buf + count)) || (val != 0)) {
- retval = -EINVAL;
- goto out;
- }
-
- zfcp_erp_modify_adapter_status(adapter, 44, NULL,
- ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
- zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 93,
- NULL);
- zfcp_erp_wait(adapter);
- out:
- up(&zfcp_data.config_sema);
- return retval ? retval : (ssize_t) count;
-}
-
-/**
- * zfcp_sysfs_adapter_failed_show - failed state of adapter
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- *
- * Show function of "failed" attribute of adapter. Will be
- * "0" if adapter is working, otherwise "1".
- */
-static ssize_t
-zfcp_sysfs_adapter_failed_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct zfcp_adapter *adapter;
-
- adapter = dev_get_drvdata(dev);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status))
- return sprintf(buf, "1\n");
- else
- return sprintf(buf, "0\n");
-}
-
-static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_adapter_failed_show,
- zfcp_sysfs_adapter_failed_store);
-
-static struct attribute *zfcp_adapter_attrs[] = {
- &dev_attr_failed.attr,
- &dev_attr_in_recovery.attr,
- &dev_attr_port_remove.attr,
- &dev_attr_port_add.attr,
- &dev_attr_peer_wwnn.attr,
- &dev_attr_peer_wwpn.attr,
- &dev_attr_peer_d_id.attr,
- &dev_attr_card_version.attr,
- &dev_attr_lic_version.attr,
- &dev_attr_status.attr,
- &dev_attr_hardware_version.attr,
- NULL
-};
-
-static struct attribute_group zfcp_adapter_attr_group = {
- .attrs = zfcp_adapter_attrs,
-};
-
-/**
- * zfcp_sysfs_create_adapter_files - create sysfs adapter files
- * @dev: pointer to belonging device
- *
- * Create all attributes of the sysfs representation of an adapter.
- */
-int
-zfcp_sysfs_adapter_create_files(struct device *dev)
-{
- return sysfs_create_group(&dev->kobj, &zfcp_adapter_attr_group);
-}
-
-/**
- * zfcp_sysfs_remove_adapter_files - remove sysfs adapter files
- * @dev: pointer to belonging device
- *
- * Remove all attributes of the sysfs representation of an adapter.
- */
-void
-zfcp_sysfs_adapter_remove_files(struct device *dev)
-{
- sysfs_remove_group(&dev->kobj, &zfcp_adapter_attr_group);
-}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_sysfs_driver.c b/drivers/s390/scsi/zfcp_sysfs_driver.c
deleted file mode 100644
index 651edd58906..00000000000
--- a/drivers/s390/scsi/zfcp_sysfs_driver.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
- *
- * (C) Copyright IBM Corp. 2002, 2006
- *
- * 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.
- */
-
-#include "zfcp_ext.h"
-
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
-
-/**
- * ZFCP_DEFINE_DRIVER_ATTR - define for all loglevels sysfs attributes
- * @_name: name of attribute
- * @_define: name of ZFCP loglevel define
- *
- * Generates store function for a sysfs loglevel attribute of zfcp driver.
- */
-#define ZFCP_DEFINE_DRIVER_ATTR(_name, _define) \
-static ssize_t zfcp_sysfs_loglevel_##_name##_store(struct device_driver *drv, \
- const char *buf, \
- size_t count) \
-{ \
- unsigned int loglevel; \
- unsigned int new_loglevel; \
- char *endp; \
- \
- new_loglevel = simple_strtoul(buf, &endp, 0); \
- if ((endp + 1) < (buf + count)) \
- return -EINVAL; \
- if (new_loglevel > 3) \
- return -EINVAL; \
- down(&zfcp_data.config_sema); \
- loglevel = atomic_read(&zfcp_data.loglevel); \
- loglevel &= ~((unsigned int) 0xf << (ZFCP_LOG_AREA_##_define << 2)); \
- loglevel |= new_loglevel << (ZFCP_LOG_AREA_##_define << 2); \
- atomic_set(&zfcp_data.loglevel, loglevel); \
- up(&zfcp_data.config_sema); \
- return count; \
-} \
- \
-static ssize_t zfcp_sysfs_loglevel_##_name##_show(struct device_driver *dev, \
- char *buf) \
-{ \
- return sprintf(buf,"%d\n", (unsigned int) \
- ZFCP_GET_LOG_VALUE(ZFCP_LOG_AREA_##_define)); \
-} \
- \
-static DRIVER_ATTR(loglevel_##_name, S_IWUSR | S_IRUGO, \
- zfcp_sysfs_loglevel_##_name##_show, \
- zfcp_sysfs_loglevel_##_name##_store);
-
-ZFCP_DEFINE_DRIVER_ATTR(other, OTHER);
-ZFCP_DEFINE_DRIVER_ATTR(scsi, SCSI);
-ZFCP_DEFINE_DRIVER_ATTR(fsf, FSF);
-ZFCP_DEFINE_DRIVER_ATTR(config, CONFIG);
-ZFCP_DEFINE_DRIVER_ATTR(cio, CIO);
-ZFCP_DEFINE_DRIVER_ATTR(qdio, QDIO);
-ZFCP_DEFINE_DRIVER_ATTR(erp, ERP);
-ZFCP_DEFINE_DRIVER_ATTR(fc, FC);
-
-static ssize_t zfcp_sysfs_version_show(struct device_driver *dev,
- char *buf)
-{
- return sprintf(buf, "%s\n", zfcp_data.driver_version);
-}
-
-static DRIVER_ATTR(version, S_IRUGO, zfcp_sysfs_version_show, NULL);
-
-static struct attribute *zfcp_driver_attrs[] = {
- &driver_attr_loglevel_other.attr,
- &driver_attr_loglevel_scsi.attr,
- &driver_attr_loglevel_fsf.attr,
- &driver_attr_loglevel_config.attr,
- &driver_attr_loglevel_cio.attr,
- &driver_attr_loglevel_qdio.attr,
- &driver_attr_loglevel_erp.attr,
- &driver_attr_loglevel_fc.attr,
- &driver_attr_version.attr,
- NULL
-};
-
-static struct attribute_group zfcp_driver_attr_group = {
- .attrs = zfcp_driver_attrs,
-};
-
-struct attribute_group *zfcp_driver_attr_groups[] = {
- &zfcp_driver_attr_group,
- NULL,
-};
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_sysfs_port.c b/drivers/s390/scsi/zfcp_sysfs_port.c
deleted file mode 100644
index 703c1b5cb60..00000000000
--- a/drivers/s390/scsi/zfcp_sysfs_port.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
- *
- * (C) Copyright IBM Corp. 2002, 2006
- *
- * 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.
- */
-
-#include "zfcp_ext.h"
-
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
-
-/**
- * zfcp_sysfs_port_release - gets called when a struct device port is released
- * @dev: pointer to belonging device
- */
-void
-zfcp_sysfs_port_release(struct device *dev)
-{
- kfree(dev);
-}
-
-/**
- * ZFCP_DEFINE_PORT_ATTR
- * @_name: name of show attribute
- * @_format: format string
- * @_value: value to print
- *
- * Generates attributes for a port.
- */
-#define ZFCP_DEFINE_PORT_ATTR(_name, _format, _value) \
-static ssize_t zfcp_sysfs_port_##_name##_show(struct device *dev, struct device_attribute *attr, \
- char *buf) \
-{ \
- struct zfcp_port *port; \
- \
- port = dev_get_drvdata(dev); \
- return sprintf(buf, _format, _value); \
-} \
- \
-static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_port_##_name##_show, NULL);
-
-ZFCP_DEFINE_PORT_ATTR(status, "0x%08x\n", atomic_read(&port->status));
-ZFCP_DEFINE_PORT_ATTR(in_recovery, "%d\n", atomic_test_mask
- (ZFCP_STATUS_COMMON_ERP_INUSE, &port->status));
-ZFCP_DEFINE_PORT_ATTR(access_denied, "%d\n", atomic_test_mask
- (ZFCP_STATUS_COMMON_ACCESS_DENIED, &port->status));
-
-/**
- * zfcp_sysfs_unit_add_store - add a unit to sysfs tree
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "unit_add" attribute of a port.
- */
-static ssize_t
-zfcp_sysfs_unit_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- fcp_lun_t fcp_lun;
- char *endp;
- struct zfcp_port *port;
- struct zfcp_unit *unit;
- int retval = -EINVAL;
-
- down(&zfcp_data.config_sema);
-
- port = dev_get_drvdata(dev);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
- retval = -EBUSY;
- goto out;
- }
-
- fcp_lun = simple_strtoull(buf, &endp, 0);
- if ((endp + 1) < (buf + count))
- goto out;
-
- unit = zfcp_unit_enqueue(port, fcp_lun);
- if (!unit)
- goto out;
-
- retval = 0;
-
- zfcp_erp_unit_reopen(unit, 0, 94, NULL);
- zfcp_erp_wait(unit->port->adapter);
- zfcp_unit_put(unit);
- out:
- up(&zfcp_data.config_sema);
- return retval ? retval : (ssize_t) count;
-}
-
-static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
-
-/**
- * zfcp_sysfs_unit_remove_store - remove a unit from sysfs tree
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- */
-static ssize_t
-zfcp_sysfs_unit_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct zfcp_port *port;
- struct zfcp_unit *unit;
- fcp_lun_t fcp_lun;
- char *endp;
- int retval = 0;
-
- down(&zfcp_data.config_sema);
-
- port = dev_get_drvdata(dev);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
- retval = -EBUSY;
- goto out;
- }
-
- fcp_lun = simple_strtoull(buf, &endp, 0);
- if ((endp + 1) < (buf + count)) {
- retval = -EINVAL;
- goto out;
- }
-
- write_lock_irq(&zfcp_data.config_lock);
- unit = zfcp_get_unit_by_lun(port, fcp_lun);
- if (unit && (atomic_read(&unit->refcount) == 0)) {
- zfcp_unit_get(unit);
- atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
- list_move(&unit->list, &port->unit_remove_lh);
- }
- else {
- unit = NULL;
- }
- write_unlock_irq(&zfcp_data.config_lock);
-
- if (!unit) {
- retval = -ENXIO;
- goto out;
- }
-
- zfcp_erp_unit_shutdown(unit, 0, 95, NULL);
- zfcp_erp_wait(unit->port->adapter);
- zfcp_unit_put(unit);
- zfcp_unit_dequeue(unit);
- out:
- up(&zfcp_data.config_sema);
- return retval ? retval : (ssize_t) count;
-}
-
-static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
-
-/**
- * zfcp_sysfs_port_failed_store - failed state of port
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "failed" attribute of a port.
- * If a "0" gets written to "failed", error recovery will be
- * started for the belonging port.
- */
-static ssize_t
-zfcp_sysfs_port_failed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct zfcp_port *port;
- unsigned int val;
- char *endp;
- int retval = 0;
-
- down(&zfcp_data.config_sema);
-
- port = dev_get_drvdata(dev);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
- retval = -EBUSY;
- goto out;
- }
-
- val = simple_strtoul(buf, &endp, 0);
- if (((endp + 1) < (buf + count)) || (val != 0)) {
- retval = -EINVAL;
- goto out;
- }
-
- zfcp_erp_modify_port_status(port, 45, NULL,
- ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
- zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, 96, NULL);
- zfcp_erp_wait(port->adapter);
- out:
- up(&zfcp_data.config_sema);
- return retval ? retval : (ssize_t) count;
-}
-
-/**
- * zfcp_sysfs_port_failed_show - failed state of port
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- *
- * Show function of "failed" attribute of port. Will be
- * "0" if port is working, otherwise "1".
- */
-static ssize_t
-zfcp_sysfs_port_failed_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct zfcp_port *port;
-
- port = dev_get_drvdata(dev);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status))
- return sprintf(buf, "1\n");
- else
- return sprintf(buf, "0\n");
-}
-
-static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_port_failed_show,
- zfcp_sysfs_port_failed_store);
-
-/**
- * zfcp_port_common_attrs
- * sysfs attributes that are common for all kind of fc ports.
- */
-static struct attribute *zfcp_port_common_attrs[] = {
- &dev_attr_failed.attr,
- &dev_attr_in_recovery.attr,
- &dev_attr_status.attr,
- &dev_attr_access_denied.attr,
- NULL
-};
-
-static struct attribute_group zfcp_port_common_attr_group = {
- .attrs = zfcp_port_common_attrs,
-};
-
-/**
- * zfcp_port_no_ns_attrs
- * sysfs attributes not to be used for nameserver ports.
- */
-static struct attribute *zfcp_port_no_ns_attrs[] = {
- &dev_attr_unit_add.attr,
- &dev_attr_unit_remove.attr,
- NULL
-};
-
-static struct attribute_group zfcp_port_no_ns_attr_group = {
- .attrs = zfcp_port_no_ns_attrs,
-};
-
-/**
- * zfcp_sysfs_port_create_files - create sysfs port files
- * @dev: pointer to belonging device
- *
- * Create all attributes of the sysfs representation of a port.
- */
-int
-zfcp_sysfs_port_create_files(struct device *dev, u32 flags)
-{
- int retval;
-
- retval = sysfs_create_group(&dev->kobj, &zfcp_port_common_attr_group);
-
- if ((flags & ZFCP_STATUS_PORT_WKA) || retval)
- return retval;
-
- retval = sysfs_create_group(&dev->kobj, &zfcp_port_no_ns_attr_group);
- if (retval)
- sysfs_remove_group(&dev->kobj, &zfcp_port_common_attr_group);
-
- return retval;
-}
-
-/**
- * zfcp_sysfs_port_remove_files - remove sysfs port files
- * @dev: pointer to belonging device
- *
- * Remove all attributes of the sysfs representation of a port.
- */
-void
-zfcp_sysfs_port_remove_files(struct device *dev, u32 flags)
-{
- sysfs_remove_group(&dev->kobj, &zfcp_port_common_attr_group);
- if (!(flags & ZFCP_STATUS_PORT_WKA))
- sysfs_remove_group(&dev->kobj, &zfcp_port_no_ns_attr_group);
-}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_sysfs_unit.c b/drivers/s390/scsi/zfcp_sysfs_unit.c
deleted file mode 100644
index 80fb2c2cf48..00000000000
--- a/drivers/s390/scsi/zfcp_sysfs_unit.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
- *
- * (C) Copyright IBM Corp. 2002, 2006
- *
- * 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.
- */
-
-#include "zfcp_ext.h"
-
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
-
-/**
- * zfcp_sysfs_unit_release - gets called when a struct device unit is released
- * @dev: pointer to belonging device
- */
-void
-zfcp_sysfs_unit_release(struct device *dev)
-{
- kfree(dev);
-}
-
-/**
- * ZFCP_DEFINE_UNIT_ATTR
- * @_name: name of show attribute
- * @_format: format string
- * @_value: value to print
- *
- * Generates attribute for a unit.
- */
-#define ZFCP_DEFINE_UNIT_ATTR(_name, _format, _value) \
-static ssize_t zfcp_sysfs_unit_##_name##_show(struct device *dev, struct device_attribute *attr, \
- char *buf) \
-{ \
- struct zfcp_unit *unit; \
- \
- unit = dev_get_drvdata(dev); \
- return sprintf(buf, _format, _value); \
-} \
- \
-static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_unit_##_name##_show, NULL);
-
-ZFCP_DEFINE_UNIT_ATTR(status, "0x%08x\n", atomic_read(&unit->status));
-ZFCP_DEFINE_UNIT_ATTR(in_recovery, "%d\n", atomic_test_mask
- (ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status));
-ZFCP_DEFINE_UNIT_ATTR(access_denied, "%d\n", atomic_test_mask
- (ZFCP_STATUS_COMMON_ACCESS_DENIED, &unit->status));
-ZFCP_DEFINE_UNIT_ATTR(access_shared, "%d\n", atomic_test_mask
- (ZFCP_STATUS_UNIT_SHARED, &unit->status));
-ZFCP_DEFINE_UNIT_ATTR(access_readonly, "%d\n", atomic_test_mask
- (ZFCP_STATUS_UNIT_READONLY, &unit->status));
-
-/**
- * zfcp_sysfs_unit_failed_store - failed state of unit
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "failed" attribute of a unit.
- * If a "0" gets written to "failed", error recovery will be
- * started for the belonging unit.
- */
-static ssize_t
-zfcp_sysfs_unit_failed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct zfcp_unit *unit;
- unsigned int val;
- char *endp;
- int retval = 0;
-
- down(&zfcp_data.config_sema);
- unit = dev_get_drvdata(dev);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status)) {
- retval = -EBUSY;
- goto out;
- }
-
- val = simple_strtoul(buf, &endp, 0);
- if (((endp + 1) < (buf + count)) || (val != 0)) {
- retval = -EINVAL;
- goto out;
- }
-
- zfcp_erp_modify_unit_status(unit, 46, NULL,
- ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
- zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED, 97, NULL);
- zfcp_erp_wait(unit->port->adapter);
- out:
- up(&zfcp_data.config_sema);
- return retval ? retval : (ssize_t) count;
-}
-
-/**
- * zfcp_sysfs_unit_failed_show - failed state of unit
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- *
- * Show function of "failed" attribute of unit. Will be
- * "0" if unit is working, otherwise "1".
- */
-static ssize_t
-zfcp_sysfs_unit_failed_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct zfcp_unit *unit;
-
- unit = dev_get_drvdata(dev);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status))
- return sprintf(buf, "1\n");
- else
- return sprintf(buf, "0\n");
-}
-
-static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_unit_failed_show,
- zfcp_sysfs_unit_failed_store);
-
-static struct attribute *zfcp_unit_attrs[] = {
- &dev_attr_failed.attr,
- &dev_attr_in_recovery.attr,
- &dev_attr_status.attr,
- &dev_attr_access_denied.attr,
- &dev_attr_access_shared.attr,
- &dev_attr_access_readonly.attr,
- NULL
-};
-
-static struct attribute_group zfcp_unit_attr_group = {
- .attrs = zfcp_unit_attrs,
-};
-
-/**
- * zfcp_sysfs_create_unit_files - create sysfs unit files
- * @dev: pointer to belonging device
- *
- * Create all attributes of the sysfs representation of a unit.
- */
-int
-zfcp_sysfs_unit_create_files(struct device *dev)
-{
- return sysfs_create_group(&dev->kobj, &zfcp_unit_attr_group);
-}
-
-/**
- * zfcp_sysfs_remove_unit_files - remove sysfs unit files
- * @dev: pointer to belonging device
- *
- * Remove all attributes of the sysfs representation of a unit.
- */
-void
-zfcp_sysfs_unit_remove_files(struct device *dev)
-{
- sysfs_remove_group(&dev->kobj, &zfcp_unit_attr_group);
-}
-
-#undef ZFCP_LOG_AREA