summaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2008-07-18 22:39:59 +0200
committerIngo Molnar <mingo@elte.hu>2008-07-18 22:39:59 +0200
commit0679c2f47d50651018dd5e0bf35330f6e6ae35ec (patch)
tree2df65ebb958f930abf0b7ad35fd6e528398b1679 /drivers/s390
parent76fbc263ff7e42ce8b21b8aee176e3c74b45f81a (diff)
parent5b664cb235e97afbf34db9c4d77f08ebd725335e (diff)
downloadlinux-0679c2f47d50651018dd5e0bf35330f6e6ae35ec.tar.gz
linux-0679c2f47d50651018dd5e0bf35330f6e6ae35ec.tar.bz2
linux-0679c2f47d50651018dd5e0bf35330f6e6ae35ec.zip
Merge branch 'linus' into pci-for-jesse
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/block/dasd.c18
-rw-r--r--drivers/s390/block/dasd_3990_erp.c15
-rw-r--r--drivers/s390/block/dasd_diag.c25
-rw-r--r--drivers/s390/block/dasd_eckd.c16
-rw-r--r--drivers/s390/block/dasd_eer.c6
-rw-r--r--drivers/s390/block/dasd_fba.c19
-rw-r--r--drivers/s390/block/dcssblk.c22
-rw-r--r--drivers/s390/block/xpram.c18
-rw-r--r--drivers/s390/char/con3215.c38
-rw-r--r--drivers/s390/char/con3270.c6
-rw-r--r--drivers/s390/char/fs3270.c34
-rw-r--r--drivers/s390/char/monreader.c78
-rw-r--r--drivers/s390/char/monwriter.c3
-rw-r--r--drivers/s390/char/raw3270.c28
-rw-r--r--drivers/s390/char/sclp.c12
-rw-r--r--drivers/s390/char/sclp_cmd.c343
-rw-r--r--drivers/s390/char/sclp_con.c5
-rw-r--r--drivers/s390/char/sclp_config.c17
-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.c62
-rw-r--r--drivers/s390/char/tape_34xx.c12
-rw-r--r--drivers/s390/char/tape_3590.c21
-rw-r--r--drivers/s390/char/tape_char.c12
-rw-r--r--drivers/s390/char/tape_core.c15
-rw-r--r--drivers/s390/char/tty3270.c9
-rw-r--r--drivers/s390/char/vmcp.c41
-rw-r--r--drivers/s390/char/vmlogrdr.c37
-rw-r--r--drivers/s390/char/vmur.c27
-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/chp.c116
-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.c282
-rw-r--r--drivers/s390/cio/cio.h14
-rw-r--r--drivers/s390/cio/cmf.c20
-rw-r--r--drivers/s390/cio/css.c283
-rw-r--r--drivers/s390/cio/css.h49
-rw-r--r--drivers/s390/cio/device.c476
-rw-r--r--drivers/s390/cio/device.h7
-rw-r--r--drivers/s390/cio/device_fsm.c210
-rw-r--r--drivers/s390/cio/device_id.c16
-rw-r--r--drivers/s390/cio/device_ops.c134
-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.h2
-rw-r--r--drivers/s390/cio/io_sch.h48
-rw-r--r--drivers/s390/cio/ioasm.h2
-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.h837
-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.c1755
-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.c521
-rw-r--r--drivers/s390/cio/qdio_thinint.c380
-rw-r--r--drivers/s390/cio/schid.h26
-rw-r--r--drivers/s390/cio/scsw.c843
-rw-r--r--drivers/s390/crypto/ap_bus.c63
-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/net/claw.c77
-rw-r--r--drivers/s390/net/ctcm_fsms.c12
-rw-r--r--drivers/s390/net/ctcm_main.c28
-rw-r--r--drivers/s390/net/cu3088.c2
-rw-r--r--drivers/s390/net/cu3088.h3
-rw-r--r--drivers/s390/net/lcs.c44
-rw-r--r--drivers/s390/net/netiucv.c61
-rw-r--r--drivers/s390/net/qeth_core.h12
-rw-r--r--drivers/s390/net/qeth_core_main.c102
-rw-r--r--drivers/s390/net/qeth_l2_main.c26
-rw-r--r--drivers/s390/net/qeth_l3_main.c34
-rw-r--r--drivers/s390/net/smsgiucv.c10
-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.c1689
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c152
-rw-r--r--drivers/s390/scsi/zfcp_cfdc.c259
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c102
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h14
-rw-r--r--drivers/s390/scsi/zfcp_def.h341
-rw-r--r--drivers/s390/scsi/zfcp_erp.c3824
-rw-r--r--drivers/s390/scsi/zfcp_ext.h306
-rw-r--r--drivers/s390/scsi/zfcp_fc.c567
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c5573
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h70
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c799
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c784
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c496
-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
114 files changed, 13672 insertions, 17078 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 1a4025683362..1b6c52ef7339 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -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);
+ cdev->dev.bus_id, ((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);
@@ -1025,7 +1025,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
/* 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);
@@ -1041,11 +1041,11 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
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;
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index e6700df52df4..5c6e6f331cb0 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -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_diag.c b/drivers/s390/block/dasd_diag.c
index d91df38ee4f7..85fcb4371054 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 a0edae091b5e..3590fdb5b2fd 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1404,21 +1404,24 @@ 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;
}
@@ -2068,11 +2071,11 @@ static void dasd_eckd_dump_sense(struct dasd_device *device,
device->cdev->dev.bus_id);
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);
+ (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 +2125,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_eer.c b/drivers/s390/block/dasd_eer.c
index 6e53ab606e97..29da4413ad43 100644
--- a/drivers/s390/block/dasd_eer.c
+++ b/drivers/s390/block/dasd_eer.c
@@ -15,6 +15,7 @@
#include <linux/device.h>
#include <linux/poll.h>
#include <linux/mutex.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
@@ -525,6 +526,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 +534,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 +542,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 +557,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 116611583df8..aa0c533423a5 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;
}
@@ -449,11 +454,11 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
device->cdev->dev.bus_id);
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);
+ (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/dcssblk.c b/drivers/s390/block/dcssblk.c
index bb52d2fbac18..01fcdd91b846 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -167,10 +167,8 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch
struct dcssblk_dev_info *dev_info;
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)) {
@@ -215,7 +213,6 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch
set_disk_ro(dev_info->gd, 0);
}
} else {
- PRINT_WARN("Invalid value, must be 0 or 1\n");
rc = -EINVAL;
goto out;
}
@@ -258,10 +255,8 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
{
struct dcssblk_dev_info *dev_info;
- 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);
@@ -289,7 +284,6 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
}
} else {
up_write(&dcssblk_devices_sem);
- PRINT_WARN("Invalid value, must be 0 or 1\n");
return -EINVAL;
}
up_write(&dcssblk_devices_sem);
@@ -441,7 +435,6 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
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;
@@ -702,10 +695,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 +704,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 +727,6 @@ dcssblk_init(void)
dcssblk_check_params();
- PRINT_DEBUG("...finished!\n");
return 0;
}
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index f231bc21b1ca..dd9b986389a2 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -100,15 +100,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 +130,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;
}
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 3e5653c92f4b..d3ec9b55ab35 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -93,9 +93,6 @@ struct raw3215_info {
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 */
};
@@ -359,11 +356,6 @@ raw3215_tasklet(void *data)
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 +373,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;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
+ if (cstat != 0)
tasklet_schedule(&raw->tasklet);
- }
if (dstat & 0x01) { /* we got a unit exception */
dstat &= ~0x01; /* we can ignore it */
}
@@ -404,8 +390,6 @@ 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);
break;
case 0x08:
@@ -415,7 +399,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 +412,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) {
@@ -481,11 +460,6 @@ 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);
}
return;
@@ -883,7 +857,6 @@ 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;
}
register_console(&con3215);
@@ -1157,7 +1130,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 0b040557db02..3c07974886ed 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -411,15 +411,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;
}
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c
index ef36f2132aa4..d18e6d2e0b49 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;
}
@@ -421,6 +422,7 @@ fs3270_open(struct inode *inode, struct file *filp)
if (imajor(filp->f_path.dentry->d_inode) != IBM_FS3270_MAJOR)
return -ENODEV;
+ lock_kernel();
minor = iminor(filp->f_path.dentry->d_inode);
/* Check for minor 0 multiplexer. */
if (minor == 0) {
@@ -429,7 +431,8 @@ fs3270_open(struct inode *inode, struct file *filp)
tty = get_current_tty();
if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) {
mutex_unlock(&tty_mutex);
- return -ENODEV;
+ rc = -ENODEV;
+ goto out;
}
minor = tty->index + RAW3270_FIRSTMINOR;
mutex_unlock(&tty_mutex);
@@ -438,19 +441,22 @@ fs3270_open(struct inode *inode, struct file *filp)
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 +464,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,9 +473,11 @@ 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;
+out:
+ unlock_kernel();
return 0;
}
@@ -512,11 +521,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 1e1f50655bbf..35fd8dfcaaa6 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 a86c0534cd49..4d71aa8c1a79 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 848ef7e8523f..81a96e019080 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;
}
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 2c7a1ee6b041..3c8b25e6c345 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -506,6 +506,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 +784,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 +883,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 b5c23396f8fe..0c2b77493db4 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,305 @@ 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:
+ 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;
+ 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 ead1043d788e..7e619c534bf4 100644
--- a/drivers/s390/char/sclp_con.c
+++ b/drivers/s390/char/sclp_con.c
@@ -14,14 +14,13 @@
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/bootmem.h>
+#include <linux/termios.h>
#include <linux/err.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"
@@ -222,8 +221,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);
diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c
index ad05a87bc480..fff4ff485d9b 100644
--- a/drivers/s390/char/sclp_config.c
+++ b/drivers/s390/char/sclp_config.c
@@ -8,6 +8,7 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/cpu.h>
+#include <linux/kthread.h>
#include <linux/sysdev.h>
#include <linux/workqueue.h>
#include <asm/smp.h>
@@ -40,9 +41,19 @@ static void sclp_cpu_capability_notify(struct work_struct *work)
put_online_cpus();
}
-static void __ref sclp_cpu_change_notify(struct work_struct *work)
+static int sclp_cpu_kthread(void *data)
{
smp_rescan_cpus();
+ return 0;
+}
+
+static void __ref sclp_cpu_change_notify(struct work_struct *work)
+{
+ /* Can't call smp_rescan_cpus() from workqueue context since it may
+ * deadlock in case of cpu hotplug. So we have to create a kernel
+ * thread in order to call it.
+ */
+ kthread_run(sclp_cpu_kthread, NULL, "cpu_rescan");
}
static void sclp_conf_receiver_fn(struct evbuf_header *evbuf)
@@ -74,10 +85,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 9f37456222e9..d887bd261d28 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 45ff25e787cb..84c191c1cd62 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 da09781b32f7..710af42603f8 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 1c064976b32b..8b854857ba07 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 40b11521cd20..434ba04b1309 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 0ce2c1fc5340..4b965b22fecd 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 3e577f655b18..ad51738c4261 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -27,7 +27,6 @@
#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 +81,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 +608,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 +620,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 +655,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 +693,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 +715,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;
@@ -773,10 +770,9 @@ 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 */
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c
index 874adf365e46..22ca34361ed7 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 42ce7915fc5d..839987618ffd 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);
@@ -1515,18 +1515,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 +1537,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_char.c b/drivers/s390/char/tape_char.c
index ebe84067bae9..687720b552d1 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>
@@ -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_core.c b/drivers/s390/char/tape_core.c
index c20e3c548343..181a5441af16 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -839,7 +839,7 @@ 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);
+ irb->scsw.cmd.dstat, irb->scsw.cmd.cstat, irb->scsw.cmd.cpa);
PRINT_INFO("DEVICE: %s\n", device->cdev->dev.bus_id);
if (request != NULL)
PRINT_INFO("OP : %s\n", tape_op_verbose[request->op]);
@@ -867,7 +867,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]);
@@ -1083,10 +1083,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 +1095,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 +1112,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/tty3270.c b/drivers/s390/char/tty3270.c
index 5043150019ac..a7fe6302c982 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 2f419b0ea628..09e7d9bf438b 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 2c2428cc05d8..c31faefa2b3b 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;
@@ -871,12 +857,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 +868,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 83ae9a852f00..b0ac44b27127 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));
@@ -885,7 +892,7 @@ static int ur_set_online(struct ccw_device *cdev)
} else if (urd->cdev->id.cu_type == PRINTER_DEVTYPE) {
sprintf(node_id, "vmprt-%s", cdev->dev.bus_id);
} else {
- rc = -ENOTSUPP;
+ rc = -EOPNOTSUPP;
goto fail_free_cdev;
}
diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c
index 19f8389291b6..21a2a829bf4e 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 bbbd14e9d48f..7fd84be11931 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 cfaf77b320f5..bd79bd165396 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 b7a07a866291..fe6cea15bbaf 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/chp.c b/drivers/s390/cio/chp.c
index 297cdceb0ca4..db00b0591733 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 = {
@@ -405,7 +397,7 @@ int chp_new(struct chp_id chpid)
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 +405,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;
@@ -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 65286563c592..26c3d2246176 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 5de86908b0d0..29826fdd47b8 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 d1f5db1e69b9..ba59bceace98 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 000000000000..91ca87aa9f97
--- /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 000000000000..589ebfad6aad
--- /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 b32d7eb3d81a..33bff8fec7d1 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;
}
@@ -128,7 +129,7 @@ cio_tpi(void)
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);
@@ -167,30 +168,30 @@ 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);
orb = &to_io_private(sch)->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,7 +203,7 @@ 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 */
@@ -237,7 +238,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;
@@ -277,7 +278,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 */
@@ -312,7 +313,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;
@@ -387,8 +388,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)
{
@@ -434,12 +437,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;
@@ -455,7 +459,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 +488,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 +499,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));
@@ -546,67 +585,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 +640,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;
@@ -706,9 +699,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 +709,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 +754,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 +770,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 +783,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 +803,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;
}
@@ -864,7 +857,7 @@ static void udelay_reset(unsigned long usecs)
}
static int
-__clear_subchannel_easy(struct subchannel_id schid)
+__clear_io_subchannel_easy(struct subchannel_id schid)
{
int retry;
@@ -883,6 +876,12 @@ __clear_subchannel_easy(struct subchannel_id schid)
return -EBUSY;
}
+static void __clear_chsc_subchannel_easy(void)
+{
+ /* It seems we can only wait for a bit here :/ */
+ udelay_reset(100);
+}
+
static int pgm_check_occured;
static void cio_reset_pgm_check_handler(void)
@@ -921,11 +920,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 +1078,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 6e933aebe013..3b236d20e835 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. */
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 2808b6833b9e..a90b28c0be57 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 a76956512b2d..46c021d880dc 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;
@@ -596,18 +538,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 +611,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 {
@@ -795,8 +748,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 +757,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;
@@ -836,8 +791,7 @@ init_channel_subsystem (void)
ret = device_register(&css->device);
if (ret)
goto out_free_all;
- if (css_characteristics_avail &&
- css_chsc_characteristics.secm) {
+ if (css_chsc_characteristics.secm) {
ret = device_create_file(&css->device,
&dev_attr_cm_enable);
if (ret)
@@ -852,7 +806,8 @@ init_channel_subsystem (void)
goto out_pseudo;
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;
@@ -875,7 +830,7 @@ out_unregister:
i--;
css = channel_subsystems[i];
device_unregister(&css->pseudo_subchannel->dev);
- if (css_characteristics_avail && css_chsc_characteristics.secm)
+ if (css_chsc_characteristics.secm)
device_remove_file(&css->device,
&dev_attr_cm_enable);
device_unregister(&css->device);
@@ -883,6 +838,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 +851,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 +898,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 +951,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 e1913518f354..57ebf120f825 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 e22813db74a2..e818d0c54c09 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,7 +22,9 @@
#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"
@@ -125,19 +126,24 @@ 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,
@@ -487,25 +493,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 +541,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 +588,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 +789,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);
}
@@ -1037,7 +1036,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. */
@@ -1122,8 +1120,33 @@ static void io_subchannel_irq(struct subchannel *sch)
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 +1155,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 +1185,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 +1221,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 +1231,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 +1257,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 +1268,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 +1280,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,6 +1399,195 @@ 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 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();
+}
+
+static int io_subchannel_sch_event(struct subchannel *sch, int slow)
+{
+ int event, ret, disc;
+ unsigned long flags;
+ enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } 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:
+ /* Prevent unwanted effects when opening lock. */
+ cio_disable_subchannel(sch);
+ device_set_disconnected(cdev);
+ /* Ask driver what to do with device. */
+ action = UNREGISTER;
+ spin_unlock_irqrestore(sch->lock, flags);
+ ret = io_subchannel_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:
+ ccw_device_trigger_reprobe(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 struct ccw_device_private console_private;
@@ -1297,14 +1600,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);
@@ -1515,71 +1820,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);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index cb08092be39f..9800a8335a3f 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -88,8 +88,6 @@ 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);
-
/* Function prototypes for device status and basic sense stuff. */
void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *);
@@ -118,6 +116,11 @@ 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);
+
/* 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 e268d5a77c12..8b5fe57fb2f3 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,43 @@ 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);
+ orb, sizeof(*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);
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 +123,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 +145,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,34 +331,30 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err)
}
}
+int ccw_device_notify(struct ccw_device *cdev, int event)
+{
+ if (!cdev->drv)
+ return 0;
+ if (!cdev->online)
+ return 0;
+ return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
+}
+
static void
ccw_device_oper_notify(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;
+ ret = ccw_device_notify(cdev, CIO_OPER);
if (ret) {
/* 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);
- }
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- if (!ret)
+ } else
/* Driver doesn't want device back. */
ccw_device_do_unreg_rereg(work);
}
@@ -621,10 +560,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,
@@ -718,13 +658,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 +736,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 +760,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 +785,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 +829,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 +849,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 +924,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 +957,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 +991,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 +1014,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 cba7020517ed..1bdaa614e34f 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 f308ad55a6d5..ee1a28310fbb 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);
@@ -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 5cf7be008e98..86bc94eb607f 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 4a38993000f2..1b03c5423be2 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 000000000000..61677dfbdc9b
--- /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.h b/drivers/s390/cio/idset.h
index 144466ab8c15..528065cb5021 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 8c613160bfce..3f8f1cf69c76 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,18 @@ 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, volatile 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");
return ccode;
}
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 652ea3625f9d..9fa2ac13ac85 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
diff --git a/drivers/s390/cio/isc.c b/drivers/s390/cio/isc.c
new file mode 100644
index 000000000000..c592087be0f1
--- /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 000000000000..17da9ab932ed
--- /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 445cf364e461..000000000000
--- 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 c3df6b2c38b7..c1a70985abfa 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -1,66 +1,20 @@
+/*
+ * 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
-
-/*
- * 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
- */
-#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_BUSY_BIT_PATIENCE 100 /* 100 microseconds */
+#define QDIO_BUSY_BIT_GIVE_UP 2000000 /* 2 seconds = eternity */
+#define QDIO_INPUT_THRESHOLD 500 /* 500 microseconds */
enum qdio_irq_states {
QDIO_IRQ_STATE_INACTIVE,
@@ -72,565 +26,352 @@ 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
-
-#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
+/* 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>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 */
+ /* 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;
+ /*
+ * inbound: next buffer the program should check for
+ * outbound: next buffer to check for having been processed
+ * by the card
+ */
+ int first_to_check;
- atomic_t number_of_buffers_used;
- atomic_t polling;
+ /* first_to_check of the last time */
+ int last_move_ftc;
- 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;
+ /* beginning position for calling the program */
+ int first_to_kick;
- /* used to save beginning position when calling dd_handlers */
- int first_element_to_kick;
+ /* number of buffers in use by the adapter */
+ atomic_t nr_buf_used;
- atomic_t use_count;
- atomic_t is_in_shutdown;
-
- 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 000000000000..337aa3087a78
--- /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", cdev->dev.bus_id);
+ 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 000000000000..8484b83698e1
--- /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 4
+#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 16
+#define QDIO_DBF_SETUP_LEVEL 6
+#define QDIO_DBF_TRACE_LEVEL 4
+#else /* !CONFIG_QDIO_DEBUG */
+#define QDIO_DBF_TRACE_PAGES 4
+#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 000000000000..d10c73cc1688
--- /dev/null
+++ b/drivers/s390/cio/qdio_main.c
@@ -0,0 +1,1755 @@
+/*
+ * 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 (!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;
+
+ 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) {
+ 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:
+ /* went smooth this time, reset timestamp */
+ q->u.out.timestamp = 0;
+
+ /* TODO: improve error handling for CC=0 case */
+#ifdef CONFIG_QDIO_DEBUG
+ 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 */
+ 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 (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", 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));
+
+ 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",
+ cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT2(1, setup, dbf_text);
+ qdio_int_error(cdev);
+ return;
+ case -ETIMEDOUT:
+ sprintf(dbf_text, "qtoh%4x",
+ cdev->private->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;
+
+ QDIO_DBF_TEXT0(0, setup, "getssqd");
+
+ 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;
+
+ 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 = 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];
+
+ 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;
+ }
+
+ sprintf(dbf_text, "qsqs%4x", irq_ptr->schid.sch_no);
+ QDIO_DBF_TEXT1(0, trace, dbf_text);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+
+ 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);
+ module_put(THIS_MODULE);
+ 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];
+
+ irq_ptr = cdev->private->qdio_data;
+ if (!irq_ptr)
+ return -ENODEV;
+
+ mutex_lock(&irq_ptr->setup_mutex);
+
+ 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;
+ 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);
+ QDIO_DBF_TEXT0(0, trace, 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);
+ QDIO_DBF_TEXT0(0, trace, 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 = kzalloc(sizeof(struct qdr), 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;
+
+ irq_ptr = cdev->private->qdio_data;
+ if (!irq_ptr)
+ return -ENODEV;
+
+ if (cdev->private->state != DEV_STATE_ONLINE)
+ return -EINVAL;
+
+ if (!try_module_get(THIS_MODULE))
+ return -EINVAL;
+
+ sprintf(dbf_text, "qest%4x", cdev->private->schid.sch_no);
+ QDIO_DBF_TEXT0(0, setup, dbf_text);
+ QDIO_DBF_TEXT0(0, trace, dbf_text);
+
+ 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, "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];
+
+ 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;
+ }
+
+ sprintf(dbf_text, "qact%4x", irq_ptr->schid.sch_no);
+ QDIO_DBF_TEXT2(0, setup, dbf_text);
+ QDIO_DBF_TEXT2(0, trace, dbf_text);
+
+ 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
+ /*
+ * One siga-w per buffer required for unicast
+ * HiperSockets.
+ */
+ 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%04x", 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 000000000000..ea01b85b1cc9
--- /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 __exit 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 000000000000..5c406a8b7387
--- /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 000000000000..f0923a8aceda
--- /dev/null
+++ b/drivers/s390/cio/qdio_setup.c
@@ -0,0 +1,521 @@
+/*
+ * 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, "qfqs%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, "schno%x", 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);
+ }
+ }
+ kfree(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, "%s ", cdev->dev.bus_id);
+
+ switch (irq_ptr->qib.qfmt) {
+ case QDIO_QETH_QFMT:
+ sprintf(s + strlen(s), "OSADE ");
+ break;
+ case QDIO_ZFCP_QFMT:
+ sprintf(s + strlen(s), "ZFCP ");
+ break;
+ case QDIO_IQDIO_QFMT:
+ sprintf(s + strlen(s), "HiperSockets ");
+ break;
+ }
+ sprintf(s + strlen(s), "using: ");
+
+ if (!is_thinint_irq(irq_ptr))
+ sprintf(s + strlen(s), "no");
+ sprintf(s + strlen(s), "AdapterInterrupts ");
+ if (!(irq_ptr->sch_token != 0))
+ sprintf(s + strlen(s), "no");
+ sprintf(s + strlen(s), "QEBSM ");
+ if (!(irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED))
+ sprintf(s + strlen(s), "no");
+ sprintf(s + strlen(s), "OutboundPCI ");
+ if (!css_general_characteristics.aif_tdd)
+ sprintf(s + strlen(s), "no");
+ sprintf(s + strlen(s), "TDD\n");
+ printk(KERN_INFO "qdio: %s", s);
+
+ memset(s, 0, sizeof(s));
+ sprintf(s, "%s SIGA required: ", cdev->dev.bus_id);
+ if (irq_ptr->siga_flag.input)
+ sprintf(s + strlen(s), "Read ");
+ if (irq_ptr->siga_flag.output)
+ sprintf(s + strlen(s), "Write ");
+ if (irq_ptr->siga_flag.sync)
+ sprintf(s + strlen(s), "Sync ");
+ if (!irq_ptr->siga_flag.no_sync_ti)
+ sprintf(s + strlen(s), "SyncAI ");
+ if (!irq_ptr->siga_flag.no_sync_out_ti)
+ sprintf(s + strlen(s), "SyncOutAI ");
+ if (!irq_ptr->siga_flag.no_sync_out_pci)
+ sprintf(s + strlen(s), "SyncOutPCI");
+ sprintf(s + strlen(s), "\n");
+ printk(KERN_INFO "qdio: %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 __exit 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 000000000000..9291a771d812
--- /dev/null
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -0,0 +1,380 @@
+/*
+ * 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_each_input_queue(irq_ptr, q, i) {
+ 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 54328fec5ade..000000000000
--- 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 000000000000..f8da25ab576d
--- /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 a1ab3e3efd11..62b6b55230d0 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,
};
/**
@@ -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 c1e1200c43fc..446378b308fc 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 4d36e805a234..cb22b97944b8 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 5c6e222b2ac4..1d1ec74dadb2 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 08657f604b8c..54f4cbc3be9e 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 3e27fe77d207..03ba27f05f92 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 6e93b4751782..12da4815ba8e 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 17ea56ce1c11..779952cb19fc 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 0bc9b3188e64..d8ad36f81540 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/net/claw.c b/drivers/s390/net/claw.c
index 04a1d7bf678c..c644669a75c2 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -703,7 +703,8 @@ claw_irq_handler(struct ccw_device *cdev,
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);
+ cdev->dev.bus_id, irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
#ifdef FUNCTRACE
printk(KERN_INFO "claw: %s() "
"exit on line %d\n",__func__,__LINE__);
@@ -732,22 +733,23 @@ claw_irq_handler(struct ccw_device *cdev,
#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);
+ dev->name, p_ch->devno, irb->scsw.cmd.cstat,
+ irb->scsw.cmd.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)) {
+ 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);
+ irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
+ irb->scsw.cmd.cpa);
#ifdef IOTRACE
dumpit((char *)irb,sizeof(struct irb));
- dumpit((char *)(unsigned long)irb->scsw.cpa,
+ dumpit((char *)(unsigned long)irb->scsw.cmd.cpa,
sizeof(struct ccw1));
#endif
#ifdef FUNCTRACE
@@ -759,22 +761,24 @@ claw_irq_handler(struct ccw_device *cdev,
}
/* 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)))) {
+ 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)))) {
#ifdef FUNCTRACE
printk(KERN_INFO "%s:%s Exit on line %d\n",
dev->name,__func__,__LINE__);
@@ -798,10 +802,13 @@ claw_irq_handler(struct ccw_device *cdev,
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)))) {
+ 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)))) {
#ifdef FUNCTRACE
printk(KERN_INFO "%s:%s Exit on line %d\n",
dev->name,__func__,__LINE__);
@@ -828,8 +835,8 @@ claw_irq_handler(struct ccw_device *cdev,
"interrupt for device:"
"%s received c-%02x d-%02x\n",
cdev->dev.bus_id,
- irb->scsw.cstat,
- irb->scsw.dstat);
+ irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
return;
}
#ifdef DEBUGMSG
@@ -844,7 +851,7 @@ claw_irq_handler(struct ccw_device *cdev,
return;
case CLAW_START_READ:
CLAW_DBF_TEXT(4,trace,"ReadIRQ");
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ 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 ||
@@ -863,8 +870,8 @@ claw_irq_handler(struct ccw_device *cdev,
CLAW_DBF_TEXT(4,trace,"notrdy");
return;
}
- if ((p_ch->irb->scsw.cstat & SCHN_STAT_PCI) &&
- (p_ch->irb->scsw.dstat==0)) {
+ 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);
@@ -879,10 +886,13 @@ claw_irq_handler(struct ccw_device *cdev,
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)))) {
+ 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)))) {
#ifdef FUNCTRACE
printk(KERN_INFO "%s:%s Exit on line %d\n",
dev->name,__func__,__LINE__);
@@ -911,7 +921,7 @@ claw_irq_handler(struct ccw_device *cdev,
CLAW_DBF_TEXT(4,trace,"RdIRQXit");
return;
case CLAW_START_WRITE:
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ 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);
@@ -934,16 +944,19 @@ claw_irq_handler(struct ccw_device *cdev,
CLAW_DBF_TEXT(4,trace,"rstrtwrt");
return;
}
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) {
+ 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.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)))) {
+ 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)))) {
#ifdef FUNCTRACE
printk(KERN_INFO "%s:%s Exit on line %d\n",
dev->name,__func__,__LINE__);
diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c
index 2a106f3a076d..7e6bd387f4d8 100644
--- a/drivers/s390/net/ctcm_fsms.c
+++ b/drivers/s390/net/ctcm_fsms.c
@@ -257,9 +257,9 @@ static void chx_txdone(fsm_instance *fi, int event, void *arg)
if (duration > ch->prof.tx_time)
ch->prof.tx_time = duration;
- if (ch->irb->scsw.count != 0)
+ if (ch->irb->scsw.cmd.count != 0)
ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n",
- dev->name, ch->irb->scsw.count);
+ dev->name, ch->irb->scsw.cmd.count);
fsm_deltimer(&ch->timer);
while ((skb = skb_dequeue(&ch->io_queue))) {
priv->stats.tx_packets++;
@@ -353,7 +353,7 @@ 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;
+ 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;
@@ -1234,9 +1234,9 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
if (duration > ch->prof.tx_time)
ch->prof.tx_time = duration;
- if (ch->irb->scsw.count != 0)
+ if (ch->irb->scsw.cmd.count != 0)
ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n",
- dev->name, ch->irb->scsw.count);
+ dev->name, ch->irb->scsw.cmd.count);
fsm_deltimer(&ch->timer);
while ((skb = skb_dequeue(&ch->io_queue))) {
priv->stats.tx_packets++;
@@ -1394,7 +1394,7 @@ static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg)
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;
+ int len = ch->max_bufsize - ch->irb->scsw.cmd.count;
if (do_debug_data) {
CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "mpc_ch_rx %s cp:%i %s\n",
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index d52843da4f55..6b13c1c1beb8 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -1236,8 +1236,8 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
/* 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);
+ cdev->dev.bus_id, irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
return;
}
@@ -1266,40 +1266,40 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
"received c-%02x d-%02x\n",
dev->name,
ch->id,
- irb->scsw.cstat,
- irb->scsw.dstat);
+ irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
/* Copy interruption response block. */
memcpy(ch->irb, irb, sizeof(struct irb));
/* Check for good subchannel return code, otherwise error message */
- if (irb->scsw.cstat) {
+ if (irb->scsw.cmd.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) {
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
diff --git a/drivers/s390/net/cu3088.c b/drivers/s390/net/cu3088.c
index 8e7697305a4c..f4a32375c037 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 1753661f702a..d8558a7105a5 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 dd22f4b37037..6de28385b354 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -1327,8 +1327,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,11 +1388,13 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
else
channel = &card->write;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.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);
+ 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);
@@ -1410,11 +1412,11 @@ 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)) {
+ 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 +1427,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);
}
@@ -1761,7 +1762,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 +2043,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 +2140,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)
@@ -2269,7 +2268,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 e4ba6a0372ac..9242b5acc66b 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);
@@ -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 699ac11debd8..1895dbb553cd 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -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
@@ -811,17 +806,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 *);
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 9a71dae223e8..c3ad89e302bd 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -420,7 +420,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,8 +735,8 @@ 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 |
@@ -823,8 +823,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 +842,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*/
@@ -2073,7 +2073,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 +2349,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 +2554,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 +2564,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 +2616,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 +2636,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 +2654,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 +2750,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 +2799,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 +2856,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 +2872,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 +2887,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);
@@ -3164,11 +3149,11 @@ 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_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 +3206,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;
@@ -3253,7 +3238,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 +3259,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)
@@ -3686,10 +3671,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 +3732,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 +3766,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) {
@@ -4092,7 +4077,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;
}
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index f682f7b14480..3fbc3bdec0c5 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -726,8 +726,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 +741,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);
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 999552c83bbe..22f64aa6dd1f 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;
}
@@ -2946,8 +2939,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 +2954,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);
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index 8735a415a116..164e090c2625 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/s390mach.c b/drivers/s390/s390mach.c
index 5bfbe7659830..834e9ee7e934 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 ca681f9b67fc..d39f8b697d27 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 d6a78f1a2f16..cb301cc6178c 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 8c7e2b778ef1..90abfd06ed55 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,58 @@ 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, &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, &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: Parse error for device parameter string %s, "
+ "device not attached.\n", devstr);
return 0;
}
-static void __init
-zfcp_init_device_configure(void)
+static struct zfcp_adapter *zfcp_get_adapter_by_busid(char *bus_id)
+{
+ struct zfcp_adapter *adapter;
+
+ list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list)
+ if ((strncmp(bus_id, adapter->ccw_device->dev.bus_id,
+ BUS_ID_SIZE) == 0) &&
+ !(atomic_read(&adapter->status) &
+ ZFCP_STATUS_COMMON_REMOVE))
+ return adapter;
+ return NULL;
+}
+
+static void __init zfcp_init_device_configure(void)
{
struct zfcp_adapter *adapter;
struct zfcp_port *port;
@@ -215,101 +131,75 @@ 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);
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);
-
- /* 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);
if (!zfcp_data.scsi_transport_template)
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: registration of 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: Registration with common I/O layer failed.\n");
goto out_ccw_register;
}
@@ -318,527 +208,88 @@ 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,
+ fcp_lun_t 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,
+ wwn_t 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, fcp_lun_t 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);
unit->sysfs_device.parent = &port->sysfs_device;
unit->sysfs_device.release = zfcp_sysfs_unit_release;
@@ -847,14 +298,28 @@ 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);
@@ -864,16 +329,27 @@ zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun)
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);
write_lock_irq(&zfcp_data.config_lock);
@@ -881,68 +357,51 @@ zfcp_unit_dequeue(struct zfcp_unit *unit)
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)
@@ -962,20 +421,61 @@ static void zfcp_dummy_release(struct device *dev)
return;
}
-/*
+/**
+ * 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)
+{
+ 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));
+}
+
+static 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 (IS_ERR(port))
+ return PTR_ERR(port);
+ zfcp_port_put(port);
+
+ return 0;
+}
+
+/**
+ * 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,85 +483,58 @@ 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);
-
- /* initialise list of ports to be removed */
INIT_LIST_HEAD(&adapter->port_remove_lh);
+ INIT_LIST_HEAD(&adapter->erp_ready_head);
+ INIT_LIST_HEAD(&adapter->erp_running_head);
- /* 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;
@@ -1072,7 +545,6 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device)
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);
@@ -1080,57 +552,49 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device)
zfcp_data.adapters++;
- goto out;
+ zfcp_nameserver_enqueue(adapter);
+
+ return 0;
- generic_services_failed:
- zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev);
- sysfs_failed:
+generic_services_failed:
+ sysfs_remove_group(&ccw_device->dev.kobj,
+ &zfcp_sysfs_adapter_attrs);
+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);
@@ -1142,26 +606,18 @@ zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
/* 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 +626,90 @@ 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, wwn_t wwpn,
+ u32 status, u32 d_id)
{
struct zfcp_port *port;
- int check_wwpn;
-
- 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;
- }
+ int retval;
+ char *bus_id;
- 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);
port->adapter = adapter;
+ port->d_id = d_id;
+ port->wwpn = wwpn;
- if (check_wwpn)
- port->wwpn = wwpn;
-
- atomic_set_mask(status, &port->status);
+ /* 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);
- /* 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");
+ bus_id = "directory";
break;
case ZFCP_DID_MANAGEMENT_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "management");
+ bus_id = "management";
break;
case ZFCP_DID_KEY_DISTRIBUTION_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "key_distribution");
+ bus_id = "key_distribution";
break;
case ZFCP_DID_ALIAS_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "alias");
+ bus_id = "alias";
break;
case ZFCP_DID_TIME_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "time");
+ bus_id = "time";
break;
default:
kfree(port);
- return NULL;
+ return ERR_PTR(-EINVAL);
}
- port->d_id = d_id;
+ snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, "%s", bus_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;
+
+ if (status & ZFCP_STATUS_PORT_WKA)
+ retval = sysfs_create_group(&port->sysfs_device.kobj,
+ &zfcp_sysfs_ns_port_attrs);
+ else
+ 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);
@@ -1274,15 +722,23 @@ zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status,
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);
write_lock_irq(&zfcp_data.config_lock);
@@ -1293,546 +749,53 @@ zfcp_port_dequeue(struct zfcp_port *port)
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));
- 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);
+ if (atomic_read(&port->status) & ZFCP_STATUS_PORT_WKA)
+ sysfs_remove_group(&port->sysfs_device.kobj,
+ &zfcp_sysfs_ns_port_attrs);
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
- */
-int
-zfcp_ns_gid_pn_request(struct zfcp_erp_action *erp_action)
-{
- 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;
+ sysfs_remove_group(&port->sysfs_device.kobj,
+ &zfcp_sysfs_port_attrs);
+ device_unregister(&port->sysfs_device);
}
-/* 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
+ * 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
*/
-static const char *
-zfcp_rc_description(u8 code, const struct zfcp_rc_entry *rc_table)
+void zfcp_sg_free_table(struct scatterlist *sg, int count)
{
- 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 66d3b88844b0..391dd29749f8 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,16 @@ 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,
+ "Setup of data structures 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,8 +41,7 @@ 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;
@@ -106,8 +51,6 @@ zfcp_ccw_remove(struct ccw_device *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) {
@@ -145,8 +88,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 +97,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 +129,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,15 +143,14 @@ 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;
@@ -222,18 +158,15 @@ zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
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, "device gone\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, "no path\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, "operational again\n");
zfcp_erp_modify_adapter_status(adapter, 11, NULL,
ZFCP_STATUS_COMMON_RUNNING,
ZFCP_SET);
@@ -247,24 +180,10 @@ zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
}
/**
- * 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)
-{
- 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_ccw_shutdown - handle shutdown from cio
+ * @cdev: device for adapter to shutdown.
*/
-static void
-zfcp_ccw_shutdown(struct ccw_device *cdev)
+static void zfcp_ccw_shutdown(struct ccw_device *cdev)
{
struct zfcp_adapter *adapter;
@@ -275,4 +194,33 @@ zfcp_ccw_shutdown(struct ccw_device *cdev)
up(&zfcp_data.config_sema);
}
-#undef ZFCP_LOG_AREA
+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)
+{
+ return ccw_driver_register(&zfcp_ccw_driver);
+}
diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c
new file mode 100644
index 000000000000..ec2abceca6dc
--- /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 c8bad675dbd1..fca48b88fc53 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,9 +311,7 @@ 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));
@@ -355,8 +336,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,9 +394,7 @@ 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);
}
@@ -515,13 +494,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",
@@ -546,8 +525,8 @@ static const char *zfcp_rec_dbf_ids[] = {
[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] = "",
[85] = "online",
[86] = "offline",
[87] = "ccw device gone",
@@ -586,8 +565,8 @@ 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",
@@ -595,7 +574,7 @@ static const char *zfcp_rec_dbf_ids[] = {
[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 +582,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 +654,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 +676,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 +818,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);
}
@@ -960,7 +955,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 +1059,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 +1273,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 54c34e483457..0ddb18449d11 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));
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index bda8c77b22da..67f45fc62f53 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,9 +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
@@ -93,11 +76,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,42 +84,17 @@ 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)
@@ -153,17 +106,9 @@ 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
@@ -224,9 +169,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;
@@ -266,7 +211,6 @@ struct fcp_logo {
* 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
@@ -311,7 +255,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 +268,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 ******************/
/*
@@ -441,6 +287,7 @@ do { \
#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
@@ -496,77 +343,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;
@@ -623,7 +399,6 @@ typedef void (*zfcp_send_ct_handler_t)(unsigned long);
* @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
@@ -636,7 +411,6 @@ struct zfcp_send_ct {
unsigned int resp_count;
zfcp_send_ct_handler_t handler;
unsigned long handler_data;
- mempool_t *pool;
int timeout;
struct completion *completion;
int status;
@@ -685,13 +459,13 @@ struct zfcp_send_els {
};
struct zfcp_qdio_queue {
- struct qdio_buffer *buffer[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */
- u8 free_index; /* index of next free bfr
+ struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */
+ u8 first; /* index of next free bfr
in queue (free_count>0) */
- atomic_t free_count; /* number of free buffers
+ atomic_t 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
+ spinlock_t lock; /* lock for operations on queue */
+ int pci_batch; /* SBALs since PCI indication
was last set */
};
@@ -708,6 +482,24 @@ struct zfcp_erp_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 */
@@ -723,24 +515,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 */
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 */
@@ -774,13 +567,9 @@ struct zfcp_adapter {
struct fc_host_statistics *fc_stats;
struct fsf_qtcb_bottom_port *stats_reset_data;
unsigned long stats_reset;
+ struct work_struct scan_work;
};
-/*
- * 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 */
@@ -804,10 +593,6 @@ struct zfcp_port {
u32 supported_classes;
};
-/* 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 */
@@ -822,6 +607,7 @@ struct zfcp_unit {
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 +617,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 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,10 +637,9 @@ 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;
@@ -873,29 +658,11 @@ struct zfcp_data {
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;
-};
-
-/* 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,7 +672,6 @@ 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
@@ -916,12 +682,6 @@ struct zfcp_fsf_req_qtcb {
((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))
@@ -934,15 +694,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)
{
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 805484658dd9..643ac4bba5b5 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -1,641 +1,406 @@
/*
- * 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_OPEN = 0x0200,
+ 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_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);
- 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 +408,182 @@ 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)
-{
- zfcp_erp_modify_adapter_status(adapter, 15, NULL,
- ZFCP_STATUS_COMMON_UNBLOCKED |
- clear_mask, ZFCP_CLEAR);
-}
-
-/* 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_set(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) & 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)
+static int status_change_clear(unsigned long mask, atomic_t *status)
{
- int changed_bits = atomic_read(v) & mask;
- atomic_clear_mask(mask, v);
- return changed_bits;
+ return atomic_read(status) & mask;
}
-/**
- * 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;
-
- /* 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;
- }
- }
+ struct zfcp_adapter *adapter = act->adapter;
- 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);
- }
+ 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_nolock - complete erp_action
- *
- * Used for normal completion, time-out, dismissal and failure after
- * low memory condition.
+ * zfcp_erp_notify - Trigger ERP action.
+ * @erp_action: ERP action to continue.
+ * @set_mask: ERP action status flags to set.
*/
-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 */
- }
-}
-
-/**
- * zfcp_erp_async_handler - wrapper for erp_async_handler_nolock w/ locking
- */
-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;
-}
-
-/*
- * 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)
- */
-static int
-zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
-{
- 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;
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
}
-/*
- * function:
- *
- * purpose:
- *
- * returns: ZFCP_ERP_DISMISSED - if action has been dismissed
- * retval - otherwise
+/**
+ * zfcp_erp_timeout_handler - Trigger ERP action from timed out ERP request
+ * @data: ERP action (from timer data)
*/
-static int
-zfcp_erp_strategy_check_action(struct zfcp_erp_action *erp_action, int retval)
+void zfcp_erp_timeout_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;
+ struct zfcp_erp_action *act = (struct zfcp_erp_action *) data;
+ zfcp_erp_notify(act, ZFCP_STATUS_ERP_TIMEDOUT);
}
-static int
-zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
+static void zfcp_erp_memwait_handler(unsigned long data)
{
- 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;
+ zfcp_erp_notify((struct zfcp_erp_action *)data, 0);
}
-/*
- * 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)
+static void 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)
+static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter,
+ int clear, u8 id, void *ref)
{
- 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)
-{
- 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)
+ if (!(atomic_read(&port->status) & ZFCP_STATUS_PORT_WKA))
+ _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;
+ struct zfcp_adapter *adapter = act->adapter;
+ struct zfcp_port *port = act->port;
+ struct zfcp_unit *unit = act->unit;
+ u32 status = act->status;
- 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);
-}
-
-/*
- * 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 +597,622 @@ 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);
+ int ret;
+ struct zfcp_adapter *adapter = act->adapter;
- 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);
- }
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
- 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:
+ &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);
-
- retval = do_QDIO(adapter->ccw_device,
- QDIO_FLAG_SYNC_INPUT,
- 0, 0, QDIO_MAX_BUFFERS_PER_Q, NULL);
-
- 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;
+ atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &act->adapter->status);
+ zfcp_erp_adapter_strategy_generic(act, 1); /* close */
+ atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &act->adapter->status);
+ if (act->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+ return ZFCP_ERP_EXIT;
- failed_do_qdio:
- /* NOP */
+ atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &act->adapter->status);
+ retval = zfcp_erp_adapter_strategy_generic(act, 0); /* open */
+ atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &act->adapter->status);
- 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;
+ if (retval == ZFCP_ERP_FAILED)
+ ssleep(8);
- out:
return retval;
}
-
-static int
-zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *act)
{
int retval;
- retval = zfcp_erp_adapter_strategy_open_fsf_xconfig(erp_action);
- if (retval == ZFCP_ERP_FAILED)
+ 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;
- 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);
+ return ZFCP_ERP_CONTINUES;
}
-static int
-zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *erp_action)
+static void zfcp_erp_port_strategy_clearstati(struct zfcp_port *port)
{
- 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;
+ 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);
+}
- ZFCP_LOG_DEBUG("host connection still initialising... "
- "waiting and retrying...\n");
- /* sleep a little bit before retry */
- ssleep(sleep);
- sleep *= 2;
- }
+static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action)
+{
+ struct zfcp_port *port = erp_action->port;
+ int status = atomic_read(&port->status);
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
- &adapter->status);
+ switch (erp_action->step) {
+ case ZFCP_ERP_STEP_UNINITIALIZED:
+ zfcp_erp_port_strategy_clearstati(port);
+ 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;
- 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;
+ case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
+ if (status & ZFCP_STATUS_PORT_PHYS_OPEN)
+ return ZFCP_ERP_SUCCEEDED;
}
-
- return retval;
+ return ZFCP_ERP_FAILED;
}
-static int
-zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_port_strategy_close(struct zfcp_erp_action *erp_action)
{
- 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_port(erp_action);
+ if (retval == -ENOMEM)
+ return ZFCP_ERP_NOMEM;
+ erp_action->step = ZFCP_ERP_STEP_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 int zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *erp_action)
{
- 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;
- }
- }
+ int retval;
- return retval;
+ 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;
}
-/*
- * 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 void zfcp_erp_port_strategy_open_ns_wake(struct zfcp_erp_action *ns_act)
{
- int retval = ZFCP_ERP_FAILED;
- struct zfcp_port *port = erp_action->port;
-
- 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;
+ unsigned long flags;
+ struct zfcp_adapter *adapter = ns_act->adapter;
+ struct zfcp_erp_action *act, *tmp;
+ int status;
- 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;
+ read_lock_irqsave(&adapter->erp_lock, flags);
+ list_for_each_entry_safe(act, tmp, &adapter->erp_running_head, list) {
+ if (act->step == ZFCP_ERP_STEP_NAMESERVER_OPEN) {
+ status = atomic_read(&adapter->nameserver_port->status);
+ if (status & ZFCP_STATUS_COMMON_ERP_FAILED)
+ zfcp_erp_port_failed(act->port, 27, NULL);
+ zfcp_erp_action_ready(act);
+ }
}
-
- return retval;
+ read_unlock_irqrestore(&adapter->erp_lock, flags);
}
-/*
- * 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_nameserver(struct zfcp_erp_action *act)
{
- int retval = ZFCP_ERP_FAILED;
- struct zfcp_port *port = erp_action->port;
-
- switch (erp_action->step) {
+ int retval;
- /*
- * FIXME:
- * the ULP spec. begs for waiting for oustanding commands
- */
+ switch (act->step) {
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;
-
+ case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
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);
+ return zfcp_erp_port_strategy_open_port(act);
+
+ case ZFCP_ERP_STEP_PORT_OPENING:
+ if (atomic_read(&act->port->status) & ZFCP_STATUS_COMMON_OPEN)
+ retval = ZFCP_ERP_SUCCEEDED;
+ else
retval = ZFCP_ERP_FAILED;
- goto out;
- } /* else it's closed now, open it */
- break;
- }
- if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
- retval = ZFCP_ERP_EXIT;
- else
- retval = zfcp_erp_port_strategy_open(erp_action);
+ /* this is needed anyway */
+ zfcp_erp_port_strategy_open_ns_wake(act);
+ return retval;
- out:
- return retval;
+ default:
+ return ZFCP_ERP_FAILED;
+ }
}
-static int
-zfcp_erp_port_strategy_open(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_port_strategy_open_lookup(struct zfcp_erp_action *act)
{
int retval;
- 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);
-
- return retval;
+ retval = zfcp_fc_ns_gid_pn_request(act);
+ if (retval == -ENOMEM)
+ return ZFCP_ERP_NOMEM;
+ act->step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
+ if (retval)
+ return ZFCP_ERP_FAILED;
+ return ZFCP_ERP_CONTINUES;
}
-static int
-zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *erp_action)
+static int zfcp_erp_open_ptp_port(struct zfcp_erp_action *act)
{
- int retval = 0;
- struct zfcp_adapter *adapter = erp_action->adapter;
- struct zfcp_port *port = erp_action->port;
+ struct zfcp_adapter *adapter = act->adapter;
+ struct zfcp_port *port = act->port;
- switch (erp_action->step) {
+ if (port->wwpn != adapter->peer_wwpn) {
+ dev_err(&adapter->ccw_device->dev,
+ "Failed to open port 0x%016Lx, "
+ "Peer WWPN 0x%016Lx does not "
+ "match.\n", port->wwpn,
+ adapter->peer_wwpn);
+ zfcp_erp_port_failed(port, 25, NULL);
+ return ZFCP_ERP_FAILED;
+ }
+ 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_common(struct zfcp_erp_action *act)
+{
+ struct zfcp_adapter *adapter = act->adapter;
+ struct zfcp_port *port = act->port;
+ struct zfcp_port *ns_port = adapter->nameserver_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 (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP)
+ return zfcp_erp_open_ptp_port(act);
+ if (!ns_port) {
+ dev_err(&adapter->ccw_device->dev,
+ "Nameserver port unavailable.\n");
+ return ZFCP_ERP_FAILED;
}
- 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");
+ if (!(atomic_read(&ns_port->status) &
+ ZFCP_STATUS_COMMON_UNBLOCKED)) {
/* 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;
+ &ns_port->status);
+ if (zfcp_erp_port_reopen(ns_port, 0, 77, act) >= 0) {
+ act->step = ZFCP_ERP_STEP_NAMESERVER_OPEN;
+ return ZFCP_ERP_CONTINUES;
+ }
+ return ZFCP_ERP_FAILED;
}
/* 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;
+ if (!(atomic_read(&ns_port->status) & ZFCP_STATUS_COMMON_OPEN))
+ return ZFCP_ERP_FAILED;
+ return zfcp_erp_port_strategy_open_lookup(act);
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;
- }
- break;
-
- default:
- ZFCP_LOG_NORMAL("bug: unknown erp step 0x%08x\n",
- erp_action->step);
- retval = ZFCP_ERP_FAILED;
+ if ((p_status & ZFCP_STATUS_COMMON_OPEN) &&
+ (p_status & ZFCP_STATUS_PORT_DID_DID))
+ return ZFCP_ERP_SUCCEEDED;
+ /* fall through otherwise */
}
+ return ZFCP_ERP_FAILED;
+}
- return retval;
+static int zfcp_erp_port_strategy_open(struct zfcp_erp_action *act)
+{
+ if (atomic_read(&act->port->status) & (ZFCP_STATUS_PORT_WKA))
+ return zfcp_erp_port_strategy_open_nameserver(act);
+ return zfcp_erp_port_strategy_open_common(act);
}
-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) {
-
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;
}
+ if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+ return ZFCP_ERP_EXIT;
+ else
+ return zfcp_erp_port_strategy_open(erp_action);
- 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;
+ return ZFCP_ERP_FAILED;
}
-static int
-zfcp_erp_port_strategy_clearstati(struct zfcp_port *port)
+static void zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *unit)
{
- int retval = 0;
-
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;
-}
-
-/*
- * 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)
-{
- 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;
+ 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)
+ 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)
+ 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)
+ 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 +1224,458 @@ 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,
+ unit->scsi_lun, 0);
+ atomic_clear_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status);
+ zfcp_unit_put(unit);
+ 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,
+ "Out of resources. Could not register unit "
+ "0x%016Lx on port 0x%016Lx with SCSI stack.\n",
+ unit->fcp_lun, 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;
+ schedule_work(&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,
+ "Failed registration of rport "
+ "0x%016Lx.\n", 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 && !(atomic_read(&port->status) &
+ ZFCP_STATUS_PORT_WKA)) {
+ 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);
+ daemonize("zfcperp%s", adapter->ccw_device->dev.bus_id);
+ /* 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(4, adapter);
+ down_interruptible(&adapter->erp_ready_sem);
+ zfcp_rec_dbf_event_thread(5, adapter);
+ }
+
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+ wake_up(&adapter->erp_thread_wqh);
+
+ return 0;
+}
+
+/**
+ * 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)
+{
+ 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,
+ "Creation of ERP thread failed.\n");
+ return retval;
+ }
+ wait_event(adapter->erp_thread_wqh,
+ atomic_read(&adapter->status) &
+ ZFCP_STATUS_ADAPTER_ERP_THREAD_UP);
+ return 0;
+}
+
+/**
+ * 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)
+{
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
+ up(&adapter->erp_ready_sem);
+ zfcp_rec_dbf_event_thread_lock(2, 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);
+ dev_err(&adapter->ccw_device->dev, "Adapter ERP failed.\n");
+}
+
+/**
+ * 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);
+
+ if (atomic_read(&port->status) & ZFCP_STATUS_PORT_WKA)
+ dev_err(&port->adapter->ccw_device->dev,
+ "Port ERP failed for WKA port d_id=0x%06x.\n",
+ port->d_id);
else
- list_for_each_entry(unit, &port->unit_list_head, list)
- zfcp_erp_action_dismiss_unit(unit);
+ dev_err(&port->adapter->ccw_device->dev,
+ "Port ERP failed for port wwpn=0x%016Lx.\n",
+ port->wwpn);
}
-static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit)
+/**
+ * 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)
{
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status))
- zfcp_erp_action_dismiss(&unit->erp_action);
+ zfcp_erp_modify_unit_status(unit, id, ref,
+ ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
+
+ dev_err(&unit->port->adapter->ccw_device->dev,
+ "Unit ERP failed for unit 0x%016Lx on port 0x%016Lx.\n",
+ unit->fcp_lun, unit->port->wwpn);
}
-static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
+/**
+ * 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)
{
- list_move(&erp_action->list, &erp_action->adapter->erp_running_head);
- zfcp_rec_dbf_event_action(145, erp_action);
+ 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);
}
-static void zfcp_erp_action_to_ready(struct zfcp_erp_action *erp_action)
+/**
+ * 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)
{
- list_move(&erp_action->list, &erp_action->adapter->erp_ready_head);
- zfcp_rec_dbf_event_action(146, erp_action);
+ 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);
}
+/**
+ * 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)
+{
+ 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 +1687,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 +1700,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 +1720,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 +1735,54 @@ 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))
+ if (!(status & (ZFCP_STATUS_COMMON_ACCESS_DENIED |
+ ZFCP_STATUS_COMMON_ACCESS_BOXED))) {
+ if (!(status & ZFCP_STATUS_PORT_WKA))
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);
+ 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);
}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 6abf178fda5d..edfdb21591f3 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,50 @@
#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 *);
+/* zfcp_aux.c */
+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 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 *, 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 *,
+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);
+
+/* 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_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 +63,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);
+
+/* 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_request(struct zfcp_erp_action *);
+extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
+extern void zfcp_test_link(struct zfcp_port *);
+
+/* zfcp_fsf.c */
+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 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 volatile struct qdio_buffer_element *zfcp_qdio_sbale_req(
+ struct zfcp_fsf_req *);
+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);
+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 void zfcp_set_fcp_dl(struct fcp_cmnd_iu *, fcp_dl_t);
+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_ns_port_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 000000000000..e984469bb98b
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -0,0 +1,567 @@
+/*
+ * 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];
+};
+
+static struct zfcp_port *zfcp_get_port_by_did(struct zfcp_adapter *adapter,
+ u32 d_id)
+{
+ struct zfcp_port *port;
+
+ 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))
+ return port;
+ return NULL;
+}
+
+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) {
+ if (atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
+ continue;
+ /* FIXME: ZFCP_STATUS_PORT_DID_DID check is racy */
+ if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status))
+ /* 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, wwn_t 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_ns_gid_pn_handler(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)
+ goto out;
+ if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT) {
+ atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
+ goto out;
+ }
+ /* paranoia */
+ if (ct_iu_req->wwpn != port->wwpn)
+ goto out;
+ /* 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);
+out:
+ mempool_free(gid_pn, port->adapter->pool.data_gid_pn);
+}
+
+/**
+ * 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_request(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));
+
+ /* setup parameters for send generic command */
+ gid_pn->port = erp_action->port;
+ 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->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;
+
+ ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp,
+ erp_action);
+ if (ret)
+ 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_acc 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_acc *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_acc));
+
+ 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 || retval == -EBUSY)
+ return;
+
+ /* send of ADISC was not possible */
+ zfcp_port_put(port);
+ zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
+}
+
+static int zfcp_scan_get_nameserver(struct zfcp_adapter *adapter)
+{
+ int ret;
+
+ if (!adapter->nameserver_port)
+ return -EINTR;
+
+ if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
+ &adapter->nameserver_port->status)) {
+ ret = zfcp_erp_port_reopen(adapter->nameserver_port, 0, 148,
+ NULL);
+ if (ret)
+ return ret;
+ zfcp_erp_wait(adapter);
+ zfcp_port_put(adapter->nameserver_port);
+ }
+ return !atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
+ &adapter->nameserver_port->status);
+}
+
+static void zfcp_gpn_ft_handler(unsigned long _done)
+{
+ complete((struct completion *)_done);
+}
+
+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 completion done;
+ 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->port = adapter->nameserver_port;
+ ct->handler = zfcp_gpn_ft_handler;
+ ct->handler_data = (unsigned long)&done;
+ 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(&done);
+ ret = zfcp_fsf_send_ct(ct, NULL, NULL);
+ if (!ret)
+ wait_for_completion(&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 == adapter->nameserver_port)
+ return;
+ if ((port->supported_classes != 0) || (port->units != 0)) {
+ 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->port->adapter;
+ struct zfcp_port *port, *tmp;
+ u32 d_id;
+ int ret = 0, x;
+
+ 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; x++) {
+ if (x % (ZFCP_GPN_FT_ENTRIES + 1))
+ acc++;
+ else
+ acc = sg_virt(++sg);
+
+ d_id = acc->port_id[0] << 16 | acc->port_id[1] << 8 |
+ acc->port_id[2];
+
+ /* skip the adapter's port and known remote ports */
+ if (acc->wwpn == fc_host_port_name(adapter->scsi_host) ||
+ zfcp_get_port_by_did(adapter, d_id))
+ 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);
+ if (acc->control & 0x80) /* last entry */
+ break;
+ }
+
+ 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_scan_get_nameserver(adapter);
+ if (ret)
+ return ret;
+
+ gpn_ft = zfcp_alloc_sg_env();
+ if (!gpn_ft)
+ return -ENOMEM;
+
+ 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);
+
+ 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 b2ea4ea051f5..19c1ca913874 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,77 @@ static u32 fsf_qtcb_type[] = {
[FSF_QTCB_UPLOAD_CONTROL_FILE] = FSF_SUPPORT_COMMAND
};
-static const char zfcp_act_subtable_type[5][8] = {
+static const char *zfcp_act_subtable_type[] = {
"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;
+ u16 subtable = table >> 16;
+ u16 rule = table & 0xffff;
- 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);
+ if (subtable && subtable < ARRAY_SIZE(zfcp_act_subtable_type))
+ dev_warn(&adapter->ccw_device->dev,
+ "Access denied in subtable %s, rule %d.\n",
+ zfcp_act_subtable_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, cannot send command to port 0x%016Lx.\n",
+ 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 for unit 0x%016Lx on port 0x%016Lx.\n",
+ unit->fcp_lun, 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,
+ "Required FC class not supported by adapter, "
+ "shutting down adapter.\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 +128,1359 @@ 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;
+ 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;
- /* 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);
- }
+ 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);
+}
- out:
- return retval;
+static void zfcp_fsf_bit_error_threshold(struct zfcp_fsf_req *req)
+{
+ struct zfcp_adapter *adapter = req->adapter;
+ struct fsf_status_read_buffer *sr_buf = req->data;
+ struct fsf_bit_error_payload *err = &sr_buf->payload.bit_error;
+
+ dev_warn(&adapter->ccw_device->dev,
+ "Warning: bit error threshold data "
+ "received for the adapter: "
+ "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",
+ err->link_failure_error_count,
+ err->loss_of_sync_error_count,
+ err->loss_of_signal_error_count,
+ err->primitive_sequence_error_count,
+ err->invalid_transmission_word_error_count,
+ err->crc_error_count);
+ dev_warn(&adapter->ccw_device->dev,
+ "Additional bit error threshold data of the adapter: "
+ "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",
+ err->primitive_sequence_event_timeout_count,
+ err->elastic_buffer_overrun_error_count,
+ err->advertised_receive_b2b_credit,
+ err->current_receive_b2b_credit,
+ err->advertised_transmit_b2b_credit,
+ err->current_transmit_b2b_credit);
}
-/*
- * 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,
+ "The local link is down: no light detected.\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,
+ "The local link is down: wrap plug detected.\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 local link is down: "
+ "adjacent node on link 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 local link is down: "
+ "firmware update in progress.\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 local link is down: "
+ "duplicate or invalid WWPN detected.\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 local link is down: "
+ "no support for NPIV by Fabric.\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 local link is down: "
+ "out of resource in FCP daughtercard.\n");
+ break;
+ case FSF_PSQ_LINK_NO_FABRIC_RESOURCES:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The local link is down: "
+ "out of resource in Fabric.\n");
+ break;
+ case FSF_PSQ_LINK_FABRIC_LOGIN_UNABLE:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "The local link is down: "
+ "unable to login to Fabric.\n");
+ break;
+ case FSF_PSQ_LINK_WWPN_ASSIGNMENT_CORRUPTED:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "WWPN assignment file corrupted on adapter.\n");
+ break;
+ case FSF_PSQ_LINK_MODE_TABLE_CURRUPTED:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "Mode table corrupted on adapter.\n");
+ break;
+ case FSF_PSQ_LINK_NO_WWPN_ASSIGNMENT:
+ dev_warn(&req->adapter->ccw_device->dev,
+ "No WWPN for assignment table on adapter.\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 local link to adapter 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 zfcp_adapter *adapter = req->adapter;
+ 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:
+ dev_warn(&adapter->ccw_device->dev,
+ "Physical link is down.\n");
+ zfcp_fsf_link_down_info_eval(req, 38, ldi);
+ break;
+ case FSF_STATUS_READ_SUB_FDISC_FAILED:
+ dev_warn(&adapter->ccw_device->dev,
+ "Local link is down "
+ "due to failed FDISC login.\n");
+ zfcp_fsf_link_down_info_eval(req, 39, ldi);
+ break;
+ case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE:
+ dev_warn(&adapter->ccw_device->dev,
+ "Local link is down "
+ "due to firmware update on adapter.\n");
+ 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:
+ zfcp_fsf_bit_error_threshold(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,
+ "Local link was replugged.\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);
+ schedule_work(&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,
+ "No recommendation could be given for a "
+ "problem on the adapter.\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,
+ "Command issued by the device driver (0x%x) is "
+ "not known by the adapter.\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,
+ "The QTCB version requested by zfcp (0x%x) is not "
+ "supported by the FCP adapter (lowest supported "
+ "0x%x, highest supported 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,
+ "Packet header type used by the device driver is "
+ "incompatible with that used on the 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,
+ "The request identifier 0x%Lx is ambiguous.\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);
- }
-
- 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);
+ dev_err(&adapter->ccw_device->dev,
+ "Transfer protocol status information"
+ "provided by the adapter (0x%x) "
+ "is not compatible with the device driver.\n",
+ qtcb->prefix.prot_status);
+ zfcp_erp_adapter_shutdown(adapter, 0, 119, 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;
+ }
+ del_timer(&req->timer);
+ zfcp_fsf_protstatus_eval(req);
+ zfcp_fsf_fsfstatus_eval(req);
+ req->handler(req);
- switch (fsf_req->fsf_command) {
+ if (req->erp_action)
+ zfcp_erp_notify(req->erp_action, 0);
+ req->status |= ZFCP_STATUS_FSFREQ_COMPLETED;
- case FSF_QTCB_FCP_CMND:
- zfcp_fsf_send_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_ABORT_FCP_CMND:
- zfcp_fsf_abort_fcp_command_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_SEND_GENERIC:
- zfcp_fsf_send_ct_handler(fsf_req);
- break;
+ bottom = &req->qtcb->bottom.config;
- case FSF_QTCB_OPEN_PORT_WITH_DID:
- zfcp_fsf_open_port_handler(fsf_req);
- break;
+ if (req->data)
+ memcpy(req->data, bottom, sizeof(*bottom));
- case FSF_QTCB_OPEN_LUN:
- zfcp_fsf_open_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_LUN:
- zfcp_fsf_close_unit_handler(fsf_req);
- break;
+ adapter->hydra_version = bottom->adapter_type;
+ adapter->timer_ticks = bottom->timer_interval;
- case FSF_QTCB_CLOSE_PORT:
- zfcp_fsf_close_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_CLOSE_PHYSICAL_PORT:
- zfcp_fsf_close_physical_port_handler(fsf_req);
- break;
+ 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;
+ if (req->erp_action)
+ dev_info(&adapter->ccw_device->dev,
+ "Point-to-Point fibrechannel "
+ "configuration detected.\n");
+ break;
+ case FSF_TOPO_FABRIC:
+ fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+ if (req->erp_action)
+ dev_info(&adapter->ccw_device->dev,
+ "Switched fabric fibrechannel "
+ "network detected.\n");
+ break;
+ case FSF_TOPO_AL:
+ fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
+ dev_err(&adapter->ccw_device->dev,
+ "Unsupported arbitrated loop fibrechannel "
+ "topology detected, shutting down "
+ "adapter.\n");
+ zfcp_erp_adapter_shutdown(adapter, 0, 127, req);
+ return -EIO;
+ default:
+ fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
+ dev_err(&adapter->ccw_device->dev,
+ "The fibrechannel topology reported by the"
+ " adapter is not known by the zfcp driver,"
+ " shutting down adapter.\n");
+ zfcp_erp_adapter_shutdown(adapter, 0, 128, req);
+ return -EIO;
+ }
- case FSF_QTCB_EXCHANGE_CONFIG_DATA:
- zfcp_fsf_exchange_config_data_handler(fsf_req);
- break;
+ return 0;
+}
- case FSF_QTCB_EXCHANGE_PORT_DATA:
- zfcp_fsf_exchange_port_data_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_SEND_ELS:
- zfcp_fsf_send_els_handler(fsf_req);
- break;
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return;
- case FSF_QTCB_DOWNLOAD_CONTROL_FILE:
- zfcp_fsf_control_file_handler(fsf_req);
- break;
+ 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;
- case FSF_QTCB_UPLOAD_CONTROL_FILE:
- zfcp_fsf_control_file_handler(fsf_req);
+ switch (qtcb->header.fsf_status) {
+ case FSF_GOOD:
+ if (zfcp_fsf_exchange_config_evaluate(req))
+ return;
+
+ if (bottom->max_qtcb_size < sizeof(struct fsf_qtcb)) {
+ dev_err(&adapter->ccw_device->dev,
+ "Maximum QTCB size (%d bytes) allowed by "
+ "the adapter is lower than the minimum "
+ "required by the driver (%ld bytes).\n",
+ bottom->max_qtcb_size,
+ sizeof(struct fsf_qtcb));
+ 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;
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
+ &adapter->status);
+
+ zfcp_fsf_link_down_info_eval(req, 42,
+ &qtcb->header.fsf_status_qual.link_down_info);
+ break;
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);
+ zfcp_erp_adapter_shutdown(adapter, 0, 130, req);
+ return;
}
- if (!erp_action)
- return retval;
-
- zfcp_erp_async_handler(erp_action, 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));
+ }
- return retval;
+ if (FSF_QTCB_CURRENT_VERSION < bottom->low_qtcb_version) {
+ dev_err(&adapter->ccw_device->dev,
+ "The adapter only supports newer control block "
+ "versions, try updated device driver.\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 adapter only supports older control block "
+ "versions, consider a microcode upgrade.\n");
+ zfcp_erp_adapter_shutdown(adapter, 0, 126, req);
+ }
}
-/*
- * 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)
+static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req)
{
- 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;
- }
-
- 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;
+ struct zfcp_adapter *adapter = req->adapter;
+ struct fsf_qtcb_bottom_port *bottom = &req->qtcb->bottom.port;
+ struct Scsi_Host *shost = adapter->scsi_host;
- 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;
- }
- memset(status_buffer, 0, sizeof (struct fsf_status_read_buffer));
- fsf_req->data = (unsigned long) status_buffer;
+ if (req->data)
+ memcpy(req->data, bottom, sizeof(*bottom));
- /* 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);
+ 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;
+}
- 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;
- }
+static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req)
+{
+ struct zfcp_adapter *adapter = req->adapter;
+ struct fsf_qtcb *qtcb = req->qtcb;
- ZFCP_LOG_TRACE("Status Read request initiated (adapter%s)\n",
- zfcp_get_busid_by_adapter(adapter));
- goto out;
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return;
- failed_req_send:
- mempool_free(status_buffer, adapter->pool.data_status_read);
+ switch (qtcb->header.fsf_status) {
+ case FSF_GOOD:
+ zfcp_fsf_exchange_port_evaluate(req);
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
+ break;
+ case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
+ zfcp_fsf_exchange_port_evaluate(req);
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
+ zfcp_fsf_link_down_info_eval(req, 43,
+ &qtcb->header.fsf_status_qual.link_down_info);
+ break;
+ }
+}
- 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;
+static int zfcp_fsf_sbal_check(struct zfcp_qdio_queue *queue)
+{
+ spin_lock(&queue->lock);
+ if (atomic_read(&queue->count))
+ return 1;
+ spin_unlock(&queue->lock);
+ return 0;
}
-static int
-zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req)
+static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter)
{
- struct fsf_status_read_buffer *status_buffer;
- struct zfcp_adapter *adapter;
- struct zfcp_port *port;
- unsigned long flags;
+ long ret;
+ struct zfcp_qdio_queue *req_q = &adapter->req_q;
- status_buffer = (struct fsf_status_read_buffer *) fsf_req->data;
- adapter = fsf_req->adapter;
+ spin_unlock(&req_q->lock);
+ ret = wait_event_interruptible_timeout(adapter->request_wq,
+ zfcp_fsf_sbal_check(req_q), 5 * HZ);
+ if (ret > 0)
+ return 0;
- 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))
- break;
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ spin_lock(&req_q->lock);
+ return -EIO;
+}
- 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;
- }
+static struct zfcp_fsf_req *zfcp_fsf_alloc_noqtcb(mempool_t *pool)
+{
+ struct zfcp_fsf_req *req;
+ req = mempool_alloc(pool, GFP_ATOMIC);
+ if (!req)
+ return NULL;
+ memset(req, 0, sizeof(*req));
+ return req;
+}
- switch (status_buffer->status_subtype) {
+static struct zfcp_fsf_req *zfcp_fsf_alloc_qtcb(mempool_t *pool)
+{
+ struct zfcp_fsf_req_qtcb *qtcb;
- case FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT:
- zfcp_erp_port_reopen(port, 0, 101, fsf_req);
- break;
+ 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_SUB_ERROR_PORT:
- zfcp_erp_port_shutdown(port, 0, 122, fsf_req);
- break;
+ memset(qtcb, 0, sizeof(*qtcb));
+ qtcb->fsf_req.qtcb = &qtcb->qtcb;
+ qtcb->fsf_req.pool = pool;
- 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;
+ return &qtcb->fsf_req;
}
-/*
- * 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_req_create(struct zfcp_adapter *adapter,
+ u32 fsf_cmd, int req_flags,
+ 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;
- }
+ volatile struct qdio_buffer_element *sbale;
- zfcp_hba_dbf_event_fsf_unsol("read", adapter, status_buffer);
+ struct zfcp_fsf_req *req;
+ struct zfcp_qdio_queue *req_q = &adapter->req_q;
- switch (status_buffer->status_type) {
+ if (req_flags & ZFCP_REQ_NO_QTCB)
+ req = zfcp_fsf_alloc_noqtcb(pool);
+ else
+ req = zfcp_fsf_alloc_qtcb(pool);
- case FSF_STATUS_READ_PORT_CLOSED:
- zfcp_fsf_status_read_port_closed(fsf_req);
- break;
+ if (unlikely(!req))
+ return ERR_PTR(-EIO);
- case FSF_STATUS_READ_INCOMING_ELS:
- zfcp_fsf_incoming_els(fsf_req);
- break;
+ if (adapter->req_no == 0)
+ adapter->req_no++;
- case FSF_STATUS_READ_SENSE_DATA_AVAIL:
- ZFCP_LOG_INFO("unsolicited sense data received (adapter %s)\n",
- zfcp_get_busid_by_adapter(adapter));
- break;
+ INIT_LIST_HEAD(&req->list);
+ init_timer(&req->timer);
+ init_waitqueue_head(&req->completion_wq);
- 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;
+ 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_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;
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].addr = (void *) req->req_id;
+ sbale[0].flags |= SBAL_FLAGS0_COMMAND;
- 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 (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);
+ }
- 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 (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) {
+ zfcp_fsf_req_free(req);
+ return ERR_PTR(-EIO);
+ }
- 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;
+ if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP))
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- 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;
+ return req;
+}
- 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;
+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;
- 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);
- }
+ /* 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);
+
+ 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;
}
- out:
- return retval;
+
+ /* Don't increase for unsolicited status */
+ if (req->qtcb)
+ adapter->fsf_req_seq_no++;
+
+ return 0;
}
-/*
- * 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
+/**
+ * zfcp_fsf_status_read - send status read request
+ * @adapter: pointer to struct zfcp_adapter
+ * @req_flags: request flags
+ * Returns: 0 on success, ERROR otherwise
*/
-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)
+int zfcp_fsf_status_read(struct zfcp_adapter *adapter)
{
+ struct zfcp_fsf_req *req;
+ struct fsf_status_read_buffer *sr_buf;
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 (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &unit->status)))
- goto unit_blocked;
+ int retval = -EIO;
- 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;
+ spin_lock(&adapter->req_q.lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
- fsf_req->data = (unsigned long) unit;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_UNSOLICITED_STATUS,
+ ZFCP_REQ_NO_QTCB,
+ adapter->pool.fsf_req_status_read);
+ if (unlikely(IS_ERR(req))) {
+ retval = PTR_ERR(req);
+ goto out;
+ }
- /* 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;
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS;
+ sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY;
+ req->sbale_curr = 2;
- /* set handle of request which should be aborted */
- fsf_req->qtcb->bottom.support.req_handle = (u64) old_req_id;
+ 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);
- zfcp_fsf_start_timer(fsf_req, ZFCP_SCSI_ER_TIMEOUT);
- retval = zfcp_fsf_req_send(fsf_req);
- if (!retval)
- goto out;
+ retval = zfcp_fsf_req_send(req);
+ if (retval)
+ goto failed_req_send;
- unit_blocked:
- zfcp_fsf_req_free(fsf_req);
- fsf_req = NULL;
+ goto out;
- out:
- write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
- return fsf_req;
+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(&adapter->req_q.lock);
+ return retval;
}
-/*
- * function: zfcp_fsf_abort_fcp_command_handler
- *
- * purpose: is called for finished Abort FCP Command request
- *
- * returns:
- */
-static int
-zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *new_fsf_req)
+static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
{
- int retval = -EINVAL;
- struct zfcp_unit *unit;
- union fsf_status_qual *fsf_stat_qual =
- &new_fsf_req->qtcb->header.fsf_status_qual;
-
- if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- /* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */
- goto skip_fsfstatus;
- }
-
- unit = (struct zfcp_unit *) new_fsf_req->data;
+ 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 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 (!atomic_read(&adapter->req_q.count))
+ goto out;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_ABORT_FCP_CMND,
+ req_flags, adapter->pool.fsf_req_abort);
+ if (unlikely(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 zfcp_port *port = send_ct->port;
+ 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;
+ zfcp_fsf_access_denied_port(req, port);
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;
+ zfcp_erp_port_boxed(port, 49, req);
+ 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;
+ struct zfcp_port *port = ct->port;
+ struct zfcp_adapter *adapter = port->adapter;
+ struct zfcp_fsf_req *req;
+ int ret = -EIO;
- 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;
- }
+ spin_lock(&adapter->req_q.lock);
+ if (zfcp_fsf_req_sbal_get(adapter))
+ goto out;
- if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &els->port->status))) {
- ret = -EBUSY;
- goto port_blocked;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_GENERIC,
+ ZFCP_REQ_AUTO_CLEANUP, pool);
+ if (unlikely(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 = 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);
- failed_req:
- out:
- write_unlock_irqrestore(&adapter->request_queue.queue_lock,
- lock_flags);
+ 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;
+
+ goto out;
- return ret;
+failed_send:
+ zfcp_fsf_req_free(req);
+ if (erp_action)
+ erp_action->fsf_req = NULL;
+out:
+ spin_unlock(&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 (!atomic_read(&adapter->req_q.count))
+ goto out;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS,
+ ZFCP_REQ_AUTO_CLEANUP, NULL);
+ if (unlikely(IS_ERR(req))) {
+ ret = PTR_ERR(req);
+ goto out;
+ }
+
+ ret = zfcp_fsf_setup_sbals(req, els->req, els->resp,
+ FSF_MAX_SBALS_PER_ELS_REQ);
+ 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 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(&adapter->req_q.lock);
+ if (!atomic_read(&adapter->req_q.count))
+ goto out;
+ req = zfcp_fsf_req_create(adapter,
+ FSF_QTCB_EXCHANGE_CONFIG_DATA,
+ 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(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(&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 zfcp_fsf_req *req = NULL;
+ int retval = -EIO;
+
+ spin_lock(&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 (unlikely(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(&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 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(&adapter->req_q.lock);
+ if (!atomic_read(&adapter->req_q.count))
+ goto out;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
+ 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(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(&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 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(&adapter->req_q.lock);
+ if (!atomic_read(&adapter->req_q.count))
+ goto out;
+
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, 0,
+ NULL);
+ if (unlikely(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(&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 */
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
goto skip_fsfstatus;
- }
- /* 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,
+ "The adapter is out of resources. The remote port "
+ "0x%016Lx could not be opened, disabling it.\n",
+ 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,
+ "The remote port 0x%016Lx could not be "
+ "opened. Disabling it.\n", 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 +1496,168 @@ 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);
- }
+ if (atomic_read(&port->status) & ZFCP_STATUS_PORT_NO_WWPN)
+ break;
+
+ 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:
+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 zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_fsf_req *req;
+ int retval = -EIO;
+
+ spin_lock(&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(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;
-
- zfcp_erp_start_timer(fsf_req);
- retval = zfcp_fsf_req_send(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;
+ atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status);
+
+ 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(&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;
+ struct zfcp_port *port = req->data;
- port = (struct zfcp_port *) fsf_req->data;
-
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- /* don't change port status in our bookkeeping */
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
goto skip_fsfstatus;
- }
-
- /* evaluate FSF status in QTCB */
- switch (fsf_req->qtcb->header.fsf_status) {
+ 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:
+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 zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_fsf_req *req;
+ int retval = -EIO;
+ spin_lock(&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 (unlikely(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;
+ atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status);
+
+ 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;
- 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);
+out:
+ spin_unlock(&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 int
-zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_close_physical_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 zfcp_unit *unit;
- 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 */
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
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 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 +1665,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 zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_fsf_req *req;
+ int retval = -EIO;
+
+ spin_lock(&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 (unlikely(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;
- 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;
+ 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);
- 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(&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 */
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
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;
atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
ZFCP_STATUS_COMMON_ACCESS_BOXED |
@@ -3002,155 +1754,65 @@ 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,
+ "FCP-LUN 0x%Lx at the remote port "
+ "with WWPN 0x%Lx "
+ "connected to the adapter "
+ "is already in use in LPAR%d, CSS%d.\n",
+ unit->fcp_lun,
+ unit->port->wwpn,
+ queue_designator->hla,
+ queue_designator->cssid);
+ 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,
+ "The adapter ran out of resources. There is no "
+ "handle available for unit 0x%016Lx on port 0x%016Lx.",
+ unit->fcp_lun, 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 +1830,629 @@ 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,
+ "Read-only access for unit 0x%016Lx "
+ "on port 0x%016Lx.\n",
+ unit->fcp_lun, 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 access of read-only unit "
+ "0x%016Lx on port 0x%016Lx not "
+ "supported, disabling unit.\n",
+ unit->fcp_lun, 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 access of read-write unit "
+ "0x%016Lx on port 0x%016Lx not "
+ "supported, disabling unit.\n",
+ unit->fcp_lun, 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:
+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 zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_fsf_req *req;
+ int retval = -EIO;
+
+ spin_lock(&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 (unlikely(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;
+
+ atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status);
+
+ 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(&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;
-
- unit = (struct zfcp_unit *) fsf_req->data;
+ struct zfcp_unit *unit = req->data;
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- /* don't change unit status in our bookkeeping */
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
goto skip_fsfstatus;
- }
-
- /* evaluate FSF status in QTCB */
- switch (fsf_req->qtcb->header.fsf_status) {
+ 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:
+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);
+ volatile 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(&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 (unlikely(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;
+ atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->unit->status);
- 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(&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;
-
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
- goto skip_fsfstatus;
- }
+ &(req->qtcb->bottom.io.fcp_rsp);
+ char *fcp_rsp_info = (unsigned char *) &fcp_rsp_iu[1];
- /* 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,
+ "Invalid data direction (%d) given for unit "
+ "0x%016Lx on port 0x%016Lx, shutting down "
+ "adapter.\n",
+ req->qtcb->bottom.io.data_direction,
+ unit->fcp_lun, 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,
+ "An invalid control-data-block length field (%d) "
+ "was found in a command for unit 0x%016Lx on port "
+ "0x%016Lx. Shutting down adapter.\n",
+ req->qtcb->bottom.io.fcp_cmnd_length,
+ unit->fcp_lun, 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;
-}
-
-static inline int
-zfcp_fsf_req_sbal_check(unsigned long *flags,
- struct zfcp_qdio_queue *queue, int needed)
-{
- 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;
-}
-
-/*
- * 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;
+ 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);
}
}
/**
- * 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 (!atomic_read(&adapter->req_q.count))
+ goto out;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
+ adapter->pool.fsf_req_scsi);
+ if (unlikely(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(fcp_dl_t);
+
+ 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,
+ "SCSI request too large. "
+ "Shutting down unit 0x%016Lx on port "
+ "0x%016Lx.\n", unit->fcp_lun,
+ 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 zfcp_fsf_req *req = NULL;
+ struct fcp_cmnd_iu *fcp_cmnd_iu;
- adapter = fsf_req->adapter;
- req_queue = &adapter->request_queue,
+ if (unlikely(!(atomic_read(&unit->status) &
+ ZFCP_STATUS_COMMON_UNBLOCKED)))
+ return NULL;
+ spin_lock(&adapter->req_q.lock);
+ if (!atomic_read(&adapter->req_q.count))
+ goto out;
+ req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
+ adapter->pool.fsf_req_scsi);
+ if (unlikely(IS_ERR(req)))
+ goto out;
- /* 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);
+ 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(fcp_dl_t);
+
+ sbale = zfcp_qdio_sbale_req(req);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
- /* 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);
+ 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;
- inc_seq_no = (fsf_req->qtcb != NULL);
+ zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT);
+ if (!zfcp_fsf_req_send(req))
+ goto out;
- 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_req_free(req);
+ req = NULL;
+out:
+ spin_unlock(&adapter->req_q.lock);
+ return req;
+}
- 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]);
+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;
+}
- /*
- * 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);
+/**
+ * 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)
+{
+ volatile struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *req = NULL;
+ struct fsf_qtcb_bottom_support *bottom;
+ int direction, retval = -EIO, bytes;
+
+ if (!(adapter->adapter_features & FSF_FEATURE_CFDC))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ 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(&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 (unlikely(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(&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 099970b27001..bf94b4da0763 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 */
@@ -258,6 +247,16 @@
#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;
@@ -288,6 +287,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 +309,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 +327,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 +449,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 8ca5f074c687..d6dbd653fde9 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
-
-/*
- * 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]);
-}
+/* 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))
-/*
- * 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 volatile 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, "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;
-
- adapter = (struct zfcp_adapter *) int_parm;
- queue = &adapter->request_queue;
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *) parm;
+ struct zfcp_qdio_queue *queue = &adapter->req_q;
- 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,203 +108,114 @@ 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;
+ volatile 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,
+ "Protocol violation by adapter. "
+ "Continuing operations.\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
- */
-static inline volatile struct qdio_buffer_element *
-zfcp_qdio_sbale_get(struct zfcp_qdio_queue *queue, int sbal, int sbale)
-{
- return &queue->buffer[sbal]->element[sbale];
-}
-
-/**
- * zfcp_qdio_sbale_req - return pointer to SBALE of request_queue for
- * a struct zfcp_fsf_req
+ * 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
*/
volatile struct qdio_buffer_element *
-zfcp_qdio_sbale_req(struct zfcp_fsf_req *fsf_req, int sbal, int sbale)
+zfcp_qdio_sbale_req(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, 0);
}
/**
- * 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)
-{
- 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
+ * 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_curr(struct zfcp_fsf_req *fsf_req)
+zfcp_qdio_sbale_curr(struct zfcp_fsf_req *req)
{
- return zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr,
- fsf_req->sbale_curr);
+ return zfcp_qdio_sbale(&req->adapter->req_q, req->sbal_last,
+ 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)
+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 *
zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
{
@@ -455,16 +226,16 @@ zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
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 +250,246 @@ 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 *
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)
+static void zfcp_qdio_undo_sbals(struct zfcp_fsf_req *fsf_req)
{
- return zfcp_qdio_sbals_zero(&fsf_req->adapter->request_queue,
- fsf_req->sbal_first, fsf_req->sbal_curr);
+ 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_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 int zfcp_qdio_fill_sbals(struct zfcp_fsf_req *fsf_req,
+ unsigned int sbtype, void *start_addr,
+ unsigned int total_length)
{
volatile struct qdio_buffer_element *sbale;
-
- sbale = zfcp_qdio_sbale_curr(fsf_req);
- sbale->addr = addr;
- sbale->length = length;
-}
-
-/**
- * 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)
-{
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) {
+ 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;
+ 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_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_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_sbals_from_scsicmnd(struct zfcp_fsf_req *fsf_req,
- unsigned long sbtype, struct scsi_cmnd *scsi_cmnd)
+int zfcp_qdio_send(struct zfcp_fsf_req *fsf_req)
{
- return zfcp_qdio_sbals_from_sg(fsf_req, sbtype, scsi_sglist(scsi_cmnd),
- scsi_sg_count(scsi_cmnd),
- ZFCP_MAX_SBALS_PER_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;
+ volatile struct qdio_buffer_element *sbale;
+
+ /* acknowledgements for transferred buffers */
+ pci_batch = 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;
+ req_q->pci_batch = pci_batch;
+ return 0;
}
/**
- * zfcp_qdio_determine_pci - set PCI flag in first SBALE on qdio queue if needed
+ * 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_determine_pci(struct zfcp_qdio_queue *req_queue,
- struct zfcp_fsf_req *fsf_req)
+int zfcp_qdio_allocate(struct zfcp_adapter *adapter)
{
- int new_distance_from_int;
- int pci_pos;
- volatile struct qdio_buffer_element *sbale;
+ struct qdio_initialize *init_data;
- 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;
- }
- return new_distance_from_int;
+ 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);
}
-/*
- * function: zfcp_zero_sbals
- *
- * purpose: zeros specified range of SBALs
- *
- * returns:
+/**
+ * zfcp_close_qdio - close qdio queues for an adapter
*/
-void
-zfcp_qdio_zero_sbals(struct qdio_buffer *buf[], int first, int clean_count)
+void zfcp_qdio_close(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 zfcp_qdio_queue *req_q;
+ int first, count;
+
+ if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status))
+ return;
+
+ /* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */
+ req_q = &adapter->req_q;
+ spin_lock(&req_q->lock);
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
+ spin_unlock(&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);
}
+ req_q->first = 0;
+ atomic_set(&req_q->count, 0);
+ req_q->pci_batch = 0;
+ adapter->resp_q.first = 0;
+ atomic_set(&adapter->resp_q.count, 0);
}
-#undef ZFCP_LOG_AREA
+/**
+ * zfcp_qdio_open - prepare and initialize response queue
+ * @adapter: pointer to struct zfcp_adapter
+ * Returns: 0 on success, otherwise -EIO
+ */
+int zfcp_qdio_open(struct zfcp_adapter *adapter)
+{
+ volatile struct qdio_buffer_element *sbale;
+ int cc;
+
+ if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status))
+ return -EIO;
+
+ if (qdio_establish(&adapter->qdio_init_data)) {
+ dev_err(&adapter->ccw_device->dev,
+ "Establish of QDIO queues failed.\n");
+ return -EIO;
+ }
+
+ if (qdio_activate(adapter->ccw_device)) {
+ dev_err(&adapter->ccw_device->dev,
+ "Activate of QDIO queues failed.\n");
+ 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;
+ }
+
+ if (do_QDIO(adapter->ccw_device, QDIO_FLAG_SYNC_INPUT, 0, 0,
+ QDIO_MAX_BUFFERS_PER_Q)) {
+ dev_err(&adapter->ccw_device->dev,
+ "Init of QDIO response queue failed.\n");
+ 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);
+ return -EIO;
+}
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 01687559dc06..aeae56b00b45 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -1,220 +1,65 @@
/*
- * 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)
+void zfcp_set_fcp_dl(struct fcp_cmnd_iu *fcp_cmd, fcp_dl_t fcp_dl)
{
- int additional_length = fcp_cmd->add_fcp_cdb_length << 2;
- fcp_dl_t *fcp_dl_addr;
+ fcp_dl_t *fcp_dl_ptr;
- 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_ptr = (fcp_dl_t *) ((unsigned char *) &fcp_cmd[1] +
+ (fcp_cmd->add_fcp_cdb_length << 2));
+ *fcp_dl_ptr = fcp_dl;
}
-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,114 +68,13 @@ 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)
-{
- int tmp;
- int retval;
-
- retval = 0;
-
- BUG_ON((adapter == NULL) || (adapter != unit->port->adapter));
- BUG_ON(scpnt->scsi_done == NULL);
-
- if (unlikely(NULL == unit)) {
- zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT);
- goto out;
- }
-
- 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));
- zfcp_scsi_command_fail(scpnt, DID_ERROR);
- goto out;
- }
-
- tmp = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, use_timer,
- 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);
- 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;
- }
-
-out:
- return retval;
-}
-
-static void
-zfcp_scsi_command_sync_handler(struct scsi_cmnd *scpnt)
-{
- 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);
-
- scpnt->SCp.ptr = NULL;
-
- return 0;
-}
-
-/*
- * 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_queuecommand(struct scsi_cmnd *scpnt,
+ void (*done) (struct scsi_cmnd *))
{
struct zfcp_unit *unit;
struct zfcp_adapter *adapter;
+ int status;
+ int ret;
/* reset the status for this request */
scpnt->result = 0;
@@ -342,44 +86,76 @@ zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
* (stored there by zfcp_scsi_slave_alloc)
*/
adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
- unit = (struct zfcp_unit *) scpnt->device->hostdata;
+ unit = scpnt->device->hostdata;
+
+ BUG_ON(!adapter || (adapter != unit->port->adapter));
+ BUG_ON(!scpnt->scsi_done);
- return zfcp_scsi_command_async(adapter, unit, scpnt, 0);
+ if (unlikely(!unit)) {
+ zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT);
+ return 0;
+ }
+
+ 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);
+ return 0;;
+ }
+
+ ret = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, 0,
+ ZFCP_REQ_AUTO_CLEANUP);
+ if (unlikely(ret == -EBUSY))
+ zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT);
+ else if (unlikely(ret < 0))
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ return ret;
}
-static struct zfcp_unit *
-zfcp_unit_lookup(struct zfcp_adapter *adapter, int channel, unsigned int id,
- unsigned int lun)
+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;
+ struct zfcp_unit *unit;
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;
- }
+ if (lun == unit->scsi_lun)
+ return unit;
}
- out:
+
+ return NULL;
+}
+
+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_read(&unit->status) & ZFCP_STATUS_UNIT_REGISTERED)) {
+ 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_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 +163,37 @@ 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->data = NULL;
fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTING;
- old_req_id = fsf_req->req_id;
/* 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 +201,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 +234,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,
+ "registration with SCSI stack failed.");
+ 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,10 +325,12 @@ 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);
@@ -604,9 +341,6 @@ zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
return;
}
-/*
- * Support functions for FC transport class
- */
static struct fc_host_statistics*
zfcp_init_fc_host_stats(struct zfcp_adapter *adapter)
{
@@ -622,13 +356,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 +372,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 +414,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 +433,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 +460,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 +509,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 +519,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 000000000000..2e85c6c49e7d
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -0,0 +1,496 @@
+/*
+ * 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",
+ adapter->peer_wwnn);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwpn, "0x%016llx\n",
+ 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;
+ wwn_t wwpn;
+ int retval = 0;
+
+ down(&zfcp_data.config_sema);
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE) {
+ retval = -EBUSY;
+ goto out;
+ }
+
+ if (strict_strtoull(buf, 0, &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, &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 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;
+ fcp_lun_t 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, &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;
+ fcp_lun_t fcp_lun;
+ int retval = 0;
+
+ down(&zfcp_data.config_sema);
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
+ retval = -EBUSY;
+ goto out;
+ }
+
+ if (strict_strtoull(buf, 0, &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, &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);
+
+static struct attribute *zfcp_port_ns_attrs[] = {
+ &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_ns_port_attrs - sysfs attributes for nameserver
+ */
+struct attribute_group zfcp_sysfs_ns_port_attrs = {
+ .attrs = zfcp_port_ns_attrs,
+};
+
+static struct attribute *zfcp_port_no_ns_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_no_ns_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",
+ unit->port->adapter->ccw_device->dev.bus_id);
+ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n", unit->port->wwpn);
+ZFCP_DEFINE_SCSI_ATTR(fcp_lun, "0x%016llx\n", 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);
+
+struct device_attribute *zfcp_sysfs_shost_attrs[] = {
+ &dev_attr_utilization,
+ &dev_attr_requests,
+ &dev_attr_megabytes,
+ &dev_attr_seconds_active,
+ NULL
+};
diff --git a/drivers/s390/scsi/zfcp_sysfs_adapter.c b/drivers/s390/scsi/zfcp_sysfs_adapter.c
deleted file mode 100644
index ccbba4dd3a77..000000000000
--- 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 651edd58906a..000000000000
--- 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 703c1b5cb602..000000000000
--- 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 80fb2c2cf48a..000000000000
--- 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