summaryrefslogtreecommitdiff
path: root/drivers/s390
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/Kconfig30
-rw-r--r--drivers/s390/block/dasd.c310
-rw-r--r--drivers/s390/block/dasd_3370_erp.c27
-rw-r--r--drivers/s390/block/dasd_3990_erp.c189
-rw-r--r--drivers/s390/block/dasd_9336_erp.c27
-rw-r--r--drivers/s390/block/dasd_9343_erp.c2
-rw-r--r--drivers/s390/block/dasd_devmap.c193
-rw-r--r--drivers/s390/block/dasd_diag.c41
-rw-r--r--drivers/s390/block/dasd_diag.h2
-rw-r--r--drivers/s390/block/dasd_eckd.c366
-rw-r--r--drivers/s390/block/dasd_eckd.h24
-rw-r--r--drivers/s390/block/dasd_eer.c8
-rw-r--r--drivers/s390/block/dasd_erp.c9
-rw-r--r--drivers/s390/block/dasd_fba.c54
-rw-r--r--drivers/s390/block/dasd_fba.h2
-rw-r--r--drivers/s390/block/dasd_genhd.c17
-rw-r--r--drivers/s390/block/dasd_int.h41
-rw-r--r--drivers/s390/block/dasd_ioctl.c15
-rw-r--r--drivers/s390/block/dasd_proc.c1
-rw-r--r--drivers/s390/block/xpram.c161
-rw-r--r--drivers/s390/char/Makefile1
-rw-r--r--drivers/s390/char/con3215.c3
-rw-r--r--drivers/s390/char/con3270.c1
-rw-r--r--drivers/s390/char/ctrlchar.c3
-rw-r--r--drivers/s390/char/defkeymap.c6
-rw-r--r--drivers/s390/char/fs3270.c14
-rw-r--r--drivers/s390/char/keyboard.c7
-rw-r--r--drivers/s390/char/monreader.c1
-rw-r--r--drivers/s390/char/monwriter.c292
-rw-r--r--drivers/s390/char/raw3270.c154
-rw-r--r--drivers/s390/char/raw3270.h2
-rw-r--r--drivers/s390/char/sclp.c41
-rw-r--r--drivers/s390/char/sclp_con.c1
-rw-r--r--drivers/s390/char/sclp_cpi.c1
-rw-r--r--drivers/s390/char/sclp_quiesce.c4
-rw-r--r--drivers/s390/char/sclp_rw.c1
-rw-r--r--drivers/s390/char/sclp_tty.c1
-rw-r--r--drivers/s390/char/sclp_vt220.c1
-rw-r--r--drivers/s390/char/tape.h1
-rw-r--r--drivers/s390/char/tape_34xx.c7
-rw-r--r--drivers/s390/char/tape_3590.c1
-rw-r--r--drivers/s390/char/tape_block.c1
-rw-r--r--drivers/s390/char/tape_char.c1
-rw-r--r--drivers/s390/char/tape_class.c10
-rw-r--r--drivers/s390/char/tape_core.c19
-rw-r--r--drivers/s390/char/tape_proc.c1
-rw-r--r--drivers/s390/char/tape_std.c1
-rw-r--r--drivers/s390/char/tty3270.c29
-rw-r--r--drivers/s390/char/vmcp.c2
-rw-r--r--drivers/s390/char/vmcp.h2
-rw-r--r--drivers/s390/char/vmlogrdr.c6
-rw-r--r--drivers/s390/char/vmwatchdog.c54
-rw-r--r--drivers/s390/cio/blacklist.c36
-rw-r--r--drivers/s390/cio/ccwgroup.c43
-rw-r--r--drivers/s390/cio/chsc.c54
-rw-r--r--drivers/s390/cio/cio.c102
-rw-r--r--drivers/s390/cio/cio.h3
-rw-r--r--drivers/s390/cio/cmf.c628
-rw-r--r--drivers/s390/cio/css.c298
-rw-r--r--drivers/s390/cio/css.h4
-rw-r--r--drivers/s390/cio/device.c147
-rw-r--r--drivers/s390/cio/device.h10
-rw-r--r--drivers/s390/cio/device_fsm.c166
-rw-r--r--drivers/s390/cio/device_id.c39
-rw-r--r--drivers/s390/cio/device_ops.c31
-rw-r--r--drivers/s390/cio/device_pgid.c215
-rw-r--r--drivers/s390/cio/device_status.c8
-rw-r--r--drivers/s390/cio/ioasm.h220
-rw-r--r--drivers/s390/cio/qdio.c7
-rw-r--r--drivers/s390/cio/qdio.h208
-rw-r--r--drivers/s390/crypto/Makefile15
-rw-r--r--drivers/s390/crypto/ap_bus.c1221
-rw-r--r--drivers/s390/crypto/ap_bus.h158
-rw-r--r--drivers/s390/crypto/z90common.h166
-rw-r--r--drivers/s390/crypto/z90crypt.h71
-rw-r--r--drivers/s390/crypto/z90hardware.c2531
-rw-r--r--drivers/s390/crypto/z90main.c3380
-rw-r--r--drivers/s390/crypto/zcrypt_api.c1091
-rw-r--r--drivers/s390/crypto/zcrypt_api.h141
-rw-r--r--drivers/s390/crypto/zcrypt_cca_key.h350
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c435
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.h126
-rw-r--r--drivers/s390/crypto/zcrypt_error.h133
-rw-r--r--drivers/s390/crypto/zcrypt_mono.c100
-rw-r--r--drivers/s390/crypto/zcrypt_pcica.c418
-rw-r--r--drivers/s390/crypto/zcrypt_pcica.h117
-rw-r--r--drivers/s390/crypto/zcrypt_pcicc.c630
-rw-r--r--drivers/s390/crypto/zcrypt_pcicc.h176
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c951
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.h79
-rw-r--r--drivers/s390/net/Kconfig9
-rw-r--r--drivers/s390/net/Makefile1
-rw-r--r--drivers/s390/net/claw.c2
-rw-r--r--drivers/s390/net/ctcmain.c24
-rw-r--r--drivers/s390/net/fsm.c1
-rw-r--r--drivers/s390/net/iucv.c46
-rw-r--r--drivers/s390/net/lcs.c20
-rw-r--r--drivers/s390/net/netiucv.c82
-rw-r--r--drivers/s390/net/qeth.h73
-rw-r--r--drivers/s390/net/qeth_eddp.c6
-rw-r--r--drivers/s390/net/qeth_main.c557
-rw-r--r--drivers/s390/net/qeth_proc.c23
-rw-r--r--drivers/s390/net/qeth_sys.c68
-rw-r--r--drivers/s390/net/qeth_tso.h2
-rw-r--r--drivers/s390/net/smsgiucv.c14
-rw-r--r--drivers/s390/s390mach.c125
-rw-r--r--drivers/s390/scsi/zfcp_aux.c204
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c18
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c13
-rw-r--r--drivers/s390/scsi/zfcp_def.h45
-rw-r--r--drivers/s390/scsi/zfcp_erp.c397
-rw-r--r--drivers/s390/scsi/zfcp_ext.h25
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c415
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c78
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c174
-rw-r--r--drivers/s390/sysinfo.c456
116 files changed, 10170 insertions, 9404 deletions
diff --git a/drivers/s390/Kconfig b/drivers/s390/Kconfig
index 4d36208ff8de..ae89b9b88743 100644
--- a/drivers/s390/Kconfig
+++ b/drivers/s390/Kconfig
@@ -213,17 +213,35 @@ config MONREADER
help
Character device driver for reading z/VM monitor service records
+config MONWRITER
+ tristate "API for writing z/VM monitor service records"
+ default "m"
+ help
+ Character device driver for writing z/VM monitor service records
+
endmenu
menu "Cryptographic devices"
-config Z90CRYPT
+config ZCRYPT
tristate "Support for PCI-attached cryptographic adapters"
- default "m"
- help
+ select ZCRYPT_MONOLITHIC if ZCRYPT="y"
+ default "m"
+ help
Select this option if you want to use a PCI-attached cryptographic
- adapter like the PCI Cryptographic Accelerator (PCICA) or the PCI
- Cryptographic Coprocessor (PCICC). This option is also available
- as a module called z90crypt.ko.
+ adapter like:
+ + PCI Cryptographic Accelerator (PCICA)
+ + PCI Cryptographic Coprocessor (PCICC)
+ + PCI-X Cryptographic Coprocessor (PCIXCC)
+ + Crypto Express2 Coprocessor (CEX2C)
+ + Crypto Express2 Accelerator (CEX2A)
+
+config ZCRYPT_MONOLITHIC
+ bool "Monolithic zcrypt module"
+ depends on ZCRYPT="m"
+ help
+ Select this option if you want to have a single module z90crypt.ko
+ that contains all parts of the crypto device driver (ap bus,
+ request router and all the card drivers).
endmenu
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index cfb1fff3787c..d0647d116eaa 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -9,7 +9,6 @@
*
*/
-#include <linux/config.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -53,7 +52,7 @@ static void dasd_setup_queue(struct dasd_device * device);
static void dasd_free_queue(struct dasd_device * device);
static void dasd_flush_request_queue(struct dasd_device *);
static void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *);
-static void dasd_flush_ccw_queue(struct dasd_device *, int);
+static int dasd_flush_ccw_queue(struct dasd_device *, int);
static void dasd_tasklet(struct dasd_device *);
static void do_kick_device(void *data);
@@ -61,6 +60,7 @@ static void do_kick_device(void *data);
* SECTION: Operations on the device structure.
*/
static wait_queue_head_t dasd_init_waitq;
+static wait_queue_head_t dasd_flush_wq;
/*
* Allocate memory for a new device structure.
@@ -95,7 +95,7 @@ dasd_alloc_device(void)
spin_lock_init(&device->mem_lock);
spin_lock_init(&device->request_queue_lock);
atomic_set (&device->tasklet_scheduled, 0);
- tasklet_init(&device->tasklet,
+ tasklet_init(&device->tasklet,
(void (*)(unsigned long)) dasd_tasklet,
(unsigned long) device);
INIT_LIST_HEAD(&device->ccw_queue);
@@ -122,13 +122,13 @@ dasd_free_device(struct dasd_device *device)
/*
* Make a new device known to the system.
*/
-static inline int
+static int
dasd_state_new_to_known(struct dasd_device *device)
{
int rc;
/*
- * As long as the device is not in state DASD_STATE_NEW we want to
+ * As long as the device is not in state DASD_STATE_NEW we want to
* keep the reference count > 0.
*/
dasd_get_device(device);
@@ -146,7 +146,7 @@ dasd_state_new_to_known(struct dasd_device *device)
/*
* Let the system forget about a device.
*/
-static inline void
+static int
dasd_state_known_to_new(struct dasd_device * device)
{
/* Disable extended error reporting for this device. */
@@ -164,12 +164,13 @@ dasd_state_known_to_new(struct dasd_device * device)
/* Give up reference we took in dasd_state_new_to_known. */
dasd_put_device(device);
+ return 0;
}
/*
* Request the irq line for the device.
*/
-static inline int
+static int
dasd_state_known_to_basic(struct dasd_device * device)
{
int rc;
@@ -183,7 +184,7 @@ dasd_state_known_to_basic(struct dasd_device * device)
device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2,
8 * sizeof (long));
debug_register_view(device->debug_area, &debug_sprintf_view);
- debug_set_level(device->debug_area, DBF_EMERG);
+ debug_set_level(device->debug_area, DBF_WARNING);
DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created");
device->state = DASD_STATE_BASIC;
@@ -193,17 +194,23 @@ dasd_state_known_to_basic(struct dasd_device * device)
/*
* Release the irq line for the device. Terminate any running i/o.
*/
-static inline void
+static int
dasd_state_basic_to_known(struct dasd_device * device)
{
+ int rc;
+
dasd_gendisk_free(device);
- dasd_flush_ccw_queue(device, 1);
+ rc = dasd_flush_ccw_queue(device, 1);
+ if (rc)
+ return rc;
+
DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device);
if (device->debug_area != NULL) {
debug_unregister(device->debug_area);
device->debug_area = NULL;
}
device->state = DASD_STATE_KNOWN;
+ return 0;
}
/*
@@ -220,7 +227,7 @@ dasd_state_basic_to_known(struct dasd_device * device)
* In case the analysis returns an error, the device setup is stopped
* (a fake disk was already added to allow formatting).
*/
-static inline int
+static int
dasd_state_basic_to_ready(struct dasd_device * device)
{
int rc;
@@ -248,25 +255,31 @@ dasd_state_basic_to_ready(struct dasd_device * device)
* Forget format information. Check if the target level is basic
* and if it is create fake disk for formatting.
*/
-static inline void
+static int
dasd_state_ready_to_basic(struct dasd_device * device)
{
- dasd_flush_ccw_queue(device, 0);
+ int rc;
+
+ rc = dasd_flush_ccw_queue(device, 0);
+ if (rc)
+ return rc;
dasd_destroy_partitions(device);
dasd_flush_request_queue(device);
device->blocks = 0;
device->bp_block = 0;
device->s2b_shift = 0;
device->state = DASD_STATE_BASIC;
+ return 0;
}
/*
* Back to basic.
*/
-static inline void
+static int
dasd_state_unfmt_to_basic(struct dasd_device * device)
{
device->state = DASD_STATE_BASIC;
+ return 0;
}
/*
@@ -274,7 +287,7 @@ dasd_state_unfmt_to_basic(struct dasd_device * device)
* the requeueing of requests from the linux request queue to the
* ccw queue.
*/
-static inline int
+static int
dasd_state_ready_to_online(struct dasd_device * device)
{
device->state = DASD_STATE_ONLINE;
@@ -285,16 +298,17 @@ dasd_state_ready_to_online(struct dasd_device * device)
/*
* Stop the requeueing of requests again.
*/
-static inline void
+static int
dasd_state_online_to_ready(struct dasd_device * device)
{
device->state = DASD_STATE_READY;
+ return 0;
}
/*
* Device startup state changes.
*/
-static inline int
+static int
dasd_increase_state(struct dasd_device *device)
{
int rc;
@@ -330,30 +344,37 @@ dasd_increase_state(struct dasd_device *device)
/*
* Device shutdown state changes.
*/
-static inline int
+static int
dasd_decrease_state(struct dasd_device *device)
{
+ int rc;
+
+ rc = 0;
if (device->state == DASD_STATE_ONLINE &&
device->target <= DASD_STATE_READY)
- dasd_state_online_to_ready(device);
-
- if (device->state == DASD_STATE_READY &&
+ rc = dasd_state_online_to_ready(device);
+
+ if (!rc &&
+ device->state == DASD_STATE_READY &&
device->target <= DASD_STATE_BASIC)
- dasd_state_ready_to_basic(device);
+ rc = dasd_state_ready_to_basic(device);
- if (device->state == DASD_STATE_UNFMT &&
+ if (!rc &&
+ device->state == DASD_STATE_UNFMT &&
device->target <= DASD_STATE_BASIC)
- dasd_state_unfmt_to_basic(device);
+ rc = dasd_state_unfmt_to_basic(device);
- if (device->state == DASD_STATE_BASIC &&
+ if (!rc &&
+ device->state == DASD_STATE_BASIC &&
device->target <= DASD_STATE_KNOWN)
- dasd_state_basic_to_known(device);
-
- if (device->state == DASD_STATE_KNOWN &&
+ rc = dasd_state_basic_to_known(device);
+
+ if (!rc &&
+ device->state == DASD_STATE_KNOWN &&
device->target <= DASD_STATE_NEW)
- dasd_state_known_to_new(device);
+ rc = dasd_state_known_to_new(device);
- return 0;
+ return rc;
}
/*
@@ -702,6 +723,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr)
cqr->retries--;
cqr->status = DASD_CQR_CLEAR;
cqr->stopclk = get_clock();
+ cqr->starttime = 0;
DBF_DEV_EVENT(DBF_DEBUG, device,
"terminate cqr %p successful",
cqr);
@@ -871,7 +893,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
device = (struct dasd_device *) cqr->device;
if (device == NULL ||
- device != dasd_device_from_cdev(cdev) ||
+ device != dasd_device_from_cdev_locked(cdev) ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
cdev->dev.bus_id);
@@ -948,7 +970,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
/* 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) {
- device = dasd_device_from_cdev(cdev);
+ device = dasd_device_from_cdev_locked(cdev);
if (!IS_ERR(device)) {
dasd_handle_state_change_pending(device);
dasd_put_device(device);
@@ -979,6 +1001,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
cqr->status = DASD_CQR_QUEUED;
dasd_clear_timer(device);
+ wake_up(&dasd_flush_wq);
dasd_schedule_bh(device);
return;
}
@@ -994,7 +1017,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr);
/* Find out the appropriate era_action. */
- if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC)
+ if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC)
era = dasd_era_fatal;
else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
irb->scsw.cstat == 0 &&
@@ -1004,7 +1027,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
era = dasd_era_fatal; /* don't recover this request */
else if (irb->esw.esw0.erw.cons)
era = device->discipline->examine_error(cqr, irb);
- else
+ else
era = dasd_era_recover;
DBF_DEV_EVENT(DBF_DEBUG, device, "era_code %d", era);
@@ -1242,6 +1265,10 @@ __dasd_check_expire(struct dasd_device * device)
cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);
if (cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) {
if (time_after_eq(jiffies, cqr->expires + cqr->starttime)) {
+ DEV_MESSAGE(KERN_ERR, device,
+ "internal error - timeout (%is) expired "
+ "for cqr %p (%i retries left)",
+ (cqr->expires/HZ), cqr, cqr->retries);
if (device->discipline->term_IO(cqr) != 0)
/* Hmpf, try again in 1/10 sec */
dasd_set_timer(device, 10);
@@ -1286,46 +1313,100 @@ __dasd_start_head(struct dasd_device * device)
dasd_set_timer(device, 50);
}
+static inline int
+_wait_for_clear(struct dasd_ccw_req *cqr)
+{
+ return (cqr->status == DASD_CQR_QUEUED);
+}
+
/*
- * Remove requests from the ccw queue.
+ * Remove all requests from the ccw queue (all = '1') or only block device
+ * requests in case all = '0'.
+ * Take care of the erp-chain (chained via cqr->refers) and remove either
+ * the whole erp-chain or none of the erp-requests.
+ * If a request is currently running, term_IO is called and the request
+ * is re-queued. Prior to removing the terminated request we need to wait
+ * for the clear-interrupt.
+ * In case termination is not possible we stop processing and just finishing
+ * the already moved requests.
*/
-static void
+static int
dasd_flush_ccw_queue(struct dasd_device * device, int all)
{
+ struct dasd_ccw_req *cqr, *orig, *n;
+ int rc, i;
+
struct list_head flush_queue;
- struct list_head *l, *n;
- struct dasd_ccw_req *cqr;
INIT_LIST_HEAD(&flush_queue);
spin_lock_irq(get_ccwdev_lock(device->cdev));
- list_for_each_safe(l, n, &device->ccw_queue) {
- cqr = list_entry(l, struct dasd_ccw_req, list);
+ rc = 0;
+restart:
+ list_for_each_entry_safe(cqr, n, &device->ccw_queue, list) {
+ /* get original request of erp request-chain */
+ for (orig = cqr; orig->refers != NULL; orig = orig->refers);
+
/* Flush all request or only block device requests? */
- if (all == 0 && cqr->callback == dasd_end_request_cb)
+ if (all == 0 && cqr->callback != dasd_end_request_cb &&
+ orig->callback != dasd_end_request_cb) {
continue;
- if (cqr->status == DASD_CQR_IN_IO)
- device->discipline->term_IO(cqr);
- if (cqr->status != DASD_CQR_DONE ||
- cqr->status != DASD_CQR_FAILED) {
- cqr->status = DASD_CQR_FAILED;
+ }
+ /* Check status and move request to flush_queue */
+ switch (cqr->status) {
+ case DASD_CQR_IN_IO:
+ rc = device->discipline->term_IO(cqr);
+ if (rc) {
+ /* unable to terminate requeust */
+ DEV_MESSAGE(KERN_ERR, device,
+ "dasd flush ccw_queue is unable "
+ " to terminate request %p",
+ cqr);
+ /* stop flush processing */
+ goto finished;
+ }
+ break;
+ case DASD_CQR_QUEUED:
+ case DASD_CQR_ERROR:
+ /* set request to FAILED */
cqr->stopclk = get_clock();
+ cqr->status = DASD_CQR_FAILED;
+ break;
+ default: /* do not touch the others */
+ break;
+ }
+ /* Rechain request (including erp chain) */
+ for (i = 0; cqr != NULL; cqr = cqr->refers, i++) {
+ cqr->endclk = get_clock();
+ list_move_tail(&cqr->list, &flush_queue);
+ }
+ if (i > 1)
+ /* moved more than one request - need to restart */
+ goto restart;
+ }
+
+finished:
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
+ /* Now call the callback function of flushed requests */
+restart_cb:
+ list_for_each_entry_safe(cqr, n, &flush_queue, list) {
+ if (cqr->status == DASD_CQR_CLEAR) {
+ /* wait for clear interrupt! */
+ wait_event(dasd_flush_wq, _wait_for_clear(cqr));
+ cqr->status = DASD_CQR_FAILED;
}
/* Process finished ERP request. */
if (cqr->refers) {
__dasd_process_erp(device, cqr);
- continue;
+ /* restart list_for_xx loop since dasd_process_erp
+ * might remove multiple elements */
+ goto restart_cb;
}
- /* Rechain request on device request queue */
+ /* call the callback function */
cqr->endclk = get_clock();
- list_move_tail(&cqr->list, &flush_queue);
- }
- spin_unlock_irq(get_ccwdev_lock(device->cdev));
- /* Now call the callback function of flushed requests */
- list_for_each_safe(l, n, &flush_queue) {
- cqr = list_entry(l, struct dasd_ccw_req, list);
if (cqr->callback != NULL)
(cqr->callback)(cqr, cqr->callback_data);
}
+ return rc;
}
/*
@@ -1450,23 +1531,23 @@ dasd_sleep_on(struct dasd_ccw_req * cqr)
wait_queue_head_t wait_q;
struct dasd_device *device;
int rc;
-
+
device = cqr->device;
spin_lock_irq(get_ccwdev_lock(device->cdev));
-
+
init_waitqueue_head (&wait_q);
cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &wait_q;
cqr->status = DASD_CQR_QUEUED;
list_add_tail(&cqr->list, &device->ccw_queue);
-
+
/* let the bh start the request to keep them in order */
dasd_schedule_bh(device);
-
+
spin_unlock_irq(get_ccwdev_lock(device->cdev));
wait_event(wait_q, _wait_for_wakeup(cqr));
-
+
/* Request status is either done or failed. */
rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
return rc;
@@ -1511,10 +1592,8 @@ dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr)
if (device->discipline->term_IO) {
cqr->retries = -1;
device->discipline->term_IO(cqr);
- /*nished =
- * wait (non-interruptible) for final status
- * because signal ist still pending
- */
+ /* wait (non-interruptible) for final status
+ * because signal ist still pending */
spin_unlock_irq(get_ccwdev_lock(device->cdev));
wait_event(wait_q, _wait_for_wakeup(cqr));
spin_lock_irq(get_ccwdev_lock(device->cdev));
@@ -1547,19 +1626,11 @@ static inline int
_dasd_term_running_cqr(struct dasd_device *device)
{
struct dasd_ccw_req *cqr;
- int rc;
if (list_empty(&device->ccw_queue))
return 0;
cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);
- rc = device->discipline->term_IO(cqr);
- if (rc == 0) {
- /* termination successful */
- cqr->status = DASD_CQR_QUEUED;
- cqr->startclk = cqr->stopclk = 0;
- cqr->starttime = 0;
- }
- return rc;
+ return device->discipline->term_IO(cqr);
}
int
@@ -1568,7 +1639,7 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
wait_queue_head_t wait_q;
struct dasd_device *device;
int rc;
-
+
device = cqr->device;
spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = _dasd_term_running_cqr(device);
@@ -1576,20 +1647,20 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
spin_unlock_irq(get_ccwdev_lock(device->cdev));
return rc;
}
-
+
init_waitqueue_head (&wait_q);
cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &wait_q;
cqr->status = DASD_CQR_QUEUED;
list_add(&cqr->list, &device->ccw_queue);
-
+
/* let the bh start the request to keep them in order */
dasd_schedule_bh(device);
-
+
spin_unlock_irq(get_ccwdev_lock(device->cdev));
wait_event(wait_q, _wait_for_wakeup(cqr));
-
+
/* Request status is either done or failed. */
rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
return rc;
@@ -1725,14 +1796,11 @@ dasd_flush_request_queue(struct dasd_device * device)
if (!device->request_queue)
return;
-
+
spin_lock_irq(&device->request_queue_lock);
- while (!list_empty(&device->request_queue->queue_head)) {
- req = elv_next_request(device->request_queue);
- if (req == NULL)
- break;
- dasd_end_request(req, 0);
+ while ((req = elv_next_request(device->request_queue))) {
blkdev_dequeue_request(req);
+ dasd_end_request(req, 0);
}
spin_unlock_irq(&device->request_queue_lock);
}
@@ -1834,7 +1902,6 @@ dasd_exit(void)
}
dasd_gendisk_exit();
dasd_devmap_exit();
- devfs_remove("dasd");
if (dasd_debug_area != NULL) {
debug_unregister(dasd_debug_area);
dasd_debug_area = NULL;
@@ -1855,15 +1922,34 @@ dasd_generic_probe (struct ccw_device *cdev,
{
int ret;
+ ret = ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
+ if (ret) {
+ printk(KERN_WARNING
+ "dasd_generic_probe: could not set ccw-device options "
+ "for %s\n", cdev->dev.bus_id);
+ return ret;
+ }
ret = dasd_add_sysfs_files(cdev);
if (ret) {
printk(KERN_WARNING
"dasd_generic_probe: could not add sysfs entries "
"for %s\n", cdev->dev.bus_id);
- } else {
- cdev->handler = &dasd_int_handler;
+ return ret;
}
+ cdev->handler = &dasd_int_handler;
+ /*
+ * Automatically online either all dasd devices (dasd_autodetect)
+ * or all devices specified with dasd= parameters during
+ * initial probe.
+ */
+ if ((dasd_get_feature(cdev, DASD_FEATURE_INITIAL_ONLINE) > 0 ) ||
+ (dasd_autodetect && dasd_busid_known(cdev->dev.bus_id) != 0))
+ ret = ccw_device_set_online(cdev);
+ if (ret)
+ printk(KERN_WARNING
+ "dasd_generic_probe: could not initially online "
+ "ccw-device %s\n", cdev->dev.bus_id);
return ret;
}
@@ -1911,6 +1997,8 @@ dasd_generic_set_online (struct ccw_device *cdev,
struct dasd_device *device;
int rc;
+ /* first online clears initial online feature flag */
+ dasd_set_feature(cdev, DASD_FEATURE_INITIAL_ONLINE, 0);
device = dasd_create_device(cdev);
if (IS_ERR(device))
return PTR_ERR(device);
@@ -2065,31 +2153,6 @@ dasd_generic_notify(struct ccw_device *cdev, int event)
return ret;
}
-/*
- * Automatically online either all dasd devices (dasd_autodetect) or
- * all devices specified with dasd= parameters.
- */
-static int
-__dasd_auto_online(struct device *dev, void *data)
-{
- struct ccw_device *cdev;
-
- cdev = to_ccwdev(dev);
- if (dasd_autodetect || dasd_busid_known(cdev->dev.bus_id) == 0)
- ccw_device_set_online(cdev);
- return 0;
-}
-
-void
-dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver)
-{
- struct device_driver *drv;
-
- drv = get_driver(&dasd_discipline_driver->driver);
- driver_for_each_device(drv, NULL, NULL, __dasd_auto_online);
- put_driver(drv);
-}
-
static int __init
dasd_init(void)
@@ -2097,6 +2160,7 @@ dasd_init(void)
int rc;
init_waitqueue_head(&dasd_init_waitq);
+ init_waitqueue_head(&dasd_flush_wq);
/* register 'common' DASD debug area, used for all DBF_XXX calls */
dasd_debug_area = debug_register("dasd", 1, 2, 8 * sizeof (long));
@@ -2105,15 +2169,12 @@ dasd_init(void)
goto failed;
}
debug_register_view(dasd_debug_area, &debug_sprintf_view);
- debug_set_level(dasd_debug_area, DBF_EMERG);
+ debug_set_level(dasd_debug_area, DBF_WARNING);
DBF_EVENT(DBF_EMERG, "%s", "debug area created");
dasd_diag_discipline_pointer = NULL;
- rc = devfs_mk_dir("dasd");
- if (rc)
- goto failed;
rc = dasd_devmap_init();
if (rc)
goto failed;
@@ -2170,23 +2231,4 @@ EXPORT_SYMBOL_GPL(dasd_generic_remove);
EXPORT_SYMBOL_GPL(dasd_generic_notify);
EXPORT_SYMBOL_GPL(dasd_generic_set_online);
EXPORT_SYMBOL_GPL(dasd_generic_set_offline);
-EXPORT_SYMBOL_GPL(dasd_generic_auto_online);
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_3370_erp.c b/drivers/s390/block/dasd_3370_erp.c
index 1d11c2a9525d..1ddab8991d92 100644
--- a/drivers/s390/block/dasd_3370_erp.c
+++ b/drivers/s390/block/dasd_3370_erp.c
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_3370_erp.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
@@ -12,10 +12,10 @@
/*
- * DASD_3370_ERP_EXAMINE
+ * DASD_3370_ERP_EXAMINE
*
* DESCRIPTION
- * Checks only for fatal/no/recover error.
+ * Checks only for fatal/no/recover error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
@@ -23,7 +23,7 @@
* 'Chapter 7. 3370 Sense Data'.
*
* RETURN VALUES
- * dasd_era_none no error
+ * dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for all others.
*/
@@ -82,22 +82,3 @@ dasd_3370_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
return dasd_era_recover;
} /* END dasd_3370_erp_examine */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index 2ed51562319e..669805d4402d 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -1,6 +1,6 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_3990_erp.c
- * Author(s)......: Horst Hummel <Horst.Hummel@de.ibm.com>
+ * Author(s)......: Horst Hummel <Horst.Hummel@de.ibm.com>
* Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
@@ -25,23 +25,23 @@ struct DCTL_data {
} __attribute__ ((packed));
/*
- *****************************************************************************
+ *****************************************************************************
* SECTION ERP EXAMINATION
- *****************************************************************************
+ *****************************************************************************
*/
/*
- * DASD_3990_ERP_EXAMINE_24
+ * DASD_3990_ERP_EXAMINE_24
*
* DESCRIPTION
- * Checks only for fatal (unrecoverable) error.
+ * Checks only for fatal (unrecoverable) error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
* Each bit configuration leading to an action code 2 (Exit with
* programming error or unusual condition indication)
* are handled as fatal error´s.
- *
+ *
* All other configurations are handled as recoverable errors.
*
* RETURN VALUES
@@ -93,15 +93,15 @@ dasd_3990_erp_examine_24(struct dasd_ccw_req * cqr, char *sense)
} /* END dasd_3990_erp_examine_24 */
/*
- * DASD_3990_ERP_EXAMINE_32
+ * DASD_3990_ERP_EXAMINE_32
*
* DESCRIPTION
- * Checks only for fatal/no/recoverable error.
+ * Checks only for fatal/no/recoverable error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
* RETURN VALUES
- * dasd_era_none no error
+ * dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for recoverable others.
*/
@@ -128,10 +128,10 @@ dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense)
} /* end dasd_3990_erp_examine_32 */
/*
- * DASD_3990_ERP_EXAMINE
+ * DASD_3990_ERP_EXAMINE
*
* DESCRIPTION
- * Checks only for fatal/no/recover error.
+ * Checks only for fatal/no/recover error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
@@ -139,7 +139,7 @@ dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense)
* 'Chapter 7. Error Recovery Procedures'.
*
* RETURN VALUES
- * dasd_era_none no error
+ * dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for all others.
*/
@@ -178,18 +178,18 @@ dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
} /* END dasd_3990_erp_examine */
/*
- *****************************************************************************
+ *****************************************************************************
* SECTION ERP HANDLING
- *****************************************************************************
+ *****************************************************************************
*/
/*
- *****************************************************************************
+ *****************************************************************************
* 24 and 32 byte sense ERP functions
- *****************************************************************************
+ *****************************************************************************
*/
/*
- * DASD_3990_ERP_CLEANUP
+ * DASD_3990_ERP_CLEANUP
*
* DESCRIPTION
* Removes the already build but not necessary ERP request and sets
@@ -197,10 +197,10 @@ dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
*
* PARAMETER
* erp request to be blocked
- * final_status either DASD_CQR_DONE or DASD_CQR_FAILED
+ * final_status either DASD_CQR_DONE or DASD_CQR_FAILED
*
* RETURN VALUES
- * cqr original cqr
+ * cqr original cqr
*/
static struct dasd_ccw_req *
dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status)
@@ -214,7 +214,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status)
} /* end dasd_3990_erp_cleanup */
/*
- * DASD_3990_ERP_BLOCK_QUEUE
+ * DASD_3990_ERP_BLOCK_QUEUE
*
* DESCRIPTION
* Block the given device request queue to prevent from further
@@ -237,7 +237,7 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
}
/*
- * DASD_3990_ERP_INT_REQ
+ * DASD_3990_ERP_INT_REQ
*
* DESCRIPTION
* Handles 'Intervention Required' error.
@@ -277,7 +277,7 @@ dasd_3990_erp_int_req(struct dasd_ccw_req * erp)
} /* end dasd_3990_erp_int_req */
/*
- * DASD_3990_ERP_ALTERNATE_PATH
+ * DASD_3990_ERP_ALTERNATE_PATH
*
* DESCRIPTION
* Repeat the operation on a different channel path.
@@ -330,15 +330,15 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
* DASD_3990_ERP_DCTL
*
* DESCRIPTION
- * Setup cqr to do the Diagnostic Control (DCTL) command with an
+ * Setup cqr to do the Diagnostic Control (DCTL) command with an
* Inhibit Write subcommand (0x20) and the given modifier.
*
* PARAMETER
* erp pointer to the current (failed) ERP
* modifier subcommand modifier
- *
+ *
* RETURN VALUES
- * dctl_cqr pointer to NEW dctl_cqr
+ * dctl_cqr pointer to NEW dctl_cqr
*
*/
static struct dasd_ccw_req *
@@ -386,7 +386,7 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
} /* end dasd_3990_erp_DCTL */
/*
- * DASD_3990_ERP_ACTION_1
+ * DASD_3990_ERP_ACTION_1
*
* DESCRIPTION
* Setup ERP to do the ERP action 1 (see Reference manual).
@@ -415,7 +415,7 @@ dasd_3990_erp_action_1(struct dasd_ccw_req * erp)
} /* end dasd_3990_erp_action_1 */
/*
- * DASD_3990_ERP_ACTION_4
+ * DASD_3990_ERP_ACTION_4
*
* DESCRIPTION
* Setup ERP to do the ERP action 4 (see Reference manual).
@@ -453,11 +453,11 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
if (sense[25] == 0x1D) { /* state change pending */
- DEV_MESSAGE(KERN_INFO, device,
+ DEV_MESSAGE(KERN_INFO, device,
"waiting for state change pending "
"interrupt, %d retries left",
erp->retries);
-
+
dasd_3990_erp_block_queue(erp, 30*HZ);
} else if (sense[25] == 0x1E) { /* busy */
@@ -469,9 +469,9 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
} else {
/* no state change pending - retry */
- DEV_MESSAGE (KERN_INFO, device,
+ DEV_MESSAGE (KERN_INFO, device,
"redriving request immediately, "
- "%d retries left",
+ "%d retries left",
erp->retries);
erp->status = DASD_CQR_QUEUED;
}
@@ -482,13 +482,13 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_action_4 */
/*
- *****************************************************************************
+ *****************************************************************************
* 24 byte sense ERP functions (only)
- *****************************************************************************
+ *****************************************************************************
*/
/*
- * DASD_3990_ERP_ACTION_5
+ * DASD_3990_ERP_ACTION_5
*
* DESCRIPTION
* Setup ERP to do the ERP action 5 (see Reference manual).
@@ -523,7 +523,7 @@ dasd_3990_erp_action_5(struct dasd_ccw_req * erp)
*
* PARAMETER
* sense current sense data
- *
+ *
* RETURN VALUES
* void
*/
@@ -1150,9 +1150,9 @@ dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense)
* PARAMETER
* erp current erp_head
* sense current sense data
- *
+ *
* RETURN VALUES
- * erp 'new' erp_head - pointer to new ERP
+ * erp 'new' erp_head - pointer to new ERP
*/
static struct dasd_ccw_req *
dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense)
@@ -1185,7 +1185,7 @@ dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_com_rej */
/*
- * DASD_3990_ERP_BUS_OUT
+ * DASD_3990_ERP_BUS_OUT
*
* DESCRIPTION
* Handles 24 byte 'Bus Out Parity Check' error.
@@ -1483,7 +1483,7 @@ dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense)
*
* PARAMETER
* erp already added default ERP
- *
+ *
* RETURN VALUES
* erp new erp_head - pointer to new ERP
*/
@@ -1527,11 +1527,11 @@ dasd_3990_erp_file_prot(struct dasd_ccw_req * erp)
} /* end dasd_3990_erp_file_prot */
/*
- * DASD_3990_ERP_INSPECT_24
+ * DASD_3990_ERP_INSPECT_24
*
* DESCRIPTION
* Does a detailed inspection of the 24 byte sense data
- * and sets up a related error recovery action.
+ * and sets up a related error recovery action.
*
* PARAMETER
* sense sense data of the actual error
@@ -1602,13 +1602,13 @@ dasd_3990_erp_inspect_24(struct dasd_ccw_req * erp, char *sense)
} /* END dasd_3990_erp_inspect_24 */
/*
- *****************************************************************************
+ *****************************************************************************
* 32 byte sense ERP functions (only)
- *****************************************************************************
+ *****************************************************************************
*/
/*
- * DASD_3990_ERPACTION_10_32
+ * DASD_3990_ERPACTION_10_32
*
* DESCRIPTION
* Handles 32 byte 'Action 10' of Single Program Action Codes.
@@ -1616,7 +1616,7 @@ dasd_3990_erp_inspect_24(struct dasd_ccw_req * erp, char *sense)
*
* PARAMETER
* erp current erp_head
- * sense current sense data
+ * sense current sense data
* RETURN VALUES
* erp modified erp_head
*/
@@ -1640,18 +1640,18 @@ dasd_3990_erp_action_10_32(struct dasd_ccw_req * erp, char *sense)
*
* DESCRIPTION
* Handles 32 byte 'Action 1B' of Single Program Action Codes.
- * A write operation could not be finished because of an unexpected
+ * A write operation could not be finished because of an unexpected
* condition.
- * The already created 'default erp' is used to get the link to
- * the erp chain, but it can not be used for this recovery
+ * The already created 'default erp' is used to get the link to
+ * the erp chain, but it can not be used for this recovery
* action because it contains no DE/LO data space.
*
* PARAMETER
* default_erp already added default erp.
- * sense current sense data
+ * sense current sense data
*
* RETURN VALUES
- * erp new erp or
+ * erp new erp or
* default_erp in case of imprecise ending or error
*/
static struct dasd_ccw_req *
@@ -1789,16 +1789,16 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
* DASD_3990_UPDATE_1B
*
* DESCRIPTION
- * Handles the update to the 32 byte 'Action 1B' of Single Program
+ * Handles the update to the 32 byte 'Action 1B' of Single Program
* Action Codes in case the first action was not successful.
* The already created 'previous_erp' is the currently not successful
- * ERP.
+ * ERP.
*
* PARAMETER
* previous_erp already created previous erp.
- * sense current sense data
+ * sense current sense data
* RETURN VALUES
- * erp modified erp
+ * erp modified erp
*/
static struct dasd_ccw_req *
dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
@@ -1897,7 +1897,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
} /* end dasd_3990_update_1B */
/*
- * DASD_3990_ERP_COMPOUND_RETRY
+ * DASD_3990_ERP_COMPOUND_RETRY
*
* DESCRIPTION
* Handles the compound ERP action retry code.
@@ -1943,7 +1943,7 @@ dasd_3990_erp_compound_retry(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_compound_retry */
/*
- * DASD_3990_ERP_COMPOUND_PATH
+ * DASD_3990_ERP_COMPOUND_PATH
*
* DESCRIPTION
* Handles the compound ERP action for retry on alternate
@@ -1965,7 +1965,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense)
dasd_3990_erp_alternate_path(erp);
if (erp->status == DASD_CQR_FAILED) {
- /* reset the lpm and the status to be able to
+ /* reset the lpm and the status to be able to
* try further actions. */
erp->lpm = 0;
@@ -1980,7 +1980,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_compound_path */
/*
- * DASD_3990_ERP_COMPOUND_CODE
+ * DASD_3990_ERP_COMPOUND_CODE
*
* DESCRIPTION
* Handles the compound ERP action for retry code.
@@ -2001,18 +2001,18 @@ dasd_3990_erp_compound_code(struct dasd_ccw_req * erp, char *sense)
switch (sense[28]) {
case 0x17:
- /* issue a Diagnostic Control command with an
+ /* issue a Diagnostic Control command with an
* Inhibit Write subcommand and controler modifier */
erp = dasd_3990_erp_DCTL(erp, 0x20);
break;
-
+
case 0x25:
/* wait for 5 seconds and retry again */
erp->retries = 1;
-
+
dasd_3990_erp_block_queue (erp, 5*HZ);
break;
-
+
default:
/* should not happen - continue */
break;
@@ -2026,7 +2026,7 @@ dasd_3990_erp_compound_code(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_compound_code */
/*
- * DASD_3990_ERP_COMPOUND_CONFIG
+ * DASD_3990_ERP_COMPOUND_CONFIG
*
* DESCRIPTION
* Handles the compound ERP action for configruation
@@ -2063,10 +2063,10 @@ dasd_3990_erp_compound_config(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_compound_config */
/*
- * DASD_3990_ERP_COMPOUND
+ * DASD_3990_ERP_COMPOUND
*
* DESCRIPTION
- * Does the further compound program action if
+ * Does the further compound program action if
* compound retry was not successful.
*
* PARAMETER
@@ -2110,11 +2110,11 @@ dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_compound */
/*
- * DASD_3990_ERP_INSPECT_32
+ * DASD_3990_ERP_INSPECT_32
*
* DESCRIPTION
* Does a detailed inspection of the 32 byte sense data
- * and sets up a related error recovery action.
+ * and sets up a related error recovery action.
*
* PARAMETER
* sense sense data of the actual error
@@ -2228,9 +2228,9 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_inspect_32 */
/*
- *****************************************************************************
+ *****************************************************************************
* main ERP control fuctions (24 and 32 byte sense)
- *****************************************************************************
+ *****************************************************************************
*/
/*
@@ -2243,7 +2243,7 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
* PARAMETER
* erp pointer to the currently created default ERP
* RETURN VALUES
- * erp_new contens was possibly modified
+ * erp_new contens was possibly modified
*/
static struct dasd_ccw_req *
dasd_3990_erp_inspect(struct dasd_ccw_req * erp)
@@ -2272,14 +2272,14 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp)
/*
* DASD_3990_ERP_ADD_ERP
- *
+ *
* DESCRIPTION
* This funtion adds an additional request block (ERP) to the head of
* the given cqr (or erp).
* This erp is initialized as an default erp (retry TIC)
*
* PARAMETER
- * cqr head of the current ERP-chain (or single cqr if
+ * cqr head of the current ERP-chain (or single cqr if
* first error)
* RETURN VALUES
* erp pointer to new ERP-chain head
@@ -2332,15 +2332,15 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
}
/*
- * DASD_3990_ERP_ADDITIONAL_ERP
- *
+ * DASD_3990_ERP_ADDITIONAL_ERP
+ *
* DESCRIPTION
* An additional ERP is needed to handle the current error.
* Add ERP to the head of the ERP-chain containing the ERP processing
* determined based on the sense data.
*
* PARAMETER
- * cqr head of the current ERP-chain (or single cqr if
+ * cqr head of the current ERP-chain (or single cqr if
* first error)
*
* RETURN VALUES
@@ -2376,7 +2376,7 @@ dasd_3990_erp_additional_erp(struct dasd_ccw_req * cqr)
* 24 byte sense byte 25 and 27 is set as well.
*
* PARAMETER
- * cqr1 first cqr, which will be compared with the
+ * cqr1 first cqr, which will be compared with the
* cqr2 second cqr.
*
* RETURN VALUES
@@ -2415,7 +2415,7 @@ dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1, struct dasd_ccw_req *cqr2)
* cqr failed cqr (either original cqr or already an erp)
*
* RETURN VALUES
- * erp erp-pointer to the already defined error
+ * erp erp-pointer to the already defined error
* recovery procedure OR
* NULL if a 'new' error occurred.
*/
@@ -2451,10 +2451,10 @@ dasd_3990_erp_in_erp(struct dasd_ccw_req *cqr)
* DASD_3990_ERP_FURTHER_ERP (24 & 32 byte sense)
*
* DESCRIPTION
- * No retry is left for the current ERP. Check what has to be done
+ * No retry is left for the current ERP. Check what has to be done
* with the ERP.
* - do further defined ERP action or
- * - wait for interrupt or
+ * - wait for interrupt or
* - exit with permanent error
*
* PARAMETER
@@ -2485,7 +2485,7 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
if (!(sense[2] & DASD_SENSE_BIT_0)) {
- /* issue a Diagnostic Control command with an
+ /* issue a Diagnostic Control command with an
* Inhibit Write subcommand */
switch (sense[25]) {
@@ -2535,14 +2535,14 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
} /* end dasd_3990_erp_further_erp */
/*
- * DASD_3990_ERP_HANDLE_MATCH_ERP
+ * DASD_3990_ERP_HANDLE_MATCH_ERP
*
* DESCRIPTION
* An error occurred again and an ERP has been detected which is already
- * used to handle this error (e.g. retries).
+ * used to handle this error (e.g. retries).
* All prior ERP's are asumed to be successful and therefore removed
* from queue.
- * If retry counter of matching erp is already 0, it is checked if further
+ * If retry counter of matching erp is already 0, it is checked if further
* action is needed (besides retry) or if the ERP has failed.
*
* PARAMETER
@@ -2631,7 +2631,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
* erp erp-pointer to the head of the ERP action chain.
* This means:
* - either a ptr to an additional ERP cqr or
- * - the original given cqr (which's status might
+ * - the original given cqr (which's status might
* be modified)
*/
struct dasd_ccw_req *
@@ -2723,22 +2723,3 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
return erp;
} /* end dasd_3990_erp_action */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_9336_erp.c b/drivers/s390/block/dasd_9336_erp.c
index dc861446d056..6e082688475a 100644
--- a/drivers/s390/block/dasd_9336_erp.c
+++ b/drivers/s390/block/dasd_9336_erp.c
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_9336_erp.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
@@ -12,10 +12,10 @@
/*
- * DASD_9336_ERP_EXAMINE
+ * DASD_9336_ERP_EXAMINE
*
* DESCRIPTION
- * Checks only for fatal/no/recover error.
+ * Checks only for fatal/no/recover error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
@@ -23,7 +23,7 @@
* 'Chapter 7. 9336 Sense Data'.
*
* RETURN VALUES
- * dasd_era_none no error
+ * dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for all others.
*/
@@ -39,22 +39,3 @@ dasd_9336_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
return dasd_era_recover;
} /* END dasd_9336_erp_examine */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_9343_erp.c b/drivers/s390/block/dasd_9343_erp.c
index 4a5b79569aaa..ddecb9808ed4 100644
--- a/drivers/s390/block/dasd_9343_erp.c
+++ b/drivers/s390/block/dasd_9343_erp.c
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_9345_erp.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 216bc4fba199..91cf971f0652 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -13,7 +13,6 @@
*
*/
-#include <linux/config.h>
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -27,7 +26,7 @@
#include "dasd_int.h"
kmem_cache_t *dasd_page_cache;
-EXPORT_SYMBOL(dasd_page_cache);
+EXPORT_SYMBOL_GPL(dasd_page_cache);
/*
* dasd_devmap_t is used to store the features and the relation
@@ -49,6 +48,22 @@ struct dasd_devmap {
};
/*
+ * dasd_server_ssid_map contains a globally unique storage server subsystem ID.
+ * dasd_server_ssid_list contains the list of all subsystem IDs accessed by
+ * the DASD device driver.
+ */
+struct dasd_server_ssid_map {
+ struct list_head list;
+ struct system_id {
+ char vendor[4];
+ char serial[15];
+ __u16 ssid;
+ } sid;
+};
+
+static struct list_head dasd_server_ssid_list;
+
+/*
* Parameter parsing functions for dasd= parameter. The syntax is:
* <devno> : (0x)?[0-9a-fA-F]+
* <busid> : [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+
@@ -64,6 +79,8 @@ struct dasd_devmap {
int dasd_probeonly = 0; /* is true, when probeonly mode is active */
int dasd_autodetect = 0; /* is true, when autodetection is active */
+int dasd_nopav = 0; /* is true, when PAV is disabled */
+EXPORT_SYMBOL_GPL(dasd_nopav);
/*
* char *dasd[] is intended to hold the ranges supplied by the dasd= statement
@@ -74,7 +91,7 @@ static char *dasd[256];
module_param_array(dasd, charp, NULL, 0);
/*
- * Single spinlock to protect devmap structures and lists.
+ * Single spinlock to protect devmap and servermap structures and lists.
*/
static DEFINE_SPINLOCK(dasd_devmap_lock);
@@ -123,7 +140,7 @@ static inline int
dasd_busid(char **str, int *id0, int *id1, int *devno)
{
int val, old_style;
-
+
/* check for leading '0x' */
old_style = 0;
if ((*str)[0] == '0' && (*str)[1] == 'x') {
@@ -179,7 +196,7 @@ dasd_feature_list(char *str, char **endp)
features = 0;
while (1) {
- for (len = 0;
+ for (len = 0;
str[len] && str[len] != ':' && str[len] != ')'; len++);
if (len == 2 && !strncmp(str, "ro", 2))
features |= DASD_FEATURE_READONLY;
@@ -228,24 +245,34 @@ dasd_parse_keyword( char *parsestring ) {
length = strlen(parsestring);
residual_str = parsestring + length;
}
- if (strncmp ("autodetect", parsestring, length) == 0) {
+ if (strncmp("autodetect", parsestring, length) == 0) {
dasd_autodetect = 1;
MESSAGE (KERN_INFO, "%s",
"turning to autodetection mode");
return residual_str;
}
- if (strncmp ("probeonly", parsestring, length) == 0) {
+ if (strncmp("probeonly", parsestring, length) == 0) {
dasd_probeonly = 1;
MESSAGE(KERN_INFO, "%s",
"turning to probeonly mode");
return residual_str;
}
- if (strncmp ("fixedbuffers", parsestring, length) == 0) {
+ if (strncmp("nopav", parsestring, length) == 0) {
+ if (MACHINE_IS_VM)
+ MESSAGE(KERN_INFO, "%s", "'nopav' not supported on VM");
+ else {
+ dasd_nopav = 1;
+ MESSAGE(KERN_INFO, "%s", "disable PAV mode");
+ }
+ return residual_str;
+ }
+ if (strncmp("fixedbuffers", parsestring, length) == 0) {
if (dasd_page_cache)
return residual_str;
dasd_page_cache =
- kmem_cache_create("dasd_page_cache", PAGE_SIZE, 0,
- SLAB_CACHE_DMA, NULL, NULL );
+ kmem_cache_create("dasd_page_cache", PAGE_SIZE,
+ PAGE_SIZE, SLAB_CACHE_DMA,
+ NULL, NULL );
if (!dasd_page_cache)
MESSAGE(KERN_WARNING, "%s", "Failed to create slab, "
"fixed buffer mode disabled.");
@@ -294,6 +321,8 @@ dasd_parse_range( char *parsestring ) {
features = dasd_feature_list(str, &str);
if (features < 0)
return ERR_PTR(-EINVAL);
+ /* each device in dasd= parameter should be set initially online */
+ features |= DASD_FEATURE_INITIAL_ONLINE;
while (from <= to) {
sprintf(bus_id, "%01x.%01x.%04x",
from_id0, from_id1, from++);
@@ -359,7 +388,7 @@ dasd_parse(void)
* Add a devmap for the device specified by busid. It is possible that
* the devmap already exists (dasd= parameter). The order of the devices
* added through this function will define the kdevs for the individual
- * devices.
+ * devices.
*/
static struct dasd_devmap *
dasd_add_busid(char *bus_id, int features)
@@ -368,11 +397,11 @@ dasd_add_busid(char *bus_id, int features)
int hash;
new = (struct dasd_devmap *)
- kmalloc(sizeof(struct dasd_devmap), GFP_KERNEL);
+ kzalloc(sizeof(struct dasd_devmap), GFP_KERNEL);
if (!new)
return ERR_PTR(-ENOMEM);
spin_lock(&dasd_devmap_lock);
- devmap = 0;
+ devmap = NULL;
hash = dasd_hash_busid(bus_id);
list_for_each_entry(tmp, &dasd_hashlists[hash], list)
if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
@@ -384,10 +413,10 @@ dasd_add_busid(char *bus_id, int features)
new->devindex = dasd_max_devindex++;
strncpy(new->bus_id, bus_id, BUS_ID_SIZE);
new->features = features;
- new->device = 0;
+ new->device = NULL;
list_add(&new->list, &dasd_hashlists[hash]);
devmap = new;
- new = 0;
+ new = NULL;
}
spin_unlock(&dasd_devmap_lock);
kfree(new);
@@ -457,7 +486,7 @@ dasd_device_from_devindex(int devindex)
int i;
spin_lock(&dasd_devmap_lock);
- devmap = 0;
+ devmap = NULL;
for (i = 0; (i < 256) && !devmap; i++)
list_for_each_entry(tmp, &dasd_hashlists[i], list)
if (tmp->devindex == devindex) {
@@ -498,17 +527,17 @@ dasd_create_device(struct ccw_device *cdev)
{
struct dasd_devmap *devmap;
struct dasd_device *device;
+ unsigned long flags;
int rc;
devmap = dasd_devmap_from_cdev(cdev);
if (IS_ERR(devmap))
return (void *) devmap;
- cdev->dev.driver_data = devmap;
device = dasd_alloc_device();
if (IS_ERR(device))
return device;
- atomic_set(&device->ref_count, 2);
+ atomic_set(&device->ref_count, 3);
spin_lock(&dasd_devmap_lock);
if (!devmap->device) {
@@ -527,6 +556,11 @@ dasd_create_device(struct ccw_device *cdev)
dasd_free_device(device);
return ERR_PTR(rc);
}
+
+ spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+ cdev->dev.driver_data = device;
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+
return device;
}
@@ -544,6 +578,7 @@ dasd_delete_device(struct dasd_device *device)
{
struct ccw_device *cdev;
struct dasd_devmap *devmap;
+ unsigned long flags;
/* First remove device pointer from devmap. */
devmap = dasd_find_busid(device->cdev->dev.bus_id);
@@ -557,9 +592,16 @@ dasd_delete_device(struct dasd_device *device)
devmap->device = NULL;
spin_unlock(&dasd_devmap_lock);
- /* Drop ref_count by 2, one for the devmap reference and
- * one for the passed reference. */
- atomic_sub(2, &device->ref_count);
+ /* Disconnect dasd_device structure from ccw_device structure. */
+ spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+ device->cdev->dev.driver_data = NULL;
+ spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+
+ /*
+ * Drop ref_count by 3, one for the devmap reference, one for
+ * the cdev reference and one for the passed reference.
+ */
+ atomic_sub(3, &device->ref_count);
/* Wait for reference counter to drop to zero. */
wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
@@ -568,9 +610,6 @@ dasd_delete_device(struct dasd_device *device)
cdev = device->cdev;
device->cdev = NULL;
- /* Disconnect dasd_devmap structure from ccw_device structure. */
- cdev->dev.driver_data = NULL;
-
/* Put ccw_device structure. */
put_device(&cdev->dev);
@@ -590,21 +629,32 @@ dasd_put_device_wake(struct dasd_device *device)
/*
* Return dasd_device structure associated with cdev.
+ * This function needs to be called with the ccw device
+ * lock held. It can be used from interrupt context.
+ */
+struct dasd_device *
+dasd_device_from_cdev_locked(struct ccw_device *cdev)
+{
+ struct dasd_device *device = cdev->dev.driver_data;
+
+ if (!device)
+ return ERR_PTR(-ENODEV);
+ dasd_get_device(device);
+ return device;
+}
+
+/*
+ * Return dasd_device structure associated with cdev.
*/
struct dasd_device *
dasd_device_from_cdev(struct ccw_device *cdev)
{
- struct dasd_devmap *devmap;
struct dasd_device *device;
+ unsigned long flags;
- device = ERR_PTR(-ENODEV);
- spin_lock(&dasd_devmap_lock);
- devmap = cdev->dev.driver_data;
- if (devmap && devmap->device) {
- device = devmap->device;
- dasd_get_device(device);
- }
- spin_unlock(&dasd_devmap_lock);
+ spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+ device = dasd_device_from_cdev_locked(cdev);
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
return device;
}
@@ -630,7 +680,8 @@ dasd_ro_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static ssize_t
-dasd_ro_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+dasd_ro_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct dasd_devmap *devmap;
int ro_flag;
@@ -658,7 +709,7 @@ static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
* use_diag controls whether the driver should use diag rather than ssch
* to talk to the device
*/
-static ssize_t
+static ssize_t
dasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dasd_devmap *devmap;
@@ -673,7 +724,8 @@ dasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static ssize_t
-dasd_use_diag_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+dasd_use_diag_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct dasd_devmap *devmap;
ssize_t rc;
@@ -697,22 +749,23 @@ dasd_use_diag_store(struct device *dev, struct device_attribute *attr, const cha
return rc;
}
-static
-DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
+static DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
static ssize_t
-dasd_discipline_show(struct device *dev, struct device_attribute *attr, char *buf)
+dasd_discipline_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
- struct dasd_devmap *devmap;
- char *dname;
+ struct dasd_device *device;
+ ssize_t len;
- spin_lock(&dasd_devmap_lock);
- dname = "none";
- devmap = dev->driver_data;
- if (devmap && devmap->device && devmap->device->discipline)
- dname = devmap->device->discipline->name;
- spin_unlock(&dasd_devmap_lock);
- return snprintf(buf, PAGE_SIZE, "%s\n", dname);
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (!IS_ERR(device) && device->discipline) {
+ len = snprintf(buf, PAGE_SIZE, "%s\n",
+ device->discipline->name);
+ dasd_put_device(device);
+ } else
+ len = snprintf(buf, PAGE_SIZE, "none\n");
+ return len;
}
static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
@@ -834,7 +887,6 @@ static struct attribute_group dasd_attr_group = {
.attrs = dasd_attrs,
};
-
/*
* Return copy of the device unique identifier.
*/
@@ -854,21 +906,52 @@ dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid)
/*
* Register the given device unique identifier into devmap struct.
+ * In addition check if the related storage server subsystem ID is already
+ * contained in the dasd_server_ssid_list. If subsystem ID is not contained,
+ * create new entry.
+ * Return 0 if server was already in serverlist,
+ * 1 if the server was added successful
+ * <0 in case of error.
*/
int
dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid)
{
struct dasd_devmap *devmap;
+ struct dasd_server_ssid_map *srv, *tmp;
devmap = dasd_find_busid(cdev->dev.bus_id);
if (IS_ERR(devmap))
return PTR_ERR(devmap);
+
+ /* generate entry for server_ssid_map */
+ srv = (struct dasd_server_ssid_map *)
+ kzalloc(sizeof(struct dasd_server_ssid_map), GFP_KERNEL);
+ if (!srv)
+ return -ENOMEM;
+ strncpy(srv->sid.vendor, uid->vendor, sizeof(srv->sid.vendor) - 1);
+ strncpy(srv->sid.serial, uid->serial, sizeof(srv->sid.serial) - 1);
+ srv->sid.ssid = uid->ssid;
+
+ /* server is already contained ? */
spin_lock(&dasd_devmap_lock);
devmap->uid = *uid;
+ list_for_each_entry(tmp, &dasd_server_ssid_list, list) {
+ if (!memcmp(&srv->sid, &tmp->sid,
+ sizeof(struct system_id))) {
+ kfree(srv);
+ srv = NULL;
+ break;
+ }
+ }
+
+ /* add servermap to serverlist */
+ if (srv)
+ list_add(&srv->list, &dasd_server_ssid_list);
spin_unlock(&dasd_devmap_lock);
- return 0;
+
+ return (srv ? 1 : 0);
}
-EXPORT_SYMBOL(dasd_set_uid);
+EXPORT_SYMBOL_GPL(dasd_set_uid);
/*
* Return value of the specified feature.
@@ -880,7 +963,7 @@ dasd_get_feature(struct ccw_device *cdev, int feature)
devmap = dasd_find_busid(cdev->dev.bus_id);
if (IS_ERR(devmap))
- return (int) PTR_ERR(devmap);
+ return PTR_ERR(devmap);
return ((devmap->features & feature) != 0);
}
@@ -896,7 +979,7 @@ dasd_set_feature(struct ccw_device *cdev, int feature, int flag)
devmap = dasd_find_busid(cdev->dev.bus_id);
if (IS_ERR(devmap))
- return (int) PTR_ERR(devmap);
+ return PTR_ERR(devmap);
spin_lock(&dasd_devmap_lock);
if (flag)
@@ -932,8 +1015,10 @@ dasd_devmap_init(void)
dasd_max_devindex = 0;
for (i = 0; i < 256; i++)
INIT_LIST_HEAD(&dasd_hashlists[i]);
- return 0;
+ /* Initialize servermap structure. */
+ INIT_LIST_HEAD(&dasd_server_ssid_list);
+ return 0;
}
void
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 3f9d704d2657..9d051e5687ea 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_diag.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Based on.......: linux/drivers/s390/block/mdisk.c
@@ -8,7 +8,6 @@
*
*/
-#include <linux/config.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -64,44 +63,26 @@ static const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 */
* and function code cmd.
* In case of an exception return 3. Otherwise return result of bitwise OR of
* resulting condition code and DIAG return code. */
-static __inline__ int
-dia250(void *iob, int cmd)
+static inline int dia250(void *iob, int cmd)
{
+ register unsigned long reg0 asm ("0") = (unsigned long) iob;
typedef union {
struct dasd_diag_init_io init_io;
struct dasd_diag_rw_io rw_io;
} addr_type;
int rc;
- __asm__ __volatile__(
-#ifdef CONFIG_64BIT
- " lghi %0,3\n"
- " lgr 0,%3\n"
+ rc = 3;
+ asm volatile(
" diag 0,%2,0x250\n"
"0: ipm %0\n"
" srl %0,28\n"
" or %0,1\n"
"1:\n"
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous\n"
-#else
- " lhi %0,3\n"
- " lr 0,%3\n"
- " diag 0,%2,0x250\n"
- "0: ipm %0\n"
- " srl %0,28\n"
- " or %0,1\n"
- "1:\n"
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous\n"
-#endif
- : "=&d" (rc), "=m" (*(addr_type *) iob)
- : "d" (cmd), "d" (iob), "m" (*(addr_type *) iob)
- : "0", "1", "cc");
+ EX_TABLE(0b,1b)
+ : "+d" (rc), "=m" (*(addr_type *) iob)
+ : "d" (cmd), "d" (reg0), "m" (*(addr_type *) iob)
+ : "1", "cc");
return rc;
}
@@ -336,7 +317,7 @@ dasd_diag_check_device(struct dasd_device *device)
private = (struct dasd_diag_private *) device->private;
if (private == NULL) {
- private = kmalloc(sizeof(struct dasd_diag_private),GFP_KERNEL);
+ private = kzalloc(sizeof(struct dasd_diag_private),GFP_KERNEL);
if (private == NULL) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
"memory allocation failed for private data");
@@ -527,7 +508,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
datasize, device);
if (IS_ERR(cqr))
return cqr;
-
+
dreq = (struct dasd_diag_req *) cqr->data;
dreq->block_count = count;
dbio = dreq->bio;
diff --git a/drivers/s390/block/dasd_diag.h b/drivers/s390/block/dasd_diag.h
index 38a4e55f8953..b8c78267ff3e 100644
--- a/drivers/s390/block/dasd_diag.h
+++ b/drivers/s390/block/dasd_diag.h
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_diag.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Based on.......: linux/drivers/s390/block/mdisk.h
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 7d5a6cee4bd8..b7a7fac3f7c3 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1,7 +1,7 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_eckd.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Horst Hummel <Horst.Hummel@de.ibm.com>
+ * Horst Hummel <Horst.Hummel@de.ibm.com>
* Carsten Otte <Cotte@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
@@ -9,7 +9,6 @@
*
*/
-#include <linux/config.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -24,6 +23,7 @@
#include <asm/io.h>
#include <asm/todclk.h>
#include <asm/uaccess.h>
+#include <asm/cio.h>
#include <asm/ccwdev.h>
#include "dasd_int.h"
@@ -65,16 +65,16 @@ struct dasd_eckd_private {
/* The ccw bus type uses this table to find devices that it sends to
* dasd_eckd_probe */
static struct ccw_device_id dasd_eckd_ids[] = {
- { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), driver_info: 0x1},
- { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), driver_info: 0x2},
- { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3390, 0), driver_info: 0x3},
- { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), driver_info: 0x4},
- { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), driver_info: 0x5},
- { CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), driver_info: 0x6},
- { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), driver_info: 0x7},
- { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), driver_info: 0x8},
- { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), driver_info: 0x9},
- { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), driver_info: 0xa},
+ { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), .driver_info = 0x1},
+ { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), .driver_info = 0x2},
+ { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3390, 0), .driver_info = 0x3},
+ { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), .driver_info = 0x4},
+ { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), .driver_info = 0x5},
+ { CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), .driver_info = 0x6},
+ { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), .driver_info = 0x7},
+ { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), .driver_info = 0x8},
+ { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), .driver_info = 0x9},
+ { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), .driver_info = 0xa},
{ /* end of list */ },
};
@@ -89,17 +89,22 @@ dasd_eckd_probe (struct ccw_device *cdev)
{
int ret;
- ret = dasd_generic_probe (cdev, &dasd_eckd_discipline);
- if (ret)
+ /* set ECKD specific ccw-device options */
+ ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE);
+ if (ret) {
+ printk(KERN_WARNING
+ "dasd_eckd_probe: could not set ccw-device options "
+ "for %s\n", cdev->dev.bus_id);
return ret;
- ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | CCWDEV_ALLOW_FORCE);
- return 0;
+ }
+ ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
+ return ret;
}
static int
dasd_eckd_set_online(struct ccw_device *cdev)
{
- return dasd_generic_set_online (cdev, &dasd_eckd_discipline);
+ return dasd_generic_set_online(cdev, &dasd_eckd_discipline);
}
static struct ccw_driver dasd_eckd_driver = {
@@ -210,14 +215,14 @@ check_XRC (struct ccw1 *de_ccw,
/* switch on System Time Stamp - needed for XRC Support */
if (private->rdc_data.facilities.XRC_supported) {
-
+
data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */
data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
-
+
data->ep_sys_time = get_clock ();
-
+
de_ccw->count = sizeof (struct DE_eckd_data);
- de_ccw->flags |= CCW_FLAG_SLI;
+ de_ccw->flags |= CCW_FLAG_SLI;
}
return;
@@ -296,8 +301,8 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
/* check for sequential prestage - enhance cylinder range */
if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
data->attributes.operation == DASD_SEQ_ACCESS) {
-
- if (end.cyl + private->attrib.nr_cyl < geo.cyl)
+
+ if (end.cyl + private->attrib.nr_cyl < geo.cyl)
end.cyl += private->attrib.nr_cyl;
else
end.cyl = (geo.cyl - 1);
@@ -317,7 +322,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
struct dasd_eckd_private *private;
int sector;
int dn, d;
-
+
private = (struct dasd_eckd_private *) device->private;
DBF_DEV_EVENT(DBF_INFO, device,
@@ -463,11 +468,11 @@ dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid)
return -ENODEV;
memset(uid, 0, sizeof(struct dasd_uid));
- strncpy(uid->vendor, confdata->ned1.HDA_manufacturer,
- sizeof(uid->vendor) - 1);
+ memcpy(uid->vendor, confdata->ned1.HDA_manufacturer,
+ sizeof(uid->vendor) - 1);
EBCASC(uid->vendor, sizeof(uid->vendor) - 1);
- strncpy(uid->serial, confdata->ned1.HDA_location,
- sizeof(uid->serial) - 1);
+ memcpy(uid->serial, confdata->ned1.HDA_location,
+ sizeof(uid->serial) - 1);
EBCASC(uid->serial, sizeof(uid->serial) - 1);
uid->ssid = confdata->neq.subsystemID;
if (confdata->ned2.sneq.flags == 0x40) {
@@ -541,6 +546,86 @@ dasd_eckd_read_conf(struct dasd_device *device)
}
/*
+ * Build CP for Perform Subsystem Function - SSC.
+ */
+struct dasd_ccw_req *
+dasd_eckd_build_psf_ssc(struct dasd_device *device)
+{
+ struct dasd_ccw_req *cqr;
+ struct dasd_psf_ssc_data *psf_ssc_data;
+ struct ccw1 *ccw;
+
+ cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ ,
+ sizeof(struct dasd_psf_ssc_data),
+ device);
+
+ if (IS_ERR(cqr)) {
+ DEV_MESSAGE(KERN_WARNING, device, "%s",
+ "Could not allocate PSF-SSC request");
+ return cqr;
+ }
+ psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
+ psf_ssc_data->order = PSF_ORDER_SSC;
+ psf_ssc_data->suborder = 0x08;
+
+ ccw = cqr->cpaddr;
+ ccw->cmd_code = DASD_ECKD_CCW_PSF;
+ ccw->cda = (__u32)(addr_t)psf_ssc_data;
+ ccw->count = 66;
+
+ cqr->device = device;
+ cqr->expires = 10*HZ;
+ cqr->buildclk = get_clock();
+ cqr->status = DASD_CQR_FILLED;
+ return cqr;
+}
+
+/*
+ * Perform Subsystem Function.
+ * It is necessary to trigger CIO for channel revalidation since this
+ * call might change behaviour of DASD devices.
+ */
+static int
+dasd_eckd_psf_ssc(struct dasd_device *device)
+{
+ struct dasd_ccw_req *cqr;
+ int rc;
+
+ cqr = dasd_eckd_build_psf_ssc(device);
+ if (IS_ERR(cqr))
+ return PTR_ERR(cqr);
+
+ rc = dasd_sleep_on(cqr);
+ if (!rc)
+ /* trigger CIO to reprobe devices */
+ css_schedule_reprobe();
+ dasd_sfree_request(cqr, cqr->device);
+ return rc;
+}
+
+/*
+ * Valide storage server of current device.
+ */
+static int
+dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid)
+{
+ int rc;
+
+ /* Currently PAV is the only reason to 'validate' server on LPAR */
+ if (dasd_nopav || MACHINE_IS_VM)
+ return 0;
+
+ rc = dasd_eckd_psf_ssc(device);
+ /* may be requested feature is not available on server,
+ * therefore just report error and go ahead */
+ DEV_MESSAGE(KERN_INFO, device,
+ "PSF-SSC on storage subsystem %s.%s.%04x returned rc=%d",
+ uid->vendor, uid->serial, uid->ssid, rc);
+ /* RE-Read Configuration Data */
+ return dasd_eckd_read_conf(device);
+}
+
+/*
* Check device characteristics.
* If the device is accessible using ECKD discipline, the device is enabled.
*/
@@ -554,7 +639,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
private = (struct dasd_eckd_private *) device->private;
if (private == NULL) {
- private = kmalloc(sizeof(struct dasd_eckd_private),
+ private = kzalloc(sizeof(struct dasd_eckd_private),
GFP_KERNEL | GFP_DMA);
if (private == NULL) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
@@ -562,7 +647,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
"data");
return -ENOMEM;
}
- memset(private, 0, sizeof(struct dasd_eckd_private));
device->private = (void *) private;
}
/* Invalidate status of initial analysis. */
@@ -571,16 +655,29 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
private->attrib.operation = DASD_NORMAL_CACHE;
private->attrib.nr_cyl = 0;
+ /* Read Configuration Data */
+ rc = dasd_eckd_read_conf(device);
+ if (rc)
+ return rc;
+
+ /* Generate device unique id and register in devmap */
+ rc = dasd_eckd_generate_uid(device, &uid);
+ if (rc)
+ return rc;
+ rc = dasd_set_uid(device->cdev, &uid);
+ if (rc == 1) /* new server found */
+ rc = dasd_eckd_validate_server(device, &uid);
+ if (rc)
+ return rc;
+
/* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data);
memset(rdc_data, 0, sizeof(rdc_data));
rc = read_dev_chars(device->cdev, &rdc_data, 64);
- if (rc) {
+ if (rc)
DEV_MESSAGE(KERN_WARNING, device,
- "Read device characteristics returned error %d",
- rc);
- return rc;
- }
+ "Read device characteristics returned "
+ "rc=%d", rc);
DEV_MESSAGE(KERN_INFO, device,
"%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d",
@@ -591,19 +688,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
private->rdc_data.no_cyl,
private->rdc_data.trk_per_cyl,
private->rdc_data.sec_per_trk);
-
- /* Read Configuration Data */
- rc = dasd_eckd_read_conf (device);
- if (rc)
- return rc;
-
- /* Generate device unique id and register in devmap */
- rc = dasd_eckd_generate_uid(device, &uid);
- if (rc)
- return rc;
-
- rc = dasd_set_uid(device->cdev, &uid);
-
return rc;
}
@@ -773,7 +857,7 @@ dasd_eckd_end_analysis(struct dasd_device *device)
((private->rdc_data.no_cyl *
private->rdc_data.trk_per_cyl *
blk_per_trk * (device->bp_block >> 9)) >> 1),
- ((blk_per_trk * device->bp_block) >> 10),
+ ((blk_per_trk * device->bp_block) >> 10),
private->uses_cdl ?
"compatible disk layout" : "linux disk layout");
@@ -970,7 +1054,7 @@ dasd_eckd_format_device(struct dasd_device * device,
if (i < 3) {
ect->kl = 4;
ect->dl = sizes_trk0[i] - 4;
- }
+ }
}
if ((fdata->intensity & 0x08) &&
fdata->start_unit == 1) {
@@ -1270,7 +1354,7 @@ dasd_eckd_fill_info(struct dasd_device * device,
/*
* Release device ioctl.
- * Buils a channel programm to releases a prior reserved
+ * Buils a channel programm to releases a prior reserved
* (see dasd_eckd_reserve) device.
*/
static int
@@ -1310,8 +1394,8 @@ dasd_eckd_release(struct dasd_device *device)
/*
* Reserve device ioctl.
* Options are set to 'synchronous wait for interrupt' and
- * 'timeout the request'. This leads to a terminate IO if
- * the interrupt is outstanding for a certain time.
+ * 'timeout the request'. This leads to a terminate IO if
+ * the interrupt is outstanding for a certain time.
*/
static int
dasd_eckd_reserve(struct dasd_device *device)
@@ -1349,7 +1433,7 @@ dasd_eckd_reserve(struct dasd_device *device)
/*
* Steal lock ioctl - unconditional reserve device.
- * Buils a channel programm to break a device's reservation.
+ * Buils a channel programm to break a device's reservation.
* (unconditional reserve)
*/
static int
@@ -1522,6 +1606,40 @@ dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp)
}
/*
+ * Dump the range of CCWs into 'page' buffer
+ * and return number of printed chars.
+ */
+static inline int
+dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
+{
+ int len, count;
+ char *datap;
+
+ len = 0;
+ while (from <= to) {
+ len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ " CCW %p: %08X %08X DAT:",
+ from, ((int *) from)[0], ((int *) from)[1]);
+
+ /* get pointer to data (consider IDALs) */
+ if (from->flags & CCW_FLAG_IDA)
+ datap = (char *) *((addr_t *) (addr_t) from->cda);
+ else
+ datap = (char *) ((addr_t) from->cda);
+
+ /* dump data (max 32 bytes) */
+ for (count = 0; count < from->count && count < 32; count++) {
+ if (count % 8 == 0) len += sprintf(page + len, " ");
+ if (count % 4 == 0) len += sprintf(page + len, " ");
+ len += sprintf(page + len, "%02x", datap[count]);
+ }
+ len += sprintf(page + len, "\n");
+ from++;
+ }
+ return len;
+}
+
+/*
* Print sense data and related channel program.
* Parts are printed because printk buffer is only 1024 bytes.
*/
@@ -1530,8 +1648,8 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
struct irb *irb)
{
char *page;
- struct ccw1 *act, *end, *last;
- int len, sl, sct, count;
+ struct ccw1 *first, *last, *fail, *from, *to;
+ int len, sl, sct;
page = (char *) get_zeroed_page(GFP_ATOMIC);
if (page == NULL) {
@@ -1539,7 +1657,8 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
"No memory to dump sense data");
return;
}
- len = sprintf(page, KERN_ERR PRINTK_HEADER
+ /* dump the sense data */
+ len = sprintf(page, KERN_ERR PRINTK_HEADER
" I/O status report for device %s:\n",
device->cdev->dev.bus_id);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
@@ -1564,87 +1683,55 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
if (irb->ecw[27] & DASD_SENSE_BIT_0) {
/* 24 Byte Sense Data */
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " 24 Byte: %x MSG %x, "
- "%s MSGb to SYSOP\n",
- irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
- irb->ecw[1] & 0x10 ? "" : "no");
+ sprintf(page + len, KERN_ERR PRINTK_HEADER
+ " 24 Byte: %x MSG %x, "
+ "%s MSGb to SYSOP\n",
+ irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
+ irb->ecw[1] & 0x10 ? "" : "no");
} else {
/* 32 Byte Sense Data */
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " 32 Byte: Format: %x "
- "Exception class %x\n",
- irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
+ sprintf(page + len, KERN_ERR PRINTK_HEADER
+ " 32 Byte: Format: %x "
+ "Exception class %x\n",
+ irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
}
} else {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " SORRY - NO VALID SENSE AVAILABLE\n");
+ sprintf(page + len, KERN_ERR PRINTK_HEADER
+ " SORRY - NO VALID SENSE AVAILABLE\n");
}
- MESSAGE_LOG(KERN_ERR, "%s",
- page + sizeof(KERN_ERR PRINTK_HEADER));
-
- /* dump the Channel Program */
- /* print first CCWs (maximum 8) */
- act = req->cpaddr;
- for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
- end = min(act + 8, last);
- len = sprintf(page, KERN_ERR PRINTK_HEADER
+ printk("%s", page);
+
+ /* dump the Channel Program (max 140 Bytes per line) */
+ /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
+ first = req->cpaddr;
+ for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
+ to = min(first + 6, last);
+ len = sprintf(page, KERN_ERR PRINTK_HEADER
" Related CP in req: %p\n", req);
- while (act <= end) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " CCW %p: %08X %08X DAT:",
- act, ((int *) act)[0], ((int *) act)[1]);
- for (count = 0; count < 32 && count < act->count;
- count += sizeof(int))
- len += sprintf(page + len, " %08X",
- ((int *) (addr_t) act->cda)
- [(count>>2)]);
- len += sprintf(page + len, "\n");
- act++;
- }
- MESSAGE_LOG(KERN_ERR, "%s",
- page + sizeof(KERN_ERR PRINTK_HEADER));
+ dasd_eckd_dump_ccw_range(first, to, page + len);
+ printk("%s", page);
- /* print failing CCW area */
+ /* print failing CCW area (maximum 4) */
+ /* scsw->cda is either valid or zero */
len = 0;
- if (act < ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) {
- act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2;
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
- }
- end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last);
- while (act <= end) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " CCW %p: %08X %08X DAT:",
- act, ((int *) act)[0], ((int *) act)[1]);
- for (count = 0; count < 32 && count < act->count;
- count += sizeof(int))
- len += sprintf(page + len, " %08X",
- ((int *) (addr_t) act->cda)
- [(count>>2)]);
- len += sprintf(page + len, "\n");
- act++;
+ from = ++to;
+ fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
+ if (from < fail - 2) {
+ from = fail - 2; /* there is a gap - print header */
+ len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
}
+ to = min(fail + 1, last);
+ len += dasd_eckd_dump_ccw_range(from, to, page + len);
- /* print last CCWs */
- if (act < last - 2) {
- act = last - 2;
+ /* print last CCWs (maximum 2) */
+ from = max(from, ++to);
+ if (from < last - 1) {
+ from = last - 1; /* there is a gap - print header */
len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
}
- while (act <= last) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " CCW %p: %08X %08X DAT:",
- act, ((int *) act)[0], ((int *) act)[1]);
- for (count = 0; count < 32 && count < act->count;
- count += sizeof(int))
- len += sprintf(page + len, " %08X",
- ((int *) (addr_t) act->cda)
- [(count>>2)]);
- len += sprintf(page + len, "\n");
- act++;
- }
+ len += dasd_eckd_dump_ccw_range(from, last, page + len);
if (len > 0)
- MESSAGE_LOG(KERN_ERR, "%s",
- page + sizeof(KERN_ERR PRINTK_HEADER));
+ printk("%s", page);
free_page((unsigned long) page);
}
@@ -1685,14 +1772,8 @@ static struct dasd_discipline dasd_eckd_discipline = {
static int __init
dasd_eckd_init(void)
{
- int ret;
-
ASCEBC(dasd_eckd_discipline.ebcname, 4);
-
- ret = ccw_driver_register(&dasd_eckd_driver);
- if (!ret)
- dasd_generic_auto_online(&dasd_eckd_driver);
- return ret;
+ return ccw_driver_register(&dasd_eckd_driver);
}
static void __exit
@@ -1703,22 +1784,3 @@ dasd_eckd_cleanup(void)
module_init(dasd_eckd_init);
module_exit(dasd_eckd_cleanup);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index d5734e976e1c..712ff1650134 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -1,7 +1,7 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_eckd.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Horst Hummel <Horst.Hummel@de.ibm.com>
+ * Horst Hummel <Horst.Hummel@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
@@ -41,9 +41,10 @@
#define DASD_ECKD_CCW_RESERVE 0xB4
/*
- *Perform Subsystem Function / Sub-Orders
+ * Perform Subsystem Function / Sub-Orders
*/
-#define PSF_ORDER_PRSSD 0x18
+#define PSF_ORDER_PRSSD 0x18
+#define PSF_ORDER_SSC 0x1D
/*****************************************************************************
* SECTION: Type Definitions
@@ -155,7 +156,7 @@ struct dasd_eckd_characteristics {
unsigned char reserved2:4;
unsigned char reserved3:8;
unsigned char defect_wr:1;
- unsigned char XRC_supported:1;
+ unsigned char XRC_supported:1;
unsigned char reserved4:1;
unsigned char striping:1;
unsigned char reserved5:4;
@@ -343,7 +344,7 @@ struct dasd_eckd_path {
};
/*
- * Perform Subsystem Function - Prepare for Read Subsystem Data
+ * Perform Subsystem Function - Prepare for Read Subsystem Data
*/
struct dasd_psf_prssd_data {
unsigned char order;
@@ -353,4 +354,15 @@ struct dasd_psf_prssd_data {
unsigned char varies[9];
} __attribute__ ((packed));
+/*
+ * Perform Subsystem Function - Set Subsystem Characteristics
+ */
+struct dasd_psf_ssc_data {
+ unsigned char order;
+ unsigned char flags;
+ unsigned char cu_type[4];
+ unsigned char suborder;
+ unsigned char reserved[59];
+} __attribute__((packed));
+
#endif /* DASD_ECKD_H */
diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c
index 2d946b6ca074..e0bf30ebb215 100644
--- a/drivers/s390/block/dasd_eer.c
+++ b/drivers/s390/block/dasd_eer.c
@@ -89,7 +89,7 @@ struct eerbuffer {
};
static LIST_HEAD(bufferlist);
-static spinlock_t bufferlock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(bufferlock);
static DECLARE_WAIT_QUEUE_HEAD(dasd_eer_read_wait_queue);
/*
@@ -276,7 +276,7 @@ struct dasd_eer_header {
__u64 tv_sec;
__u64 tv_usec;
char busid[DASD_EER_BUSID_SIZE];
-};
+} __attribute__ ((packed));
/*
* The following function can be used for those triggers that have
@@ -521,6 +521,8 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
unsigned long flags;
eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL);
+ if (!eerb)
+ return -ENOMEM;
eerb->buffer_page_count = eer_pages;
if (eerb->buffer_page_count < 1 ||
eerb->buffer_page_count > INT_MAX / PAGE_SIZE) {
@@ -676,7 +678,7 @@ int __init dasd_eer_init(void)
return 0;
}
-void __exit dasd_eer_exit(void)
+void dasd_eer_exit(void)
{
WARN_ON(misc_deregister(&dasd_eer_dev) != 0);
}
diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c
index b842377cb0c6..58a65097922b 100644
--- a/drivers/s390/block/dasd_erp.c
+++ b/drivers/s390/block/dasd_erp.c
@@ -9,7 +9,6 @@
*
*/
-#include <linux/config.h>
#include <linux/ctype.h>
#include <linux/init.h>
@@ -90,7 +89,7 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr)
/* just retry - there is nothing to save ... I got no sense data.... */
if (cqr->retries > 0) {
- DEV_MESSAGE (KERN_DEBUG, device,
+ DEV_MESSAGE (KERN_DEBUG, device,
"default ERP called (%i retries left)",
cqr->retries);
cqr->lpm = LPM_ANYPATH;
@@ -155,7 +154,7 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
/*
* Print the hex dump of the memory used by a request. This includes
- * all error recovery ccws that have been chained in from of the
+ * all error recovery ccws that have been chained in from of the
* real request.
*/
static inline void
@@ -227,12 +226,12 @@ dasd_log_ccw(struct dasd_ccw_req * cqr, int caller, __u32 cpa)
/*
* Log bytes arround failed CCW but only if we did
* not log the whole CP of the CCW is outside the
- * logged CP.
+ * logged CP.
*/
if (cplength > 40 ||
((addr_t) cpa < (addr_t) lcqr->cpaddr &&
(addr_t) cpa > (addr_t) (lcqr->cpaddr + cplength + 4))) {
-
+
DEV_MESSAGE(KERN_ERR, device,
"Failed CCW (%p) (area):",
(void *) (long) cpa);
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 91145698f8e9..e85015be109b 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_fba.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
@@ -6,7 +6,6 @@
*
*/
-#include <linux/config.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <asm/debug.h>
@@ -45,8 +44,8 @@ struct dasd_fba_private {
};
static struct ccw_device_id dasd_fba_ids[] = {
- { CCW_DEVICE_DEVTYPE (0x6310, 0, 0x9336, 0), driver_info: 0x1},
- { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3370, 0), driver_info: 0x2},
+ { CCW_DEVICE_DEVTYPE (0x6310, 0, 0x9336, 0), .driver_info = 0x1},
+ { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3370, 0), .driver_info = 0x2},
{ /* end of list */ },
};
@@ -56,19 +55,13 @@ static struct ccw_driver dasd_fba_driver; /* see below */
static int
dasd_fba_probe(struct ccw_device *cdev)
{
- int ret;
-
- ret = dasd_generic_probe (cdev, &dasd_fba_discipline);
- if (ret)
- return ret;
- ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
- return 0;
+ return dasd_generic_probe(cdev, &dasd_fba_discipline);
}
static int
dasd_fba_set_online(struct ccw_device *cdev)
{
- return dasd_generic_set_online (cdev, &dasd_fba_discipline);
+ return dasd_generic_set_online(cdev, &dasd_fba_discipline);
}
static struct ccw_driver dasd_fba_driver = {
@@ -125,13 +118,13 @@ static int
dasd_fba_check_characteristics(struct dasd_device *device)
{
struct dasd_fba_private *private;
- struct ccw_device *cdev = device->cdev;
+ struct ccw_device *cdev = device->cdev;
void *rdc_data;
int rc;
private = (struct dasd_fba_private *) device->private;
if (private == NULL) {
- private = kmalloc(sizeof(struct dasd_fba_private), GFP_KERNEL);
+ private = kzalloc(sizeof(struct dasd_fba_private), GFP_KERNEL);
if (private == NULL) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
"memory allocation failed for private "
@@ -204,7 +197,7 @@ dasd_fba_examine_error(struct dasd_ccw_req * cqr, struct irb * irb)
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
-
+
cdev = device->cdev;
switch (cdev->id.dev_type) {
case 0x3370:
@@ -539,7 +532,7 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
* 8192 bytes (=2 pages). For 64 bit one dasd_mchunkt_t structure has
* 24 bytes, the struct dasd_ccw_req has 136 bytes and each block can use
* up to 16 bytes (8 for the ccw and 8 for the idal pointer). In
- * addition we have one define extent ccw + 16 bytes of data and a
+ * addition we have one define extent ccw + 16 bytes of data and a
* locate record ccw for each block (stupid devices!) + 16 bytes of data.
* That makes:
* (8192 - 24 - 136 - 8 - 16) / 40 = 200.2 blocks at maximum.
@@ -569,16 +562,8 @@ static struct dasd_discipline dasd_fba_discipline = {
static int __init
dasd_fba_init(void)
{
- int ret;
-
ASCEBC(dasd_fba_discipline.ebcname, 4);
-
- ret = ccw_driver_register(&dasd_fba_driver);
- if (ret)
- return ret;
-
- dasd_generic_auto_online(&dasd_fba_driver);
- return 0;
+ return ccw_driver_register(&dasd_fba_driver);
}
static void __exit
@@ -589,22 +574,3 @@ dasd_fba_cleanup(void)
module_init(dasd_fba_init);
module_exit(dasd_fba_cleanup);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_fba.h b/drivers/s390/block/dasd_fba.h
index da1fa91fc01d..14c910baa5fe 100644
--- a/drivers/s390/block/dasd_fba.h
+++ b/drivers/s390/block/dasd_fba.h
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_fba.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c
index fce2835e7d19..d163632101d2 100644
--- a/drivers/s390/block/dasd_genhd.c
+++ b/drivers/s390/block/dasd_genhd.c
@@ -11,7 +11,6 @@
*
*/
-#include <linux/config.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/blkpg.h>
@@ -68,8 +67,6 @@ dasd_gendisk_alloc(struct dasd_device *device)
}
len += sprintf(gdp->disk_name + len, "%c", 'a'+(device->devindex%26));
- sprintf(gdp->devfs_name, "dasd/%s", device->cdev->dev.bus_id);
-
if (device->features & DASD_FEATURE_READONLY)
set_disk_ro(gdp, 1);
gdp->private_data = device;
@@ -86,10 +83,12 @@ dasd_gendisk_alloc(struct dasd_device *device)
void
dasd_gendisk_free(struct dasd_device *device)
{
- del_gendisk(device->gdp);
- device->gdp->queue = 0;
- put_disk(device->gdp);
- device->gdp = 0;
+ if (device->gdp) {
+ del_gendisk(device->gdp);
+ device->gdp->queue = NULL;
+ put_disk(device->gdp);
+ device->gdp = NULL;
+ }
}
/*
@@ -139,7 +138,7 @@ dasd_destroy_partitions(struct dasd_device * device)
* device->bdev to lower the offline open_count limit again.
*/
bdev = device->bdev;
- device->bdev = 0;
+ device->bdev = NULL;
/*
* See fs/partition/check.c:delete_partition
@@ -148,7 +147,7 @@ dasd_destroy_partitions(struct dasd_device * device)
*/
memset(&bpart, 0, sizeof(struct blkpg_partition));
memset(&barg, 0, sizeof(struct blkpg_ioctl_arg));
- barg.data = &bpart;
+ barg.data = (void __user *) &bpart;
barg.op = BLKPG_DEL_PARTITION;
for (bpart.pno = device->gdp->minors - 1; bpart.pno > 0; bpart.pno--)
ioctl_by_bdev(bdev, BLKPG, (unsigned long) &barg);
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index d4b13e300a76..9f52004f6fc2 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -1,7 +1,7 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_int.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Horst Hummel <Horst.Hummel@de.ibm.com>
+ * Horst Hummel <Horst.Hummel@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
@@ -54,7 +54,6 @@
#include <linux/module.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
-#include <linux/devfs_fs_kernel.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/interrupt.h>
@@ -186,7 +185,7 @@ struct dasd_ccw_req {
void *callback_data;
};
-/*
+/*
* dasd_ccw_req -> status can be:
*/
#define DASD_CQR_FILLED 0x00 /* request is ready to be processed */
@@ -248,7 +247,7 @@ struct dasd_discipline {
/*
* Error recovery functions. examine_error() returns a value that
* indicates what to do for an error condition. If examine_error()
- * returns 'dasd_era_recover' erp_action() is called to create a
+ * returns 'dasd_era_recover' erp_action() is called to create a
* special error recovery ccw. erp_postaction() is called after
* an error recovery ccw has finished its execution. dump_sense
* is called for every error condition to print the sense data
@@ -302,11 +301,11 @@ struct dasd_device {
spinlock_t request_queue_lock;
struct block_device *bdev;
unsigned int devindex;
- unsigned long blocks; /* size of volume in blocks */
- unsigned int bp_block; /* bytes per block */
- unsigned int s2b_shift; /* log2 (bp_block/512) */
- unsigned long flags; /* per device flags */
- unsigned short features; /* copy of devmap-features (read-only!) */
+ unsigned long blocks; /* size of volume in blocks */
+ unsigned int bp_block; /* bytes per block */
+ unsigned int s2b_shift; /* log2 (bp_block/512) */
+ unsigned long flags; /* per device flags */
+ unsigned short features; /* copy of devmap-features (read-only!) */
/* extended error reporting stuff (eer) */
struct dasd_ccw_req *eer_cqr;
@@ -513,12 +512,12 @@ void dasd_generic_remove (struct ccw_device *cdev);
int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *);
int dasd_generic_set_offline (struct ccw_device *cdev);
int dasd_generic_notify(struct ccw_device *, int);
-void dasd_generic_auto_online (struct ccw_driver *);
/* externals in dasd_devmap.c */
extern int dasd_max_devindex;
extern int dasd_probeonly;
extern int dasd_autodetect;
+extern int dasd_nopav;
int dasd_devmap_init(void);
void dasd_devmap_exit(void);
@@ -535,6 +534,7 @@ int dasd_add_sysfs_files(struct ccw_device *);
void dasd_remove_sysfs_files(struct ccw_device *);
struct dasd_device *dasd_device_from_cdev(struct ccw_device *);
+struct dasd_device *dasd_device_from_cdev_locked(struct ccw_device *);
struct dasd_device *dasd_device_from_devindex(int);
int dasd_parse(void);
@@ -606,22 +606,3 @@ static inline int dasd_eer_enabled(struct dasd_device *device)
#endif /* __KERNEL__ */
#endif /* DASD_H */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index b8c80d28df41..8fed3603e9ea 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -9,7 +9,6 @@
*
* i/o controls for the dasd driver.
*/
-#include <linux/config.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <linux/fs.h>
@@ -90,10 +89,10 @@ static int
dasd_ioctl_quiesce(struct dasd_device *device)
{
unsigned long flags;
-
+
if (!capable (CAP_SYS_ADMIN))
return -EACCES;
-
+
DEV_MESSAGE (KERN_DEBUG, device, "%s",
"Quiesce IO on device");
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
@@ -110,13 +109,13 @@ static int
dasd_ioctl_resume(struct dasd_device *device)
{
unsigned long flags;
-
- if (!capable (CAP_SYS_ADMIN))
+
+ if (!capable (CAP_SYS_ADMIN))
return -EACCES;
DEV_MESSAGE (KERN_DEBUG, device, "%s",
"resume IO on device");
-
+
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~DASD_STOPPED_QUIESCE;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
@@ -287,7 +286,7 @@ dasd_ioctl_information(struct dasd_device *device,
dasd_info->open_count = atomic_read(&device->open_count);
if (!device->bdev)
dasd_info->open_count++;
-
+
/*
* check if device is really formatted
* LDL / CDL was returned by 'fill_info'
@@ -346,7 +345,7 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
if (bdev != bdev->bd_contains)
// ro setting is not allowed for partitions
return -EINVAL;
- if (get_user(intval, (int *)argp))
+ if (get_user(intval, (int __user *)argp))
return -EFAULT;
set_disk_ro(bdev->bd_disk, intval);
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index ad23aede356c..bfa010f6dab2 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -11,7 +11,6 @@
*
*/
-#include <linux/config.h>
#include <linux/ctype.h>
#include <linux/seq_file.h>
#include <linux/vmalloc.h>
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index 54ecd548c318..a04d9120cef0 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -36,7 +36,6 @@
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/sysdev.h>
#include <linux/bio.h>
-#include <linux/devfs_fs_kernel.h>
#include <asm/uaccess.h>
#define XPRAM_NAME "xpram"
@@ -49,15 +48,6 @@
#define PRINT_ERR(x...) printk(KERN_ERR XPRAM_NAME " error:" x)
-static struct sysdev_class xpram_sysclass = {
- set_kset_name("xpram"),
-};
-
-static struct sys_device xpram_sys_device = {
- .id = 0,
- .cls = &xpram_sysclass,
-};
-
typedef struct {
unsigned int size; /* size of xpram segment in pages */
unsigned int offset; /* start page of xpram segment */
@@ -72,11 +62,11 @@ static int xpram_devs;
/*
* Parameter parsing functions.
*/
-static int devs = XPRAM_DEVS;
-static unsigned int sizes[XPRAM_MAX_DEVS];
+static int __initdata devs = XPRAM_DEVS;
+static char __initdata *sizes[XPRAM_MAX_DEVS];
module_param(devs, int, 0);
-module_param_array(sizes, int, NULL, 0);
+module_param_array(sizes, charp, NULL, 0);
MODULE_PARM_DESC(devs, "number of devices (\"partitions\"), " \
"the default is " __MODULE_STRING(XPRAM_DEVS) "\n");
@@ -87,59 +77,6 @@ MODULE_PARM_DESC(sizes, "list of device (partition) sizes " \
"claimed by explicit sizes\n");
MODULE_LICENSE("GPL");
-#ifndef MODULE
-/*
- * Parses the kernel parameters given in the kernel parameter line.
- * The expected format is
- * <number_of_partitions>[","<partition_size>]*
- * where
- * devices is a positive integer that initializes xpram_devs
- * each size is a non-negative integer possibly followed by a
- * magnitude (k,K,m,M,g,G), the list of sizes initialises
- * xpram_sizes
- *
- * Arguments
- * str: substring of kernel parameter line that contains xprams
- * kernel parameters.
- *
- * Result 0 on success, -EINVAL else -- only for Version > 2.3
- *
- * Side effects
- * the global variabls devs is set to the value of
- * <number_of_partitions> and sizes[i] is set to the i-th
- * partition size (if provided). A parsing error of a value
- * results in this value being set to -EINVAL.
- */
-static int __init xpram_setup (char *str)
-{
- char *cp;
- int i;
-
- devs = simple_strtoul(str, &cp, 10);
- if (cp <= str || devs > XPRAM_MAX_DEVS)
- return 0;
- for (i = 0; (i < devs) && (*cp++ == ','); i++) {
- sizes[i] = simple_strtoul(cp, &cp, 10);
- if (*cp == 'g' || *cp == 'G') {
- sizes[i] <<= 20;
- cp++;
- } else if (*cp == 'm' || *cp == 'M') {
- sizes[i] <<= 10;
- cp++;
- } else if (*cp == 'k' || *cp == 'K')
- cp++;
- while (isspace(*cp)) cp++;
- }
- if (*cp == ',' && i >= devs)
- PRINT_WARN("partition sizes list has too many entries.\n");
- else if (*cp != 0)
- PRINT_WARN("ignored '%s' at end of parameter string.\n", cp);
- return 1;
-}
-
-__setup("xpram_parts=", xpram_setup);
-#endif
-
/*
* Copy expanded memory page (4kB) into main memory
* Arguments
@@ -152,28 +89,15 @@ __setup("xpram_parts=", xpram_setup);
*/
static int xpram_page_in (unsigned long page_addr, unsigned int xpage_index)
{
- int cc;
+ int cc = 2; /* return unused cc 2 if pgin traps */
- __asm__ __volatile__ (
- " lhi %0,2\n" /* return unused cc 2 if pgin traps */
- " .insn rre,0xb22e0000,%1,%2\n" /* pgin %1,%2 */
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " .insn rre,0xb22e0000,%1,%2\n" /* pgin %1,%2 */
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifndef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (cc)
- : "a" (__pa(page_addr)), "a" (xpage_index)
- : "cc" );
+ EX_TABLE(0b,1b)
+ : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
if (cc == 3)
return -ENXIO;
if (cc == 2) {
@@ -200,28 +124,15 @@ static int xpram_page_in (unsigned long page_addr, unsigned int xpage_index)
*/
static long xpram_page_out (unsigned long page_addr, unsigned int xpage_index)
{
- int cc;
+ int cc = 2; /* return unused cc 2 if pgin traps */
- __asm__ __volatile__ (
- " lhi %0,2\n" /* return unused cc 2 if pgout traps */
- " .insn rre,0xb22f0000,%1,%2\n" /* pgout %1,%2 */
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " .insn rre,0xb22f0000,%1,%2\n" /* pgout %1,%2 */
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifndef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (cc)
- : "a" (__pa(page_addr)), "a" (xpage_index)
- : "cc" );
+ EX_TABLE(0b,1b)
+ : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
if (cc == 3)
return -ENXIO;
if (cc == 2) {
@@ -358,6 +269,7 @@ static int __init xpram_setup_sizes(unsigned long pages)
{
unsigned long mem_needed;
unsigned long mem_auto;
+ unsigned long long size;
int mem_auto_no;
int i;
@@ -375,7 +287,19 @@ static int __init xpram_setup_sizes(unsigned long pages)
mem_needed = 0;
mem_auto_no = 0;
for (i = 0; i < xpram_devs; i++) {
- xpram_sizes[i] = (sizes[i] + 3) & -4UL;
+ if (sizes[i]) {
+ size = simple_strtoull(sizes[i], &sizes[i], 0);
+ switch (sizes[i][0]) {
+ case 'g':
+ case 'G':
+ size <<= 20;
+ break;
+ case 'm':
+ case 'M':
+ size <<= 10;
+ }
+ xpram_sizes[i] = (size + 3) & -4UL;
+ }
if (xpram_sizes[i])
mem_needed += xpram_sizes[i];
else
@@ -439,8 +363,6 @@ static int __init xpram_setup_blkdev(void)
if (rc < 0)
goto out;
- devfs_mk_dir("slram");
-
/*
* Assign the other needed values: make request function, sizes and
* hardsect size. All the minor devices feature the same value.
@@ -469,14 +391,12 @@ static int __init xpram_setup_blkdev(void)
disk->private_data = &xpram_devices[i];
disk->queue = xpram_queue;
sprintf(disk->disk_name, "slram%d", i);
- sprintf(disk->devfs_name, "slram/%d", i);
set_capacity(disk, xpram_sizes[i] << 1);
add_disk(disk);
}
return 0;
out_unreg:
- devfs_remove("slram");
unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME);
out:
while (i--)
@@ -495,10 +415,7 @@ static void __exit xpram_exit(void)
put_disk(xpram_disks[i]);
}
unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME);
- devfs_remove("slram");
blk_cleanup_queue(xpram_queue);
- sysdev_unregister(&xpram_sys_device);
- sysdev_class_unregister(&xpram_sysclass);
}
static int __init xpram_init(void)
@@ -510,25 +427,13 @@ static int __init xpram_init(void)
PRINT_WARN("No expanded memory available\n");
return -ENODEV;
}
- xpram_pages = xpram_highest_page_index();
+ xpram_pages = xpram_highest_page_index() + 1;
PRINT_INFO(" %u pages expanded memory found (%lu KB).\n",
xpram_pages, (unsigned long) xpram_pages*4);
rc = xpram_setup_sizes(xpram_pages);
if (rc)
return rc;
- rc = sysdev_class_register(&xpram_sysclass);
- if (rc)
- return rc;
-
- rc = sysdev_register(&xpram_sys_device);
- if (rc) {
- sysdev_class_unregister(&xpram_sysclass);
- return rc;
- }
- rc = xpram_setup_blkdev();
- if (rc)
- sysdev_unregister(&xpram_sys_device);
- return rc;
+ return xpram_setup_blkdev();
}
module_init(xpram_init);
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 0c0162ff6c0c..c3e97b4fc186 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o
obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
obj-$(CONFIG_MONREADER) += monreader.o
+obj-$(CONFIG_MONWRITER) += monwriter.o
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 606f6ad285a0..2fa566fa6da4 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -11,7 +11,6 @@
* Dan Morrison, IBM Corporation (dmorriso@cse.buffalo.edu)
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
@@ -694,7 +693,7 @@ raw3215_probe (struct ccw_device *cdev)
GFP_KERNEL|GFP_DMA);
if (raw->buffer == NULL) {
spin_lock(&raw3215_device_lock);
- raw3215[line] = 0;
+ raw3215[line] = NULL;
spin_unlock(&raw3215_device_lock);
kfree(raw);
return -ENOMEM;
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index ef607a1de55a..7566be890688 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -8,7 +8,6 @@
* -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
*/
-#include <linux/config.h>
#include <linux/bootmem.h>
#include <linux/console.h>
#include <linux/init.h>
diff --git a/drivers/s390/char/ctrlchar.c b/drivers/s390/char/ctrlchar.c
index be463242cf0f..d83eb6358bac 100644
--- a/drivers/s390/char/ctrlchar.c
+++ b/drivers/s390/char/ctrlchar.c
@@ -7,7 +7,6 @@
*
*/
-#include <linux/config.h>
#include <linux/stddef.h>
#include <asm/errno.h>
#include <linux/sysrq.h>
@@ -24,7 +23,7 @@ ctrlchar_handle_sysrq(void *tty)
handle_sysrq(ctrlchar_sysrq_key, NULL, (struct tty_struct *) tty);
}
-static DECLARE_WORK(ctrlchar_work, ctrlchar_handle_sysrq, 0);
+static DECLARE_WORK(ctrlchar_work, ctrlchar_handle_sysrq, NULL);
#endif
diff --git a/drivers/s390/char/defkeymap.c b/drivers/s390/char/defkeymap.c
index ca15adb140d1..17027d918cf7 100644
--- a/drivers/s390/char/defkeymap.c
+++ b/drivers/s390/char/defkeymap.c
@@ -83,8 +83,8 @@ static u_short shift_ctrl_map[NR_KEYS] = {
};
ushort *key_maps[MAX_NR_KEYMAPS] = {
- plain_map, shift_map, 0, 0,
- ctrl_map, shift_ctrl_map, 0
+ plain_map, shift_map, NULL, NULL,
+ ctrl_map, shift_ctrl_map, NULL,
};
unsigned int keymap_count = 4;
@@ -145,7 +145,7 @@ char *func_table[MAX_NR_FUNC] = {
func_buf + 97,
func_buf + 103,
func_buf + 109,
- 0,
+ NULL,
};
struct kbdiacr accent_table[MAX_DIACR] = {
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c
index a6415377bc73..b4557fa30858 100644
--- a/drivers/s390/char/fs3270.c
+++ b/drivers/s390/char/fs3270.c
@@ -8,7 +8,6 @@
* -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
*/
-#include <linux/config.h>
#include <linux/bootmem.h>
#include <linux/console.h>
#include <linux/init.h>
@@ -18,7 +17,6 @@
#include <asm/ccwdev.h>
#include <asm/cio.h>
-#include <asm/cpcmd.h>
#include <asm/ebcdic.h>
#include <asm/idals.h>
@@ -237,7 +235,7 @@ fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb)
* Process reads from fullscreen 3270.
*/
static ssize_t
-fs3270_read(struct file *filp, char *data, size_t count, loff_t *off)
+fs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off)
{
struct fs3270 *fp;
struct raw3270_request *rq;
@@ -282,7 +280,7 @@ fs3270_read(struct file *filp, char *data, size_t count, loff_t *off)
* Process writes to fullscreen 3270.
*/
static ssize_t
-fs3270_write(struct file *filp, const char *data, size_t count, loff_t *off)
+fs3270_write(struct file *filp, const char __user *data, size_t count, loff_t *off)
{
struct fs3270 *fp;
struct raw3270_request *rq;
@@ -339,10 +337,10 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
fp->write_command = arg;
break;
case TUBGETI:
- rc = put_user(fp->read_command, (char *) arg);
+ rc = put_user(fp->read_command, (char __user *) arg);
break;
case TUBGETO:
- rc = put_user(fp->write_command,(char *) arg);
+ rc = put_user(fp->write_command,(char __user *) arg);
break;
case TUBGETMOD:
iocb.model = fp->view.model;
@@ -351,7 +349,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
iocb.pf_cnt = 24;
iocb.re_cnt = 20;
iocb.map = 0;
- if (copy_to_user((char *) arg, &iocb,
+ if (copy_to_user((char __user *) arg, &iocb,
sizeof(struct raw3270_iocb)))
rc = -EFAULT;
break;
@@ -480,7 +478,7 @@ fs3270_close(struct inode *inode, struct file *filp)
struct fs3270 *fp;
fp = filp->private_data;
- filp->private_data = 0;
+ filp->private_data = NULL;
if (fp) {
fp->fs_pid = 0;
raw3270_reset(&fp->view);
diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c
index d4d2ff0a9da2..3be06569180d 100644
--- a/drivers/s390/char/keyboard.c
+++ b/drivers/s390/char/keyboard.c
@@ -7,7 +7,6 @@
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/sysrq.h>
@@ -104,7 +103,7 @@ out_maps:
out_kbd:
kfree(kbd);
out:
- return 0;
+ return NULL;
}
void
@@ -305,7 +304,7 @@ kbd_keycode(struct kbd_data *kbd, unsigned int keycode)
if (kbd->sysrq) {
if (kbd->sysrq == K(KT_LATIN, '-')) {
kbd->sysrq = 0;
- handle_sysrq(value, 0, kbd->tty);
+ handle_sysrq(value, NULL, kbd->tty);
return;
}
if (value == '-') {
@@ -364,7 +363,7 @@ do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe,
/* disallocate map */
key_map = kbd->key_maps[tmp.kb_table];
if (key_map) {
- kbd->key_maps[tmp.kb_table] = 0;
+ kbd->key_maps[tmp.kb_table] = NULL;
kfree(key_map);
}
break;
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index fb7bc9e5eebc..a138b1510093 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -586,7 +586,6 @@ static struct file_operations mon_fops = {
static struct miscdevice mon_dev = {
.name = "monreader",
- .devfs_name = "monreader",
.fops = &mon_fops,
.minor = MISC_DYNAMIC_MINOR,
};
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
new file mode 100644
index 000000000000..1e3939aeb8ab
--- /dev/null
+++ b/drivers/s390/char/monwriter.c
@@ -0,0 +1,292 @@
+/*
+ * drivers/s390/char/monwriter.c
+ *
+ * Character device driver for writing z/VM *MONITOR service records.
+ *
+ * Copyright (C) IBM Corp. 2006
+ *
+ * Author(s): Melissa Howland <Melissa.Howland@us.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/ctype.h>
+#include <linux/poll.h>
+#include <asm/uaccess.h>
+#include <asm/ebcdic.h>
+#include <asm/io.h>
+#include <asm/appldata.h>
+#include <asm/monwriter.h>
+
+#define MONWRITE_MAX_DATALEN 4024
+
+static int mon_max_bufs = 255;
+
+struct mon_buf {
+ struct list_head list;
+ struct monwrite_hdr hdr;
+ int diag_done;
+ char *data;
+};
+
+struct mon_private {
+ struct list_head list;
+ struct monwrite_hdr hdr;
+ size_t hdr_to_read;
+ size_t data_to_read;
+ struct mon_buf *current_buf;
+ int mon_buf_count;
+};
+
+/*
+ * helper functions
+ */
+
+static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn)
+{
+ struct appldata_product_id id;
+ int rc;
+
+ strcpy(id.prod_nr, "LNXAPPL");
+ id.prod_fn = myhdr->applid;
+ id.record_nr = myhdr->record_num;
+ id.version_nr = myhdr->version;
+ id.release_nr = myhdr->release;
+ id.mod_lvl = myhdr->mod_level;
+ rc = appldata_asm(&id, fcn, (void *) buffer, myhdr->datalen);
+ if (rc <= 0)
+ return rc;
+ if (rc == 5)
+ return -EPERM;
+ printk("DIAG X'DC' error with return code: %i\n", rc);
+ return -EINVAL;
+}
+
+static inline struct mon_buf *monwrite_find_hdr(struct mon_private *monpriv,
+ struct monwrite_hdr *monhdr)
+{
+ struct mon_buf *entry, *next;
+
+ list_for_each_entry_safe(entry, next, &monpriv->list, list)
+ if (entry->hdr.applid == monhdr->applid &&
+ entry->hdr.record_num == monhdr->record_num &&
+ entry->hdr.version == monhdr->version &&
+ entry->hdr.release == monhdr->release &&
+ entry->hdr.mod_level == monhdr->mod_level)
+ return entry;
+ return NULL;
+}
+
+static int monwrite_new_hdr(struct mon_private *monpriv)
+{
+ struct monwrite_hdr *monhdr = &monpriv->hdr;
+ struct mon_buf *monbuf;
+ int rc;
+
+ if (monhdr->datalen > MONWRITE_MAX_DATALEN ||
+ monhdr->mon_function > MONWRITE_START_CONFIG ||
+ monhdr->hdrlen != sizeof(struct monwrite_hdr))
+ return -EINVAL;
+ monbuf = monwrite_find_hdr(monpriv, monhdr);
+ if (monbuf) {
+ if (monhdr->mon_function == MONWRITE_STOP_INTERVAL) {
+ monhdr->datalen = monbuf->hdr.datalen;
+ rc = monwrite_diag(monhdr, monbuf->data,
+ APPLDATA_STOP_REC);
+ list_del(&monbuf->list);
+ monpriv->mon_buf_count--;
+ kfree(monbuf->data);
+ kfree(monbuf);
+ monbuf = NULL;
+ }
+ } else {
+ if (monpriv->mon_buf_count >= mon_max_bufs)
+ return -ENOSPC;
+ monbuf = kzalloc(sizeof(struct mon_buf), GFP_KERNEL);
+ if (!monbuf)
+ return -ENOMEM;
+ monbuf->data = kzalloc(monbuf->hdr.datalen,
+ GFP_KERNEL | GFP_DMA);
+ if (!monbuf->data) {
+ kfree(monbuf);
+ return -ENOMEM;
+ }
+ monbuf->hdr = *monhdr;
+ list_add_tail(&monbuf->list, &monpriv->list);
+ monpriv->mon_buf_count++;
+ }
+ monpriv->current_buf = monbuf;
+ return 0;
+}
+
+static int monwrite_new_data(struct mon_private *monpriv)
+{
+ struct monwrite_hdr *monhdr = &monpriv->hdr;
+ struct mon_buf *monbuf = monpriv->current_buf;
+ int rc = 0;
+
+ switch (monhdr->mon_function) {
+ case MONWRITE_START_INTERVAL:
+ if (!monbuf->diag_done) {
+ rc = monwrite_diag(monhdr, monbuf->data,
+ APPLDATA_START_INTERVAL_REC);
+ monbuf->diag_done = 1;
+ }
+ break;
+ case MONWRITE_START_CONFIG:
+ if (!monbuf->diag_done) {
+ rc = monwrite_diag(monhdr, monbuf->data,
+ APPLDATA_START_CONFIG_REC);
+ monbuf->diag_done = 1;
+ }
+ break;
+ case MONWRITE_GEN_EVENT:
+ rc = monwrite_diag(monhdr, monbuf->data,
+ APPLDATA_GEN_EVENT_REC);
+ list_del(&monpriv->current_buf->list);
+ kfree(monpriv->current_buf->data);
+ kfree(monpriv->current_buf);
+ monpriv->current_buf = NULL;
+ break;
+ default:
+ /* monhdr->mon_function is checked in monwrite_new_hdr */
+ BUG();
+ }
+ return rc;
+}
+
+/*
+ * file operations
+ */
+
+static int monwrite_open(struct inode *inode, struct file *filp)
+{
+ struct mon_private *monpriv;
+
+ monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
+ if (!monpriv)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&monpriv->list);
+ monpriv->hdr_to_read = sizeof(monpriv->hdr);
+ filp->private_data = monpriv;
+ return nonseekable_open(inode, filp);
+}
+
+static int monwrite_close(struct inode *inode, struct file *filp)
+{
+ struct mon_private *monpriv = filp->private_data;
+ struct mon_buf *entry, *next;
+
+ list_for_each_entry_safe(entry, next, &monpriv->list, list) {
+ if (entry->hdr.mon_function != MONWRITE_GEN_EVENT)
+ monwrite_diag(&entry->hdr, entry->data,
+ APPLDATA_STOP_REC);
+ monpriv->mon_buf_count--;
+ list_del(&entry->list);
+ kfree(entry->data);
+ kfree(entry);
+ }
+ kfree(monpriv);
+ return 0;
+}
+
+static ssize_t monwrite_write(struct file *filp, const char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct mon_private *monpriv = filp->private_data;
+ size_t len, written;
+ void *to;
+ int rc;
+
+ for (written = 0; written < count; ) {
+ if (monpriv->hdr_to_read) {
+ len = min(count - written, monpriv->hdr_to_read);
+ to = (char *) &monpriv->hdr +
+ sizeof(monpriv->hdr) - monpriv->hdr_to_read;
+ if (copy_from_user(to, data + written, len)) {
+ rc = -EFAULT;
+ goto out_error;
+ }
+ monpriv->hdr_to_read -= len;
+ written += len;
+ if (monpriv->hdr_to_read > 0)
+ continue;
+ rc = monwrite_new_hdr(monpriv);
+ if (rc)
+ goto out_error;
+ monpriv->data_to_read = monpriv->current_buf ?
+ monpriv->current_buf->hdr.datalen : 0;
+ }
+
+ if (monpriv->data_to_read) {
+ len = min(count - written, monpriv->data_to_read);
+ to = monpriv->current_buf->data +
+ monpriv->hdr.datalen - monpriv->data_to_read;
+ if (copy_from_user(to, data + written, len)) {
+ rc = -EFAULT;
+ goto out_error;
+ }
+ monpriv->data_to_read -= len;
+ written += len;
+ if (monpriv->data_to_read > 0)
+ continue;
+ rc = monwrite_new_data(monpriv);
+ if (rc)
+ goto out_error;
+ }
+ monpriv->hdr_to_read = sizeof(monpriv->hdr);
+ }
+ return written;
+
+out_error:
+ monpriv->data_to_read = 0;
+ monpriv->hdr_to_read = sizeof(struct monwrite_hdr);
+ return rc;
+}
+
+static struct file_operations monwrite_fops = {
+ .owner = THIS_MODULE,
+ .open = &monwrite_open,
+ .release = &monwrite_close,
+ .write = &monwrite_write,
+};
+
+static struct miscdevice mon_dev = {
+ .name = "monwriter",
+ .fops = &monwrite_fops,
+ .minor = MISC_DYNAMIC_MINOR,
+};
+
+/*
+ * module init/exit
+ */
+
+static int __init mon_init(void)
+{
+ if (MACHINE_IS_VM)
+ return misc_register(&mon_dev);
+ else
+ return -ENODEV;
+}
+
+static void __exit mon_exit(void)
+{
+ WARN_ON(misc_deregister(&mon_dev) != 0);
+}
+
+module_init(mon_init);
+module_exit(mon_exit);
+
+module_param_named(max_bufs, mon_max_bufs, int, 0644);
+MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers"
+ "that can be active at one time");
+
+MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>");
+MODULE_DESCRIPTION("Character device driver for writing z/VM "
+ "APPLDATA monitor records.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c
index eecb2afad5c2..7a84014f2037 100644
--- a/drivers/s390/char/raw3270.c
+++ b/drivers/s390/char/raw3270.c
@@ -8,7 +8,6 @@
* -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
*/
-#include <linux/config.h>
#include <linux/bootmem.h>
#include <linux/module.h>
#include <linux/err.h>
@@ -50,6 +49,9 @@ struct raw3270 {
unsigned char *ascebc; /* ascii -> ebcdic table */
struct class_device *clttydev; /* 3270-class tty device ptr */
struct class_device *cltubdev; /* 3270-class tub device ptr */
+
+ struct raw3270_request init_request;
+ unsigned char init_data[256];
};
/* raw3270->flags */
@@ -484,8 +486,6 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */
} __attribute__ ((packed)) aua;
} __attribute__ ((packed));
-static unsigned char raw3270_init_data[256];
-static struct raw3270_request raw3270_init_request;
static struct diag210 raw3270_init_diag210;
static DECLARE_MUTEX(raw3270_init_sem);
@@ -555,7 +555,7 @@ raw3270_start_init(struct raw3270 *rp, struct raw3270_view *view,
#ifdef CONFIG_TN3270_CONSOLE
if (raw3270_registered == 0) {
spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags);
- rq->callback = 0;
+ rq->callback = NULL;
rc = __raw3270_start(rp, view, rq);
if (rc == 0)
while (!raw3270_request_final(rq)) {
@@ -644,17 +644,17 @@ __raw3270_size_device(struct raw3270 *rp)
* required (3270 device switched to 'stand-by') and command
* rejects (old devices that can't do 'read partition').
*/
- memset(&raw3270_init_request, 0, sizeof(raw3270_init_request));
- memset(raw3270_init_data, 0, sizeof(raw3270_init_data));
- /* Store 'read partition' data stream to raw3270_init_data */
- memcpy(raw3270_init_data, wbuf, sizeof(wbuf));
- INIT_LIST_HEAD(&raw3270_init_request.list);
- raw3270_init_request.ccw.cmd_code = TC_WRITESF;
- raw3270_init_request.ccw.flags = CCW_FLAG_SLI;
- raw3270_init_request.ccw.count = sizeof(wbuf);
- raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data);
-
- rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request);
+ memset(&rp->init_request, 0, sizeof(rp->init_request));
+ memset(&rp->init_data, 0, 256);
+ /* Store 'read partition' data stream to init_data */
+ memcpy(&rp->init_data, wbuf, sizeof(wbuf));
+ INIT_LIST_HEAD(&rp->init_request.list);
+ rp->init_request.ccw.cmd_code = TC_WRITESF;
+ rp->init_request.ccw.flags = CCW_FLAG_SLI;
+ rp->init_request.ccw.count = sizeof(wbuf);
+ rp->init_request.ccw.cda = (__u32) __pa(&rp->init_data);
+
+ rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request);
if (rc)
/* Check error cases: -ERESTARTSYS, -EIO and -EOPNOTSUPP */
return rc;
@@ -679,18 +679,18 @@ __raw3270_size_device(struct raw3270 *rp)
* The device accepted the 'read partition' command. Now
* set up a read ccw and issue it.
*/
- raw3270_init_request.ccw.cmd_code = TC_READMOD;
- raw3270_init_request.ccw.flags = CCW_FLAG_SLI;
- raw3270_init_request.ccw.count = sizeof(raw3270_init_data);
- raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data);
- rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request);
+ rp->init_request.ccw.cmd_code = TC_READMOD;
+ rp->init_request.ccw.flags = CCW_FLAG_SLI;
+ rp->init_request.ccw.count = sizeof(rp->init_data);
+ rp->init_request.ccw.cda = (__u32) __pa(rp->init_data);
+ rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request);
if (rc)
return rc;
/* Got a Query Reply */
- count = sizeof(raw3270_init_data) - raw3270_init_request.rescnt;
- uap = (struct raw3270_ua *) (raw3270_init_data + 1);
+ count = sizeof(rp->init_data) - rp->init_request.rescnt;
+ uap = (struct raw3270_ua *) (rp->init_data + 1);
/* Paranoia check. */
- if (raw3270_init_data[0] != 0x88 || uap->uab.qcode != 0x81)
+ if (rp->init_data[0] != 0x88 || uap->uab.qcode != 0x81)
return -EOPNOTSUPP;
/* Copy rows/columns of default Usable Area */
rp->rows = uap->uab.h;
@@ -719,8 +719,8 @@ raw3270_size_device(struct raw3270 *rp)
rc = __raw3270_size_device_vm(rp);
else
rc = __raw3270_size_device(rp);
- raw3270_init_view.dev = 0;
- rp->view = 0;
+ raw3270_init_view.dev = NULL;
+ rp->view = NULL;
up(&raw3270_init_sem);
if (rc == 0) { /* Found something. */
/* Try to find a model. */
@@ -749,20 +749,20 @@ raw3270_reset_device(struct raw3270 *rp)
int rc;
down(&raw3270_init_sem);
- memset(&raw3270_init_request, 0, sizeof(raw3270_init_request));
- memset(raw3270_init_data, 0, sizeof(raw3270_init_data));
- /* Store reset data stream to raw3270_init_data/raw3270_init_request */
- raw3270_init_data[0] = TW_KR;
- INIT_LIST_HEAD(&raw3270_init_request.list);
- raw3270_init_request.ccw.cmd_code = TC_EWRITEA;
- raw3270_init_request.ccw.flags = CCW_FLAG_SLI;
- raw3270_init_request.ccw.count = 1;
- raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data);
+ memset(&rp->init_request, 0, sizeof(rp->init_request));
+ memset(&rp->init_data, 0, sizeof(rp->init_data));
+ /* Store reset data stream to init_data/init_request */
+ rp->init_data[0] = TW_KR;
+ INIT_LIST_HEAD(&rp->init_request.list);
+ rp->init_request.ccw.cmd_code = TC_EWRITEA;
+ rp->init_request.ccw.flags = CCW_FLAG_SLI;
+ rp->init_request.ccw.count = 1;
+ rp->init_request.ccw.cda = (__u32) __pa(rp->init_data);
rp->view = &raw3270_init_view;
raw3270_init_view.dev = rp;
- rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request);
- raw3270_init_view.dev = 0;
- rp->view = 0;
+ rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request);
+ raw3270_init_view.dev = NULL;
+ rp->view = NULL;
up(&raw3270_init_sem);
return rc;
}
@@ -854,7 +854,7 @@ raw3270_setup_console(struct ccw_device *cdev)
char *ascebc;
int rc;
- rp = (struct raw3270 *) alloc_bootmem(sizeof(struct raw3270));
+ rp = (struct raw3270 *) alloc_bootmem_low(sizeof(struct raw3270));
ascebc = (char *) alloc_bootmem(256);
rc = raw3270_setup_device(cdev, rp, ascebc);
if (rc)
@@ -895,7 +895,7 @@ raw3270_create_device(struct ccw_device *cdev)
char *ascebc;
int rc;
- rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL);
+ rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA);
if (!rp)
return ERR_PTR(-ENOMEM);
ascebc = kmalloc(256, GFP_KERNEL);
@@ -934,7 +934,7 @@ raw3270_activate_view(struct raw3270_view *view)
else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags))
rc = -ENODEV;
else {
- oldview = 0;
+ oldview = NULL;
if (rp->view) {
oldview = rp->view;
oldview->fn->deactivate(oldview);
@@ -951,7 +951,7 @@ raw3270_activate_view(struct raw3270_view *view)
rp->view = nv;
if (nv->fn->activate(nv) == 0)
break;
- rp->view = 0;
+ rp->view = NULL;
}
}
}
@@ -975,7 +975,7 @@ raw3270_deactivate_view(struct raw3270_view *view)
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
if (rp->view == view) {
view->fn->deactivate(view);
- rp->view = 0;
+ rp->view = NULL;
/* Move deactivated view to end of list. */
list_del_init(&view->list);
list_add_tail(&view->list, &rp->view_list);
@@ -985,7 +985,7 @@ raw3270_deactivate_view(struct raw3270_view *view)
rp->view = view;
if (view->fn->activate(view) == 0)
break;
- rp->view = 0;
+ rp->view = NULL;
}
}
}
@@ -1076,7 +1076,7 @@ raw3270_del_view(struct raw3270_view *view)
spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
if (rp->view == view) {
view->fn->deactivate(view);
- rp->view = 0;
+ rp->view = NULL;
}
list_del_init(&view->list);
if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags)) {
@@ -1106,10 +1106,10 @@ raw3270_delete_device(struct raw3270 *rp)
/* Remove from device chain. */
mutex_lock(&raw3270_mutex);
- if (rp->clttydev)
+ if (rp->clttydev && !IS_ERR(rp->clttydev))
class_device_destroy(class3270,
MKDEV(IBM_TTY3270_MAJOR, rp->minor));
- if (rp->cltubdev)
+ if (rp->cltubdev && !IS_ERR(rp->cltubdev))
class_device_destroy(class3270,
MKDEV(IBM_FS3270_MAJOR, rp->minor));
list_del_init(&rp->list);
@@ -1117,9 +1117,9 @@ raw3270_delete_device(struct raw3270 *rp)
/* Disconnect from ccw_device. */
cdev = rp->cdev;
- rp->cdev = 0;
- cdev->dev.driver_data = 0;
- cdev->handler = 0;
+ rp->cdev = NULL;
+ cdev->dev.driver_data = NULL;
+ cdev->handler = NULL;
/* Put ccw_device structure. */
put_device(&cdev->dev);
@@ -1144,7 +1144,7 @@ raw3270_model_show(struct device *dev, struct device_attribute *attr, char *buf)
return snprintf(buf, PAGE_SIZE, "%i\n",
((struct raw3270 *) dev->driver_data)->model);
}
-static DEVICE_ATTR(model, 0444, raw3270_model_show, 0);
+static DEVICE_ATTR(model, 0444, raw3270_model_show, NULL);
static ssize_t
raw3270_rows_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -1152,7 +1152,7 @@ raw3270_rows_show(struct device *dev, struct device_attribute *attr, char *buf)
return snprintf(buf, PAGE_SIZE, "%i\n",
((struct raw3270 *) dev->driver_data)->rows);
}
-static DEVICE_ATTR(rows, 0444, raw3270_rows_show, 0);
+static DEVICE_ATTR(rows, 0444, raw3270_rows_show, NULL);
static ssize_t
raw3270_columns_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -1160,7 +1160,7 @@ raw3270_columns_show(struct device *dev, struct device_attribute *attr, char *bu
return snprintf(buf, PAGE_SIZE, "%i\n",
((struct raw3270 *) dev->driver_data)->cols);
}
-static DEVICE_ATTR(columns, 0444, raw3270_columns_show, 0);
+static DEVICE_ATTR(columns, 0444, raw3270_columns_show, NULL);
static struct attribute * raw3270_attrs[] = {
&dev_attr_model.attr,
@@ -1173,21 +1173,37 @@ static struct attribute_group raw3270_attr_group = {
.attrs = raw3270_attrs,
};
-static void
-raw3270_create_attributes(struct raw3270 *rp)
+static int raw3270_create_attributes(struct raw3270 *rp)
{
- //FIXME: check return code
- sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group);
- rp->clttydev =
- class_device_create(class3270, NULL,
- MKDEV(IBM_TTY3270_MAJOR, rp->minor),
- &rp->cdev->dev, "tty%s",
- rp->cdev->dev.bus_id);
- rp->cltubdev =
- class_device_create(class3270, NULL,
- MKDEV(IBM_FS3270_MAJOR, rp->minor),
- &rp->cdev->dev, "tub%s",
- rp->cdev->dev.bus_id);
+ int rc;
+
+ rc = sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group);
+ if (rc)
+ goto out;
+
+ rp->clttydev = class_device_create(class3270, NULL,
+ MKDEV(IBM_TTY3270_MAJOR, rp->minor),
+ &rp->cdev->dev, "tty%s",
+ rp->cdev->dev.bus_id);
+ if (IS_ERR(rp->clttydev)) {
+ rc = PTR_ERR(rp->clttydev);
+ goto out_ttydev;
+ }
+
+ rp->cltubdev = class_device_create(class3270, NULL,
+ MKDEV(IBM_FS3270_MAJOR, rp->minor),
+ &rp->cdev->dev, "tub%s",
+ rp->cdev->dev.bus_id);
+ if (!IS_ERR(rp->cltubdev))
+ goto out;
+
+ rc = PTR_ERR(rp->cltubdev);
+ class_device_destroy(class3270, MKDEV(IBM_TTY3270_MAJOR, rp->minor));
+
+out_ttydev:
+ sysfs_remove_group(&rp->cdev->dev.kobj, &raw3270_attr_group);
+out:
+ return rc;
}
/*
@@ -1255,7 +1271,9 @@ raw3270_set_online (struct ccw_device *cdev)
rc = raw3270_reset_device(rp);
if (rc)
goto failure;
- raw3270_create_attributes(rp);
+ rc = raw3270_create_attributes(rp);
+ if (rc)
+ goto failure;
set_bit(RAW3270_FLAGS_READY, &rp->flags);
mutex_lock(&raw3270_mutex);
list_for_each_entry(np, &raw3270_notifier, list)
@@ -1296,7 +1314,7 @@ raw3270_remove (struct ccw_device *cdev)
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
if (rp->view) {
rp->view->fn->deactivate(rp->view);
- rp->view = 0;
+ rp->view = NULL;
}
while (!list_empty(&rp->view_list)) {
v = list_entry(rp->view_list.next, struct raw3270_view, list);
diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h
index b635bf8e7775..90beaa80a782 100644
--- a/drivers/s390/char/raw3270.h
+++ b/drivers/s390/char/raw3270.h
@@ -231,7 +231,7 @@ alloc_string(struct list_head *free_list, unsigned long len)
INIT_LIST_HEAD(&cs->update);
return cs;
}
- return 0;
+ return NULL;
}
static inline unsigned long
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 4138564402b8..31e335751d6d 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -100,13 +100,12 @@ service_call(sclp_cmdw_t command, void *sccb)
{
int cc;
- __asm__ __volatile__(
- " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */
- " ipm %0\n"
- " srl %0,28"
- : "=&d" (cc)
- : "d" (command), "a" (__pa(sccb))
- : "cc", "memory" );
+ asm volatile(
+ " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */
+ " ipm %0\n"
+ " srl %0,28"
+ : "=&d" (cc) : "d" (command), "a" (__pa(sccb))
+ : "cc", "memory");
if (cc == 3)
return -EIO;
if (cc == 2)
@@ -360,16 +359,6 @@ sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
sclp_process_queue();
}
-/* Return current Time-Of-Day clock. */
-static inline u64
-sclp_get_clock(void)
-{
- u64 result;
-
- asm volatile ("STCK 0(%1)" : "=m" (result) : "a" (&(result)) : "cc");
- return result;
-}
-
/* Convert interval in jiffies to TOD ticks. */
static inline u64
sclp_tod_from_jiffies(unsigned long jiffies)
@@ -382,7 +371,7 @@ sclp_tod_from_jiffies(unsigned long jiffies)
void
sclp_sync_wait(void)
{
- unsigned long psw_mask;
+ unsigned long flags;
unsigned long cr0, cr0_sync;
u64 timeout;
@@ -391,35 +380,35 @@ sclp_sync_wait(void)
timeout = 0;
if (timer_pending(&sclp_request_timer)) {
/* Get timeout TOD value */
- timeout = sclp_get_clock() +
+ timeout = get_clock() +
sclp_tod_from_jiffies(sclp_request_timer.expires -
jiffies);
}
+ local_irq_save(flags);
/* Prevent bottom half from executing once we force interrupts open */
local_bh_disable();
/* Enable service-signal interruption, disable timer interrupts */
+ trace_hardirqs_on();
__ctl_store(cr0, 0, 0);
cr0_sync = cr0;
cr0_sync |= 0x00000200;
cr0_sync &= 0xFFFFF3AC;
__ctl_load(cr0_sync, 0, 0);
- asm volatile ("STOSM 0(%1),0x01"
- : "=m" (psw_mask) : "a" (&psw_mask) : "memory");
+ __raw_local_irq_stosm(0x01);
/* Loop until driver state indicates finished request */
while (sclp_running_state != sclp_running_state_idle) {
/* Check for expired request timer */
if (timer_pending(&sclp_request_timer) &&
- sclp_get_clock() > timeout &&
+ get_clock() > timeout &&
del_timer(&sclp_request_timer))
sclp_request_timer.function(sclp_request_timer.data);
barrier();
cpu_relax();
}
- /* Restore interrupt settings */
- asm volatile ("SSM 0(%0)"
- : : "a" (&psw_mask) : "memory");
+ local_irq_disable();
__ctl_load(cr0, 0, 0);
- __local_bh_enable();
+ _local_bh_enable();
+ local_irq_restore(flags);
}
EXPORT_SYMBOL(sclp_sync_wait);
diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c
index 10ef22f13541..86864f641716 100644
--- a/drivers/s390/char/sclp_con.c
+++ b/drivers/s390/char/sclp_con.c
@@ -8,7 +8,6 @@
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
-#include <linux/config.h>
#include <linux/kmod.h>
#include <linux/console.h>
#include <linux/init.h>
diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c
index 80f7f31310e6..732dfbdb85c4 100644
--- a/drivers/s390/char/sclp_cpi.c
+++ b/drivers/s390/char/sclp_cpi.c
@@ -5,7 +5,6 @@
* SCLP Control-Program Identification.
*/
-#include <linux/config.h>
#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/module.h>
diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c
index 56fa69168898..32004aae95c1 100644
--- a/drivers/s390/char/sclp_quiesce.c
+++ b/drivers/s390/char/sclp_quiesce.c
@@ -7,12 +7,12 @@
* Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/cpumask.h>
#include <linux/smp.h>
#include <linux/init.h>
+#include <linux/reboot.h>
#include <asm/atomic.h>
#include <asm/ptrace.h>
#include <asm/sigp.h>
@@ -66,8 +66,6 @@ do_machine_quiesce(void)
}
#endif
-extern void ctrl_alt_del(void);
-
/* Handler for quiesce event. Start shutdown procedure. */
static void
sclp_quiesce_handler(struct evbuf_header *evbuf)
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index 91e93c78f57a..0c92d3909cca 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -8,7 +8,6 @@
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
-#include <linux/config.h>
#include <linux/kmod.h>
#include <linux/types.h>
#include <linux/err.h>
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index 6cbf067f1a8f..f6cf9023039e 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -8,7 +8,6 @@
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/tty.h>
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index 9e02625c82cf..54fba6f17188 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -7,7 +7,6 @@
* Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/list.h>
diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h
index cd51ace8b610..1f4c89967be4 100644
--- a/drivers/s390/char/tape.h
+++ b/drivers/s390/char/tape.h
@@ -16,7 +16,6 @@
#include <asm/ccwdev.h>
#include <asm/debug.h>
#include <asm/idals.h>
-#include <linux/config.h>
#include <linux/blkdev.h>
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c
index d4f2da738078..7b95dab913d0 100644
--- a/drivers/s390/char/tape_34xx.c
+++ b/drivers/s390/char/tape_34xx.c
@@ -8,7 +8,6 @@
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/bio.h>
@@ -1310,9 +1309,9 @@ static struct tape_discipline tape_discipline_34xx = {
};
static struct ccw_device_id tape_34xx_ids[] = {
- { CCW_DEVICE_DEVTYPE(0x3480, 0, 0x3480, 0), driver_info: tape_3480},
- { CCW_DEVICE_DEVTYPE(0x3490, 0, 0x3490, 0), driver_info: tape_3490},
- { /* end of list */ }
+ { CCW_DEVICE_DEVTYPE(0x3480, 0, 0x3480, 0), .driver_info = tape_3480},
+ { CCW_DEVICE_DEVTYPE(0x3490, 0, 0x3490, 0), .driver_info = tape_3490},
+ { /* end of list */ },
};
static int
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index d71ef1adea59..928cbefc49d5 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -8,7 +8,6 @@
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/bio.h>
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
index b70d92690242..3225fcd1dcb4 100644
--- a/drivers/s390/char/tape_block.c
+++ b/drivers/s390/char/tape_block.c
@@ -11,7 +11,6 @@
*/
#include <linux/fs.h>
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c
index 5ce7ca38ace0..97f75237bed6 100644
--- a/drivers/s390/char/tape_char.c
+++ b/drivers/s390/char/tape_char.c
@@ -10,7 +10,6 @@
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c
index a5c68e60fcf4..56b87618b100 100644
--- a/drivers/s390/char/tape_class.c
+++ b/drivers/s390/char/tape_class.c
@@ -76,14 +76,22 @@ struct tape_class_device *register_tape_dev(
device,
"%s", tcd->device_name
);
- sysfs_create_link(
+ rc = IS_ERR(tcd->class_device) ? PTR_ERR(tcd->class_device) : 0;
+ if (rc)
+ goto fail_with_cdev;
+ rc = sysfs_create_link(
&device->kobj,
&tcd->class_device->kobj,
tcd->mode_name
);
+ if (rc)
+ goto fail_with_class_device;
return tcd;
+fail_with_class_device:
+ class_device_destroy(tape_class, tcd->char_device->dev);
+
fail_with_cdev:
cdev_del(tcd->char_device);
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index e6e4086d3224..2826aed91043 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -11,7 +11,6 @@
* Stefan Bader <shbader@de.ibm.com>
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h> // for kernel parameters
#include <linux/kmod.h> // for requesting modules
@@ -544,20 +543,24 @@ int
tape_generic_probe(struct ccw_device *cdev)
{
struct tape_device *device;
+ int ret;
device = tape_alloc_device();
if (IS_ERR(device))
return -ENODEV;
- PRINT_INFO("tape device %s found\n", cdev->dev.bus_id);
+ ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
+ ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group);
+ if (ret) {
+ tape_put_device(device);
+ PRINT_ERR("probe failed for tape device %s\n", cdev->dev.bus_id);
+ return ret;
+ }
cdev->dev.driver_data = device;
+ cdev->handler = __tape_do_irq;
device->cdev = cdev;
device->cdev_id = busid_to_int(cdev->dev.bus_id);
- cdev->handler = __tape_do_irq;
-
- ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
- sysfs_create_group(&cdev->dev.kobj, &tape_attr_group);
-
- return 0;
+ PRINT_INFO("tape device %s found\n", cdev->dev.bus_id);
+ return ret;
}
static inline void
diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c
index 5fec0a10cc3d..655d375ab22b 100644
--- a/drivers/s390/char/tape_proc.c
+++ b/drivers/s390/char/tape_proc.c
@@ -11,7 +11,6 @@
* PROCFS Functions
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/seq_file.h>
diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c
index 99cf881f41db..7a76ec413a3a 100644
--- a/drivers/s390/char/tape_std.c
+++ b/drivers/s390/char/tape_std.c
@@ -11,7 +11,6 @@
* Stefan Bader <shbader@de.ibm.com>
*/
-#include <linux/config.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/bio.h>
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c
index 9a141776873f..06e2eeec8473 100644
--- a/drivers/s390/char/tty3270.c
+++ b/drivers/s390/char/tty3270.c
@@ -8,7 +8,6 @@
* -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
@@ -438,7 +437,7 @@ tty3270_rcl_add(struct tty3270 *tp, char *input, int len)
{
struct string *s;
- tp->rcl_walk = 0;
+ tp->rcl_walk = NULL;
if (len <= 0)
return;
if (tp->rcl_nr >= tp->rcl_max) {
@@ -467,12 +466,12 @@ tty3270_rcl_backward(struct kbd_data *kbd)
else if (!list_empty(&tp->rcl_lines))
tp->rcl_walk = tp->rcl_lines.prev;
s = tp->rcl_walk ?
- list_entry(tp->rcl_walk, struct string, list) : 0;
+ list_entry(tp->rcl_walk, struct string, list) : NULL;
if (tp->rcl_walk) {
s = list_entry(tp->rcl_walk, struct string, list);
tty3270_update_prompt(tp, s->string, s->len);
} else
- tty3270_update_prompt(tp, 0, 0);
+ tty3270_update_prompt(tp, NULL, 0);
tty3270_set_timer(tp, 1);
}
spin_unlock_bh(&tp->view.lock);
@@ -554,7 +553,7 @@ tty3270_read_tasklet(struct raw3270_request *rrq)
* has to be emitted to the tty and for 0x6d the screen
* needs to be redrawn.
*/
- input = 0;
+ input = NULL;
len = 0;
if (tp->input->string[0] == 0x7d) {
/* Enter: write input to tty. */
@@ -568,7 +567,7 @@ tty3270_read_tasklet(struct raw3270_request *rrq)
tty3270_update_status(tp);
}
/* Clear input area. */
- tty3270_update_prompt(tp, 0, 0);
+ tty3270_update_prompt(tp, NULL, 0);
tty3270_set_timer(tp, 1);
} else if (tp->input->string[0] == 0x6d) {
/* Display has been cleared. Redraw. */
@@ -699,7 +698,6 @@ tty3270_alloc_view(void)
if (!tp->freemem_pages)
goto out_tp;
INIT_LIST_HEAD(&tp->freemem);
- init_timer(&tp->timer);
for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) {
tp->freemem_pages[pages] = (void *)
__get_free_pages(GFP_KERNEL|GFP_DMA, 0);
@@ -809,8 +807,8 @@ tty3270_release(struct raw3270_view *view)
tp = (struct tty3270 *) view;
tty = tp->tty;
if (tty) {
- tty->driver_data = 0;
- tp->tty = tp->kbd->tty = 0;
+ tty->driver_data = NULL;
+ tp->tty = tp->kbd->tty = NULL;
tty_hangup(tty);
raw3270_put_view(&tp->view);
}
@@ -949,8 +947,8 @@ tty3270_close(struct tty_struct *tty, struct file * filp)
return;
tp = (struct tty3270 *) tty->driver_data;
if (tp) {
- tty->driver_data = 0;
- tp->tty = tp->kbd->tty = 0;
+ tty->driver_data = NULL;
+ tp->tty = tp->kbd->tty = NULL;
raw3270_put_view(&tp->view);
}
}
@@ -1674,7 +1672,7 @@ tty3270_set_termios(struct tty_struct *tty, struct termios *old)
new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN;
if (new != tp->inattr) {
tp->inattr = new;
- tty3270_update_prompt(tp, 0, 0);
+ tty3270_update_prompt(tp, NULL, 0);
tty3270_set_timer(tp, 1);
}
}
@@ -1760,7 +1758,7 @@ void
tty3270_notifier(int index, int active)
{
if (active)
- tty_register_device(tty3270_driver, index, 0);
+ tty_register_device(tty3270_driver, index, NULL);
else
tty_unregister_device(tty3270_driver, index);
}
@@ -1785,7 +1783,6 @@ tty3270_init(void)
* proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
*/
driver->owner = THIS_MODULE;
- driver->devfs_name = "ttyTUB/";
driver->driver_name = "ttyTUB";
driver->name = "ttyTUB";
driver->major = IBM_TTY3270_MAJOR;
@@ -1793,7 +1790,7 @@ tty3270_init(void)
driver->type = TTY_DRIVER_TYPE_SYSTEM;
driver->subtype = SYSTEM_TYPE_TTY;
driver->init_termios = tty_std_termios;
- driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_NO_DEVFS;
+ driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_DYNAMIC_DEV;
tty_set_operations(driver, &tty3270_ops);
ret = tty_register_driver(driver);
if (ret) {
@@ -1820,7 +1817,7 @@ tty3270_exit(void)
raw3270_unregister_notifier(tty3270_notifier);
driver = tty3270_driver;
- tty3270_driver = 0;
+ tty3270_driver = NULL;
tty_unregister_driver(driver);
tty3270_del_views();
}
diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c
index 19762f3476aa..1678b6c757ec 100644
--- a/drivers/s390/char/vmcp.c
+++ b/drivers/s390/char/vmcp.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2004,2005 IBM Corporation
- * Interface implementation for communication with the v/VM control program
+ * Interface implementation for communication with the z/VM control program
* Author(s): Christian Borntraeger <cborntra@de.ibm.com>
*
*
diff --git a/drivers/s390/char/vmcp.h b/drivers/s390/char/vmcp.h
index 87389e730465..8a5975f3dad7 100644
--- a/drivers/s390/char/vmcp.h
+++ b/drivers/s390/char/vmcp.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2004, 2005 IBM Corporation
- * Interface implementation for communication with the v/VM control program
+ * Interface implementation for communication with the z/VM control program
* Version 1.0
* Author(s): Christian Borntraeger <cborntra@de.ibm.com>
*
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index c625b69ebd19..6cb23040954b 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -86,8 +86,8 @@ struct vmlogrdr_priv_t {
*/
static int vmlogrdr_open(struct inode *, struct file *);
static int vmlogrdr_release(struct inode *, struct file *);
-static ssize_t vmlogrdr_read (struct file *filp, char *data, size_t count,
- loff_t * ppos);
+static ssize_t vmlogrdr_read (struct file *filp, char __user *data,
+ size_t count, loff_t * ppos);
static struct file_operations vmlogrdr_fops = {
.owner = THIS_MODULE,
@@ -515,7 +515,7 @@ vmlogrdr_receive_data(struct vmlogrdr_priv_t *priv) {
static ssize_t
-vmlogrdr_read (struct file *filp, char *data, size_t count, loff_t * ppos)
+vmlogrdr_read(struct file *filp, char __user *data, size_t count, loff_t * ppos)
{
int rc;
struct vmlogrdr_priv_t * priv = filp->private_data;
diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c
index 5acc0ace3d7d..4b868f72fe89 100644
--- a/drivers/s390/char/vmwatchdog.c
+++ b/drivers/s390/char/vmwatchdog.c
@@ -54,48 +54,20 @@ enum vmwdt_func {
static int __diag288(enum vmwdt_func func, unsigned int timeout,
char *cmd, size_t len)
{
- register unsigned long __func asm("2");
- register unsigned long __timeout asm("3");
- register unsigned long __cmdp asm("4");
- register unsigned long __cmdl asm("5");
+ register unsigned long __func asm("2") = func;
+ register unsigned long __timeout asm("3") = timeout;
+ register unsigned long __cmdp asm("4") = virt_to_phys(cmd);
+ register unsigned long __cmdl asm("5") = len;
int err;
- __func = func;
- __timeout = timeout;
- __cmdp = virt_to_phys(cmd);
- __cmdl = len;
- err = 0;
- asm volatile (
-#ifdef CONFIG_64BIT
- "diag %2,%4,0x288\n"
- "1: \n"
- ".section .fixup,\"ax\"\n"
- "2: lghi %0,%1\n"
- " jg 1b\n"
- ".previous\n"
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 1b,2b\n"
- ".previous\n"
-#else
- "diag %2,%4,0x288\n"
- "1: \n"
- ".section .fixup,\"ax\"\n"
- "2: lhi %0,%1\n"
- " bras 1,3f\n"
- " .long 1b\n"
- "3: l 1,0(1)\n"
- " br 1\n"
- ".previous\n"
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 1b,2b\n"
- ".previous\n"
-#endif
- : "+&d"(err)
- : "i"(-EINVAL), "d"(__func), "d"(__timeout),
- "d"(__cmdp), "d"(__cmdl)
- : "1", "cc");
+ err = -EINVAL;
+ asm volatile(
+ " diag %1,%3,0x288\n"
+ "0: la %0,0\n"
+ "1:\n"
+ EX_TABLE(0b,1b)
+ : "=d" (err) : "d"(__func), "d"(__timeout),
+ "d"(__cmdp), "d"(__cmdl), "0" (-EINVAL) : "1", "cc");
return err;
}
@@ -193,7 +165,7 @@ static int vmwdt_ioctl(struct inode *i, struct file *f,
return 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
- return put_user(0, (int *)arg);
+ return put_user(0, (int __user *)arg);
case WDIOC_GETTEMP:
return -EINVAL;
case WDIOC_SETOPTIONS:
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 0960bef7b199..12c2d6b746e6 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -9,7 +9,6 @@
* Arnd Bergmann (arndb@de.ibm.com)
*/
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
@@ -224,39 +223,6 @@ is_blacklisted (int ssid, int devno)
}
#ifdef CONFIG_PROC_FS
-static int
-__s390_redo_validation(struct subchannel_id schid, void *data)
-{
- int ret;
- struct subchannel *sch;
-
- sch = get_subchannel_by_schid(schid);
- if (sch) {
- /* Already known. */
- put_device(&sch->dev);
- return 0;
- }
- ret = css_probe_device(schid);
- if (ret == -ENXIO)
- return ret; /* We're through. */
- if (ret == -ENOMEM)
- /* Stop validation for now. Bad, but no need for a panic. */
- return ret;
- return 0;
-}
-
-/*
- * Function: s390_redo_validation
- * Look for no longer blacklisted devices
- * FIXME: there must be a better way to do this */
-static inline void
-s390_redo_validation (void)
-{
- CIO_TRACE_EVENT (0, "redoval");
-
- for_each_subchannel(__s390_redo_validation, NULL);
-}
-
/*
* Function: blacklist_parse_proc_parameters
* parse the stuff which is piped to /proc/cio_ignore
@@ -281,7 +247,7 @@ blacklist_parse_proc_parameters (char *buf)
return;
}
- s390_redo_validation ();
+ css_schedule_reprobe();
}
/* Iterator struct for all devices. */
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index bdfee7fbaa2e..38954f5cd14c 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -152,7 +152,6 @@ ccwgroup_create(struct device *root,
struct ccwgroup_device *gdev;
int i;
int rc;
- int del_drvdata;
if (argc > 256) /* disallow dumb users */
return -EINVAL;
@@ -163,7 +162,6 @@ ccwgroup_create(struct device *root,
atomic_set(&gdev->onoff, 0);
- del_drvdata = 0;
for (i = 0; i < argc; i++) {
gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]);
@@ -180,18 +178,14 @@ ccwgroup_create(struct device *root,
rc = -EINVAL;
goto free_dev;
}
- }
- for (i = 0; i < argc; i++)
gdev->cdev[i]->dev.driver_data = gdev;
- del_drvdata = 1;
+ }
gdev->creator_id = creator_id;
gdev->count = argc;
- gdev->dev = (struct device ) {
- .bus = &ccwgroup_bus_type,
- .parent = root,
- .release = ccwgroup_release,
- };
+ gdev->dev.bus = &ccwgroup_bus_type;
+ gdev->dev.parent = root;
+ gdev->dev.release = ccwgroup_release;
snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s",
gdev->cdev[0]->dev.bus_id);
@@ -226,9 +220,9 @@ error:
free_dev:
for (i = 0; i < argc; i++)
if (gdev->cdev[i]) {
- put_device(&gdev->cdev[i]->dev);
- if (del_drvdata)
+ if (gdev->cdev[i]->dev.driver_data == gdev)
gdev->cdev[i]->dev.driver_data = NULL;
+ put_device(&gdev->cdev[i]->dev);
}
kfree(gdev);
return rc;
@@ -319,7 +313,7 @@ ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const
if (!try_module_get(gdrv->owner))
return -EINVAL;
- value = simple_strtoul(buf, 0, 0);
+ value = simple_strtoul(buf, NULL, 0);
ret = count;
if (value == 1)
ccwgroup_set_online(gdev);
@@ -395,30 +389,31 @@ int
ccwgroup_driver_register (struct ccwgroup_driver *cdriver)
{
/* register our new driver with the core */
- cdriver->driver = (struct device_driver) {
- .bus = &ccwgroup_bus_type,
- .name = cdriver->name,
- };
+ cdriver->driver.bus = &ccwgroup_bus_type;
+ cdriver->driver.name = cdriver->name;
return driver_register(&cdriver->driver);
}
static int
-__ccwgroup_driver_unregister_device(struct device *dev, void *data)
+__ccwgroup_match_all(struct device *dev, void *data)
{
- __ccwgroup_remove_symlinks(to_ccwgroupdev(dev));
- device_unregister(dev);
- put_device(dev);
- return 0;
+ return 1;
}
void
ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver)
{
+ struct device *dev;
+
/* We don't want ccwgroup devices to live longer than their driver. */
get_driver(&cdriver->driver);
- driver_for_each_device(&cdriver->driver, NULL, NULL,
- __ccwgroup_driver_unregister_device);
+ while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
+ __ccwgroup_match_all))) {
+ __ccwgroup_remove_symlinks(to_ccwgroupdev(dev));
+ device_unregister(dev);
+ put_device(dev);
+ }
put_driver(&cdriver->driver);
driver_unregister(&cdriver->driver);
}
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 72187e54dcac..3bb4e472d73d 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -10,7 +10,6 @@
*/
#include <linux/module.h>
-#include <linux/config.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>
@@ -239,13 +238,10 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
/* Check for single path devices. */
if (sch->schib.pmcw.pim == 0x80)
goto out_unreg;
- if (sch->vpm == mask)
- goto out_unreg;
if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) &&
(sch->schib.scsw.actl & SCSW_ACTL_SCHACT) &&
- (sch->schib.pmcw.lpum == mask) &&
- (sch->vpm == 0)) {
+ (sch->schib.pmcw.lpum == mask)) {
int cc;
cc = cio_clear(sch);
@@ -260,6 +256,8 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
/* trigger path verification. */
if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
+ else if (sch->lpm == mask)
+ goto out_unreg;
out_unlock:
spin_unlock_irq(&sch->lock);
return 0;
@@ -380,6 +378,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
if (chp_mask == 0) {
spin_unlock_irq(&sch->lock);
+ put_device(&sch->dev);
return 0;
}
old_lpm = sch->lpm;
@@ -394,7 +393,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
spin_unlock_irq(&sch->lock);
put_device(&sch->dev);
- return (res_data->fla_mask == 0xffff) ? -ENODEV : 0;
+ return 0;
}
@@ -918,12 +917,13 @@ chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
chp = to_channelpath(container_of(kobj, struct device, kobj));
css = to_css(chp->dev.parent);
- size = sizeof(struct cmg_chars);
+ size = sizeof(struct cmg_entry);
/* Only allow single reads. */
if (off || count < size)
return 0;
chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id);
+ count = size;
return count;
}
@@ -1392,10 +1392,8 @@ new_channel_path(int chpid)
/* fill in status, etc. */
chp->id = chpid;
chp->state = 1;
- chp->dev = (struct device) {
- .parent = &css[0]->device,
- .release = chp_release,
- };
+ chp->dev.parent = &css[0]->device;
+ chp->dev.release = chp_release;
snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid);
/* Obtain channel path description and fill it in. */
@@ -1465,6 +1463,40 @@ chsc_get_chp_desc(struct subchannel *sch, int chp_no)
return desc;
}
+static int reset_channel_path(struct channel_path *chp)
+{
+ int cc;
+
+ cc = rchp(chp->id);
+ switch (cc) {
+ case 0:
+ return 0;
+ case 2:
+ return -EBUSY;
+ default:
+ return -ENODEV;
+ }
+}
+
+static void reset_channel_paths_css(struct channel_subsystem *css)
+{
+ int i;
+
+ for (i = 0; i <= __MAX_CHPID; i++) {
+ if (css->chps[i])
+ reset_channel_path(css->chps[i]);
+ }
+}
+
+void cio_reset_channel_paths(void)
+{
+ int i;
+
+ for (i = 0; i <= __MAX_CSSID; i++) {
+ if (css[i] && css[i]->valid)
+ reset_channel_paths_css(css[i]);
+ }
+}
static int __init
chsc_alloc_sei_area(void)
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 5b20d8c9c025..2e2882daefbb 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -11,17 +11,15 @@
*/
#include <linux/module.h>
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/kernel_stat.h>
#include <linux/interrupt.h>
-
#include <asm/cio.h>
#include <asm/delay.h>
#include <asm/irq.h>
-
+#include <asm/setup.h>
#include "airq.h"
#include "cio.h"
#include "css.h"
@@ -148,7 +146,7 @@ cio_tpi(void)
sch->driver->irq(&sch->dev);
spin_unlock(&sch->lock);
irq_exit ();
- __local_bh_enable();
+ _local_bh_enable();
return 1;
}
@@ -193,7 +191,7 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
sch->orb.pfch = sch->options.prefetch == 0;
sch->orb.spnd = sch->options.suspend;
sch->orb.ssic = sch->options.suspend && sch->options.inter;
- sch->orb.lpm = (lpm != 0) ? (lpm & sch->opm) : sch->lpm;
+ sch->orb.lpm = (lpm != 0) ? lpm : sch->lpm;
#ifdef CONFIG_64BIT
/*
* for 64 bit we always support 64 bit IDAWs with 4k page size only
@@ -520,6 +518,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
memset(sch, 0, sizeof(struct subchannel));
spin_lock_init(&sch->lock);
+ mutex_init(&sch->reg_mutex);
/* Set a name for the subchannel */
snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid,
@@ -570,10 +569,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
sch->opm = 0xff;
if (!cio_is_console(sch->schid))
chsc_validate_chpids(sch);
- sch->lpm = sch->schib.pmcw.pim &
- sch->schib.pmcw.pam &
- sch->schib.pmcw.pom &
- sch->opm;
+ sch->lpm = sch->schib.pmcw.pam & sch->opm;
CIO_DEBUG(KERN_INFO, 0,
"Detected device %04x on subchannel 0.%x.%04X"
@@ -798,7 +794,7 @@ struct subchannel *
cio_get_console_subchannel(void)
{
if (!console_subchannel_in_use)
- return 0;
+ return NULL;
return &console_subchannel;
}
@@ -841,14 +837,26 @@ __clear_subchannel_easy(struct subchannel_id schid)
return -EBUSY;
}
-extern void do_reipl(unsigned long devno);
-static int
-__shutdown_subchannel_easy(struct subchannel_id schid, void *data)
+struct sch_match_id {
+ struct subchannel_id schid;
+ struct ccw_dev_id devid;
+ int rc;
+};
+
+static int __shutdown_subchannel_easy_and_match(struct subchannel_id schid,
+ void *data)
{
struct schib schib;
+ struct sch_match_id *match_id = data;
if (stsch_err(schid, &schib))
return -ENXIO;
+ if (match_id && schib.pmcw.dnv &&
+ (schib.pmcw.dev == match_id->devid.devno) &&
+ (schid.ssid == match_id->devid.ssid)) {
+ match_id->schid = schid;
+ match_id->rc = 0;
+ }
if (!schib.pmcw.ena)
return 0;
switch(__disable_subchannel_easy(schid, &schib)) {
@@ -864,17 +872,71 @@ __shutdown_subchannel_easy(struct subchannel_id schid, void *data)
return 0;
}
-void
-clear_all_subchannels(void)
+static int clear_all_subchannels_and_match(struct ccw_dev_id *devid,
+ struct subchannel_id *schid)
{
+ struct sch_match_id match_id;
+
+ match_id.devid = *devid;
+ match_id.rc = -ENODEV;
local_irq_disable();
- for_each_subchannel(__shutdown_subchannel_easy, NULL);
+ for_each_subchannel(__shutdown_subchannel_easy_and_match, &match_id);
+ if (match_id.rc == 0)
+ *schid = match_id.schid;
+ return match_id.rc;
}
+
+void clear_all_subchannels(void)
+{
+ local_irq_disable();
+ for_each_subchannel(__shutdown_subchannel_easy_and_match, NULL);
+}
+
+extern void do_reipl_asm(__u32 schid);
+
/* Make sure all subchannels are quiet before we re-ipl an lpar. */
-void
-reipl(unsigned long devno)
+void reipl_ccw_dev(struct ccw_dev_id *devid)
{
- clear_all_subchannels();
- do_reipl(devno);
+ struct subchannel_id schid;
+
+ if (clear_all_subchannels_and_match(devid, &schid))
+ panic("IPL Device not found\n");
+ cio_reset_channel_paths();
+ do_reipl_asm(*((__u32*)&schid));
+}
+
+extern struct schib ipl_schib;
+
+/*
+ * ipl_save_parameters gets called very early. It is not allowed to access
+ * anything in the bss section at all. The bss section is not cleared yet,
+ * but may contain some ipl parameters written by the firmware.
+ * These parameters (if present) are copied to 0x2000.
+ * To avoid corruption of the ipl parameters, all variables used by this
+ * function must reside on the stack or in the data section.
+ */
+void ipl_save_parameters(void)
+{
+ struct subchannel_id schid;
+ unsigned int *ipl_ptr;
+ void *src, *dst;
+
+ schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID;
+ if (!schid.one)
+ return;
+ if (stsch(schid, &ipl_schib))
+ return;
+ if (!ipl_schib.pmcw.dnv)
+ return;
+ ipl_devno = ipl_schib.pmcw.dev;
+ ipl_flags |= IPL_DEVNO_VALID;
+ if (!ipl_schib.pmcw.qf)
+ return;
+ ipl_flags |= IPL_PARMBLOCK_VALID;
+ ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR;
+ src = (void *)(unsigned long)*ipl_ptr;
+ dst = (void *)IPL_PARMBLOCK_ORIGIN;
+ memmove(dst, src, PAGE_SIZE);
+ *ipl_ptr = IPL_PARMBLOCK_ORIGIN;
}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 0ca987344e07..4541c1af4b66 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -2,6 +2,7 @@
#define S390_CIO_H
#include "schid.h"
+#include <linux/mutex.h>
/*
* where we put the ssd info
@@ -87,7 +88,7 @@ struct orb {
struct subchannel {
struct subchannel_id schid;
spinlock_t lock; /* subchannel lock */
-
+ struct mutex reg_mutex;
enum {
SUBCHANNEL_TYPE_IO = 0,
SUBCHANNEL_TYPE_CHSC = 1,
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 07ef3f640f4a..828b2d334f0a 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -3,9 +3,10 @@
*
* Linux on zSeries Channel Measurement Facility support
*
- * Copyright 2000,2003 IBM Corporation
+ * Copyright 2000,2006 IBM Corporation
*
- * Author: Arnd Bergmann <arndb@de.ibm.com>
+ * Authors: Arnd Bergmann <arndb@de.ibm.com>
+ * Cornelia Huck <cornelia.huck@de.ibm.com>
*
* original idea from Natarajan Krishnaswami <nkrishna@us.ibm.com>
*
@@ -96,9 +97,9 @@ module_param(format, bool, 0444);
/**
* struct cmb_operations - functions to use depending on cmb_format
*
- * all these functions operate on a struct cmf_device. There is only
- * one instance of struct cmb_operations because all cmf_device
- * objects are guaranteed to be of the same type.
+ * Most of these functions operate on a struct ccw_device. There is only
+ * one instance of struct cmb_operations because the format of the measurement
+ * data is guaranteed to be the same for every ccw_device.
*
* @alloc: allocate memory for a channel measurement block,
* either with the help of a special pool or with kmalloc
@@ -107,6 +108,7 @@ module_param(format, bool, 0444);
* @readall: read a measurement block in a common format
* @reset: clear the data in the associated measurement block and
* reset its time stamp
+ * @align: align an allocated block so that the hardware can use it
*/
struct cmb_operations {
int (*alloc) (struct ccw_device*);
@@ -115,11 +117,19 @@ struct cmb_operations {
u64 (*read) (struct ccw_device*, int);
int (*readall)(struct ccw_device*, struct cmbdata *);
void (*reset) (struct ccw_device*);
+ void * (*align) (void *);
struct attribute_group *attr_group;
};
static struct cmb_operations *cmbops;
+struct cmb_data {
+ void *hw_block; /* Pointer to block updated by hardware */
+ void *last_block; /* Last changed block copied from hardware block */
+ int size; /* Size of hw_block and last_block */
+ unsigned long long last_update; /* when last_block was updated */
+};
+
/* our user interface is designed in terms of nanoseconds,
* while the hardware measures total times in its own
* unit.*/
@@ -226,63 +236,229 @@ struct set_schib_struct {
unsigned long address;
wait_queue_head_t wait;
int ret;
+ struct kref kref;
};
+static void cmf_set_schib_release(struct kref *kref)
+{
+ struct set_schib_struct *set_data;
+
+ set_data = container_of(kref, struct set_schib_struct, kref);
+ kfree(set_data);
+}
+
+#define CMF_PENDING 1
+
static int set_schib_wait(struct ccw_device *cdev, u32 mme,
int mbfc, unsigned long address)
{
- struct set_schib_struct s = {
- .mme = mme,
- .mbfc = mbfc,
- .address = address,
- .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s.wait),
- };
+ struct set_schib_struct *set_data;
+ int ret;
spin_lock_irq(cdev->ccwlock);
- s.ret = set_schib(cdev, mme, mbfc, address);
- if (s.ret != -EBUSY) {
- goto out_nowait;
+ if (!cdev->private->cmb) {
+ ret = -ENODEV;
+ goto out;
}
+ set_data = kzalloc(sizeof(struct set_schib_struct), GFP_ATOMIC);
+ if (!set_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ init_waitqueue_head(&set_data->wait);
+ kref_init(&set_data->kref);
+ set_data->mme = mme;
+ set_data->mbfc = mbfc;
+ set_data->address = address;
+
+ ret = set_schib(cdev, mme, mbfc, address);
+ if (ret != -EBUSY)
+ goto out_put;
if (cdev->private->state != DEV_STATE_ONLINE) {
- s.ret = -EBUSY;
/* if the device is not online, don't even try again */
- goto out_nowait;
+ ret = -EBUSY;
+ goto out_put;
}
+
cdev->private->state = DEV_STATE_CMFCHANGE;
- cdev->private->cmb_wait = &s;
- s.ret = 1;
+ set_data->ret = CMF_PENDING;
+ cdev->private->cmb_wait = set_data;
spin_unlock_irq(cdev->ccwlock);
- if (wait_event_interruptible(s.wait, s.ret != 1)) {
+ if (wait_event_interruptible(set_data->wait,
+ set_data->ret != CMF_PENDING)) {
spin_lock_irq(cdev->ccwlock);
- if (s.ret == 1) {
- s.ret = -ERESTARTSYS;
- cdev->private->cmb_wait = 0;
+ if (set_data->ret == CMF_PENDING) {
+ set_data->ret = -ERESTARTSYS;
if (cdev->private->state == DEV_STATE_CMFCHANGE)
cdev->private->state = DEV_STATE_ONLINE;
}
spin_unlock_irq(cdev->ccwlock);
}
- return s.ret;
-
-out_nowait:
+ spin_lock_irq(cdev->ccwlock);
+ cdev->private->cmb_wait = NULL;
+ ret = set_data->ret;
+out_put:
+ kref_put(&set_data->kref, cmf_set_schib_release);
+out:
spin_unlock_irq(cdev->ccwlock);
- return s.ret;
+ return ret;
}
void retry_set_schib(struct ccw_device *cdev)
{
- struct set_schib_struct *s;
+ struct set_schib_struct *set_data;
+
+ set_data = cdev->private->cmb_wait;
+ if (!set_data) {
+ WARN_ON(1);
+ return;
+ }
+ kref_get(&set_data->kref);
+ set_data->ret = set_schib(cdev, set_data->mme, set_data->mbfc,
+ set_data->address);
+ wake_up(&set_data->wait);
+ kref_put(&set_data->kref, cmf_set_schib_release);
+}
+
+static int cmf_copy_block(struct ccw_device *cdev)
+{
+ struct subchannel *sch;
+ void *reference_buf;
+ void *hw_block;
+ struct cmb_data *cmb_data;
+
+ sch = to_subchannel(cdev->dev.parent);
+
+ if (stsch(sch->schid, &sch->schib))
+ return -ENODEV;
+
+ if (sch->schib.scsw.fctl & 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 &
+ (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) &&
+ (!sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS))
+ return -EBUSY;
+ }
+ cmb_data = cdev->private->cmb;
+ hw_block = cmbops->align(cmb_data->hw_block);
+ if (!memcmp(cmb_data->last_block, hw_block, cmb_data->size))
+ /* No need to copy. */
+ return 0;
+ reference_buf = kzalloc(cmb_data->size, GFP_ATOMIC);
+ if (!reference_buf)
+ return -ENOMEM;
+ /* Ensure consistency of block copied from hardware. */
+ do {
+ memcpy(cmb_data->last_block, hw_block, cmb_data->size);
+ memcpy(reference_buf, hw_block, cmb_data->size);
+ } while (memcmp(cmb_data->last_block, reference_buf, cmb_data->size));
+ cmb_data->last_update = get_clock();
+ kfree(reference_buf);
+ return 0;
+}
+
+struct copy_block_struct {
+ wait_queue_head_t wait;
+ int ret;
+ struct kref kref;
+};
+
+static void cmf_copy_block_release(struct kref *kref)
+{
+ struct copy_block_struct *copy_block;
+
+ copy_block = container_of(kref, struct copy_block_struct, kref);
+ kfree(copy_block);
+}
+
+static int cmf_cmb_copy_wait(struct ccw_device *cdev)
+{
+ struct copy_block_struct *copy_block;
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ if (!cdev->private->cmb) {
+ ret = -ENODEV;
+ goto out;
+ }
+ copy_block = kzalloc(sizeof(struct copy_block_struct), GFP_ATOMIC);
+ if (!copy_block) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ init_waitqueue_head(&copy_block->wait);
+ kref_init(&copy_block->kref);
+
+ ret = cmf_copy_block(cdev);
+ if (ret != -EBUSY)
+ goto out_put;
+
+ if (cdev->private->state != DEV_STATE_ONLINE) {
+ ret = -EBUSY;
+ goto out_put;
+ }
+
+ cdev->private->state = DEV_STATE_CMFUPDATE;
+ copy_block->ret = CMF_PENDING;
+ cdev->private->cmb_wait = copy_block;
+
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ if (wait_event_interruptible(copy_block->wait,
+ copy_block->ret != CMF_PENDING)) {
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ if (copy_block->ret == CMF_PENDING) {
+ copy_block->ret = -ERESTARTSYS;
+ if (cdev->private->state == DEV_STATE_CMFUPDATE)
+ cdev->private->state = DEV_STATE_ONLINE;
+ }
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ }
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ cdev->private->cmb_wait = NULL;
+ ret = copy_block->ret;
+out_put:
+ kref_put(&copy_block->kref, cmf_copy_block_release);
+out:
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ return ret;
+}
+
+void cmf_retry_copy_block(struct ccw_device *cdev)
+{
+ struct copy_block_struct *copy_block;
- s = cdev->private->cmb_wait;
- cdev->private->cmb_wait = 0;
- if (!s) {
+ copy_block = cdev->private->cmb_wait;
+ if (!copy_block) {
WARN_ON(1);
return;
}
- s->ret = set_schib(cdev, s->mme, s->mbfc, s->address);
- wake_up(&s->wait);
+ kref_get(&copy_block->kref);
+ copy_block->ret = cmf_copy_block(cdev);
+ wake_up(&copy_block->wait);
+ kref_put(&copy_block->kref, cmf_copy_block_release);
+}
+
+static void cmf_generic_reset(struct ccw_device *cdev)
+{
+ struct cmb_data *cmb_data;
+
+ spin_lock_irq(cdev->ccwlock);
+ cmb_data = cdev->private->cmb;
+ if (cmb_data) {
+ memset(cmb_data->last_block, 0, cmb_data->size);
+ /*
+ * Need to reset hw block as well to make the hardware start
+ * from 0 again.
+ */
+ memset(cmbops->align(cmb_data->hw_block), 0, cmb_data->size);
+ cmb_data->last_update = 0;
+ }
+ cdev->private->cmb_start_time = get_clock();
+ spin_unlock_irq(cdev->ccwlock);
}
/**
@@ -343,8 +519,8 @@ struct cmb {
/* insert a single device into the cmb_area list
* called with cmb_area.lock held from alloc_cmb
*/
-static inline int
-alloc_cmb_single (struct ccw_device *cdev)
+static inline int alloc_cmb_single (struct ccw_device *cdev,
+ struct cmb_data *cmb_data)
{
struct cmb *cmb;
struct ccw_device_private *node;
@@ -358,10 +534,12 @@ alloc_cmb_single (struct ccw_device *cdev)
/* find first unused cmb in cmb_area.mem.
* this is a little tricky: cmb_area.list
- * remains sorted by ->cmb pointers */
+ * remains sorted by ->cmb->hw_data pointers */
cmb = cmb_area.mem;
list_for_each_entry(node, &cmb_area.list, cmb_list) {
- if ((struct cmb*)node->cmb > cmb)
+ struct cmb_data *data;
+ data = node->cmb;
+ if ((struct cmb*)data->hw_block > cmb)
break;
cmb++;
}
@@ -372,7 +550,8 @@ alloc_cmb_single (struct ccw_device *cdev)
/* insert new cmb */
list_add_tail(&cdev->private->cmb_list, &node->cmb_list);
- cdev->private->cmb = cmb;
+ cmb_data->hw_block = cmb;
+ cdev->private->cmb = cmb_data;
ret = 0;
out:
spin_unlock_irq(cdev->ccwlock);
@@ -385,7 +564,19 @@ alloc_cmb (struct ccw_device *cdev)
int ret;
struct cmb *mem;
ssize_t size;
+ struct cmb_data *cmb_data;
+
+ /* Allocate private cmb_data. */
+ cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL);
+ if (!cmb_data)
+ return -ENOMEM;
+ cmb_data->last_block = kzalloc(sizeof(struct cmb), GFP_KERNEL);
+ if (!cmb_data->last_block) {
+ kfree(cmb_data);
+ return -ENOMEM;
+ }
+ cmb_data->size = sizeof(struct cmb);
spin_lock(&cmb_area.lock);
if (!cmb_area.mem) {
@@ -414,29 +605,36 @@ alloc_cmb (struct ccw_device *cdev)
}
/* do the actual allocation */
- ret = alloc_cmb_single(cdev);
+ ret = alloc_cmb_single(cdev, cmb_data);
out:
spin_unlock(&cmb_area.lock);
-
+ if (ret) {
+ kfree(cmb_data->last_block);
+ kfree(cmb_data);
+ }
return ret;
}
-static void
-free_cmb(struct ccw_device *cdev)
+static void free_cmb(struct ccw_device *cdev)
{
struct ccw_device_private *priv;
-
- priv = cdev->private;
+ struct cmb_data *cmb_data;
spin_lock(&cmb_area.lock);
spin_lock_irq(cdev->ccwlock);
+ priv = cdev->private;
+
if (list_empty(&priv->cmb_list)) {
/* already freed */
goto out;
}
+ cmb_data = priv->cmb;
priv->cmb = NULL;
+ if (cmb_data)
+ kfree(cmb_data->last_block);
+ kfree(cmb_data);
list_del_init(&priv->cmb_list);
if (list_empty(&cmb_area.list)) {
@@ -451,83 +649,97 @@ out:
spin_unlock(&cmb_area.lock);
}
-static int
-set_cmb(struct ccw_device *cdev, u32 mme)
+static int set_cmb(struct ccw_device *cdev, u32 mme)
{
u16 offset;
+ struct cmb_data *cmb_data;
+ unsigned long flags;
- if (!cdev->private->cmb)
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ if (!cdev->private->cmb) {
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
return -EINVAL;
-
- offset = mme ? (struct cmb *)cdev->private->cmb - cmb_area.mem : 0;
+ }
+ cmb_data = cdev->private->cmb;
+ offset = mme ? (struct cmb *)cmb_data->hw_block - cmb_area.mem : 0;
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
return set_schib_wait(cdev, mme, 0, offset);
}
-static u64
-read_cmb (struct ccw_device *cdev, int index)
+static u64 read_cmb (struct ccw_device *cdev, int index)
{
- /* yes, we have to put it on the stack
- * because the cmb must only be accessed
- * atomically, e.g. with mvc */
- struct cmb cmb;
- unsigned long flags;
+ struct cmb *cmb;
u32 val;
+ int ret;
+ unsigned long flags;
+
+ ret = cmf_cmb_copy_wait(cdev);
+ if (ret < 0)
+ return 0;
spin_lock_irqsave(cdev->ccwlock, flags);
if (!cdev->private->cmb) {
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- return 0;
+ ret = 0;
+ goto out;
}
-
- cmb = *(struct cmb*)cdev->private->cmb;
- spin_unlock_irqrestore(cdev->ccwlock, flags);
+ cmb = ((struct cmb_data *)cdev->private->cmb)->last_block;
switch (index) {
case cmb_ssch_rsch_count:
- return cmb.ssch_rsch_count;
+ ret = cmb->ssch_rsch_count;
+ goto out;
case cmb_sample_count:
- return cmb.sample_count;
+ ret = cmb->sample_count;
+ goto out;
case cmb_device_connect_time:
- val = cmb.device_connect_time;
+ val = cmb->device_connect_time;
break;
case cmb_function_pending_time:
- val = cmb.function_pending_time;
+ val = cmb->function_pending_time;
break;
case cmb_device_disconnect_time:
- val = cmb.device_disconnect_time;
+ val = cmb->device_disconnect_time;
break;
case cmb_control_unit_queuing_time:
- val = cmb.control_unit_queuing_time;
+ val = cmb->control_unit_queuing_time;
break;
case cmb_device_active_only_time:
- val = cmb.device_active_only_time;
+ val = cmb->device_active_only_time;
break;
default:
- return 0;
+ ret = 0;
+ goto out;
}
- return time_to_avg_nsec(val, cmb.sample_count);
+ ret = time_to_avg_nsec(val, cmb->sample_count);
+out:
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ return ret;
}
-static int
-readall_cmb (struct ccw_device *cdev, struct cmbdata *data)
+static int readall_cmb (struct ccw_device *cdev, struct cmbdata *data)
{
- /* yes, we have to put it on the stack
- * because the cmb must only be accessed
- * atomically, e.g. with mvc */
- struct cmb cmb;
- unsigned long flags;
+ struct cmb *cmb;
+ struct cmb_data *cmb_data;
u64 time;
+ unsigned long flags;
+ int ret;
+ ret = cmf_cmb_copy_wait(cdev);
+ if (ret < 0)
+ return ret;
spin_lock_irqsave(cdev->ccwlock, flags);
- if (!cdev->private->cmb) {
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- return -ENODEV;
+ cmb_data = cdev->private->cmb;
+ if (!cmb_data) {
+ ret = -ENODEV;
+ goto out;
}
-
- cmb = *(struct cmb*)cdev->private->cmb;
- time = get_clock() - cdev->private->cmb_start_time;
- spin_unlock_irqrestore(cdev->ccwlock, flags);
+ if (cmb_data->last_update == 0) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ cmb = cmb_data->last_block;
+ time = cmb_data->last_update - cdev->private->cmb_start_time;
memset(data, 0, sizeof(struct cmbdata));
@@ -538,31 +750,32 @@ readall_cmb (struct ccw_device *cdev, struct cmbdata *data)
data->elapsed_time = (time * 1000) >> 12;
/* copy data to new structure */
- data->ssch_rsch_count = cmb.ssch_rsch_count;
- data->sample_count = cmb.sample_count;
+ data->ssch_rsch_count = cmb->ssch_rsch_count;
+ data->sample_count = cmb->sample_count;
/* time fields are converted to nanoseconds while copying */
- data->device_connect_time = time_to_nsec(cmb.device_connect_time);
- data->function_pending_time = time_to_nsec(cmb.function_pending_time);
- data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time);
+ data->device_connect_time = time_to_nsec(cmb->device_connect_time);
+ data->function_pending_time = time_to_nsec(cmb->function_pending_time);
+ data->device_disconnect_time =
+ time_to_nsec(cmb->device_disconnect_time);
data->control_unit_queuing_time
- = time_to_nsec(cmb.control_unit_queuing_time);
+ = time_to_nsec(cmb->control_unit_queuing_time);
data->device_active_only_time
- = time_to_nsec(cmb.device_active_only_time);
+ = time_to_nsec(cmb->device_active_only_time);
+ ret = 0;
+out:
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ return ret;
+}
- return 0;
+static void reset_cmb(struct ccw_device *cdev)
+{
+ cmf_generic_reset(cdev);
}
-static void
-reset_cmb(struct ccw_device *cdev)
+static void * align_cmb(void *area)
{
- struct cmb *cmb;
- spin_lock_irq(cdev->ccwlock);
- cmb = cdev->private->cmb;
- if (cmb)
- memset (cmb, 0, sizeof (*cmb));
- cdev->private->cmb_start_time = get_clock();
- spin_unlock_irq(cdev->ccwlock);
+ return area;
}
static struct attribute_group cmf_attr_group;
@@ -574,6 +787,7 @@ static struct cmb_operations cmbops_basic = {
.read = read_cmb,
.readall = readall_cmb,
.reset = reset_cmb,
+ .align = align_cmb,
.attr_group = &cmf_attr_group,
};
@@ -610,22 +824,34 @@ static inline struct cmbe* cmbe_align(struct cmbe *c)
return (struct cmbe*)addr;
}
-static int
-alloc_cmbe (struct ccw_device *cdev)
+static int alloc_cmbe (struct ccw_device *cdev)
{
struct cmbe *cmbe;
- cmbe = kmalloc (sizeof (*cmbe) * 2, GFP_KERNEL);
+ struct cmb_data *cmb_data;
+ int ret;
+
+ cmbe = kzalloc (sizeof (*cmbe) * 2, GFP_KERNEL);
if (!cmbe)
return -ENOMEM;
-
+ cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL);
+ if (!cmb_data) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL);
+ if (!cmb_data->last_block) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ cmb_data->size = sizeof(struct cmbe);
spin_lock_irq(cdev->ccwlock);
if (cdev->private->cmb) {
- kfree(cmbe);
spin_unlock_irq(cdev->ccwlock);
- return -EBUSY;
+ ret = -EBUSY;
+ goto out_free;
}
-
- cdev->private->cmb = cmbe;
+ cmb_data->hw_block = cmbe;
+ cdev->private->cmb = cmb_data;
spin_unlock_irq(cdev->ccwlock);
/* activate global measurement if this is the first channel */
@@ -636,14 +862,24 @@ alloc_cmbe (struct ccw_device *cdev)
spin_unlock(&cmb_area.lock);
return 0;
+out_free:
+ if (cmb_data)
+ kfree(cmb_data->last_block);
+ kfree(cmb_data);
+ kfree(cmbe);
+ return ret;
}
-static void
-free_cmbe (struct ccw_device *cdev)
+static void free_cmbe (struct ccw_device *cdev)
{
+ struct cmb_data *cmb_data;
+
spin_lock_irq(cdev->ccwlock);
- kfree(cdev->private->cmb);
+ cmb_data = cdev->private->cmb;
cdev->private->cmb = NULL;
+ if (cmb_data)
+ kfree(cmb_data->last_block);
+ kfree(cmb_data);
spin_unlock_irq(cdev->ccwlock);
/* deactivate global measurement if this is the last channel */
@@ -654,89 +890,105 @@ free_cmbe (struct ccw_device *cdev)
spin_unlock(&cmb_area.lock);
}
-static int
-set_cmbe(struct ccw_device *cdev, u32 mme)
+static int set_cmbe(struct ccw_device *cdev, u32 mme)
{
unsigned long mba;
+ struct cmb_data *cmb_data;
+ unsigned long flags;
- if (!cdev->private->cmb)
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ if (!cdev->private->cmb) {
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
return -EINVAL;
- mba = mme ? (unsigned long) cmbe_align(cdev->private->cmb) : 0;
+ }
+ cmb_data = cdev->private->cmb;
+ mba = mme ? (unsigned long) cmbe_align(cmb_data->hw_block) : 0;
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
return set_schib_wait(cdev, mme, 1, mba);
}
-u64
-read_cmbe (struct ccw_device *cdev, int index)
+static u64 read_cmbe (struct ccw_device *cdev, int index)
{
- /* yes, we have to put it on the stack
- * because the cmb must only be accessed
- * atomically, e.g. with mvc */
- struct cmbe cmb;
- unsigned long flags;
+ struct cmbe *cmb;
+ struct cmb_data *cmb_data;
u32 val;
+ int ret;
+ unsigned long flags;
- spin_lock_irqsave(cdev->ccwlock, flags);
- if (!cdev->private->cmb) {
- spin_unlock_irqrestore(cdev->ccwlock, flags);
+ ret = cmf_cmb_copy_wait(cdev);
+ if (ret < 0)
return 0;
- }
- cmb = *cmbe_align(cdev->private->cmb);
- spin_unlock_irqrestore(cdev->ccwlock, flags);
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ cmb_data = cdev->private->cmb;
+ if (!cmb_data) {
+ ret = 0;
+ goto out;
+ }
+ cmb = cmb_data->last_block;
switch (index) {
case cmb_ssch_rsch_count:
- return cmb.ssch_rsch_count;
+ ret = cmb->ssch_rsch_count;
+ goto out;
case cmb_sample_count:
- return cmb.sample_count;
+ ret = cmb->sample_count;
+ goto out;
case cmb_device_connect_time:
- val = cmb.device_connect_time;
+ val = cmb->device_connect_time;
break;
case cmb_function_pending_time:
- val = cmb.function_pending_time;
+ val = cmb->function_pending_time;
break;
case cmb_device_disconnect_time:
- val = cmb.device_disconnect_time;
+ val = cmb->device_disconnect_time;
break;
case cmb_control_unit_queuing_time:
- val = cmb.control_unit_queuing_time;
+ val = cmb->control_unit_queuing_time;
break;
case cmb_device_active_only_time:
- val = cmb.device_active_only_time;
+ val = cmb->device_active_only_time;
break;
case cmb_device_busy_time:
- val = cmb.device_busy_time;
+ val = cmb->device_busy_time;
break;
case cmb_initial_command_response_time:
- val = cmb.initial_command_response_time;
+ val = cmb->initial_command_response_time;
break;
default:
- return 0;
+ ret = 0;
+ goto out;
}
- return time_to_avg_nsec(val, cmb.sample_count);
+ ret = time_to_avg_nsec(val, cmb->sample_count);
+out:
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ return ret;
}
-static int
-readall_cmbe (struct ccw_device *cdev, struct cmbdata *data)
+static int readall_cmbe (struct ccw_device *cdev, struct cmbdata *data)
{
- /* yes, we have to put it on the stack
- * because the cmb must only be accessed
- * atomically, e.g. with mvc */
- struct cmbe cmb;
- unsigned long flags;
+ struct cmbe *cmb;
+ struct cmb_data *cmb_data;
u64 time;
+ unsigned long flags;
+ int ret;
+ ret = cmf_cmb_copy_wait(cdev);
+ if (ret < 0)
+ return ret;
spin_lock_irqsave(cdev->ccwlock, flags);
- if (!cdev->private->cmb) {
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- return -ENODEV;
+ cmb_data = cdev->private->cmb;
+ if (!cmb_data) {
+ ret = -ENODEV;
+ goto out;
}
-
- cmb = *cmbe_align(cdev->private->cmb);
- time = get_clock() - cdev->private->cmb_start_time;
- spin_unlock_irqrestore(cdev->ccwlock, flags);
+ if (cmb_data->last_update == 0) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ time = cmb_data->last_update - cdev->private->cmb_start_time;
memset (data, 0, sizeof(struct cmbdata));
@@ -746,35 +998,38 @@ readall_cmbe (struct ccw_device *cdev, struct cmbdata *data)
/* conver to nanoseconds */
data->elapsed_time = (time * 1000) >> 12;
+ cmb = cmb_data->last_block;
/* copy data to new structure */
- data->ssch_rsch_count = cmb.ssch_rsch_count;
- data->sample_count = cmb.sample_count;
+ data->ssch_rsch_count = cmb->ssch_rsch_count;
+ data->sample_count = cmb->sample_count;
/* time fields are converted to nanoseconds while copying */
- data->device_connect_time = time_to_nsec(cmb.device_connect_time);
- data->function_pending_time = time_to_nsec(cmb.function_pending_time);
- data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time);
+ data->device_connect_time = time_to_nsec(cmb->device_connect_time);
+ data->function_pending_time = time_to_nsec(cmb->function_pending_time);
+ data->device_disconnect_time =
+ time_to_nsec(cmb->device_disconnect_time);
data->control_unit_queuing_time
- = time_to_nsec(cmb.control_unit_queuing_time);
+ = time_to_nsec(cmb->control_unit_queuing_time);
data->device_active_only_time
- = time_to_nsec(cmb.device_active_only_time);
- data->device_busy_time = time_to_nsec(cmb.device_busy_time);
+ = time_to_nsec(cmb->device_active_only_time);
+ data->device_busy_time = time_to_nsec(cmb->device_busy_time);
data->initial_command_response_time
- = time_to_nsec(cmb.initial_command_response_time);
+ = time_to_nsec(cmb->initial_command_response_time);
- return 0;
+ ret = 0;
+out:
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ return ret;
}
-static void
-reset_cmbe(struct ccw_device *cdev)
+static void reset_cmbe(struct ccw_device *cdev)
{
- struct cmbe *cmb;
- spin_lock_irq(cdev->ccwlock);
- cmb = cmbe_align(cdev->private->cmb);
- if (cmb)
- memset (cmb, 0, sizeof (*cmb));
- cdev->private->cmb_start_time = get_clock();
- spin_unlock_irq(cdev->ccwlock);
+ cmf_generic_reset(cdev);
+}
+
+static void * align_cmbe(void *area)
+{
+ return cmbe_align(area);
}
static struct attribute_group cmf_attr_group_ext;
@@ -786,6 +1041,7 @@ static struct cmb_operations cmbops_extended = {
.read = read_cmbe,
.readall = readall_cmbe,
.reset = reset_cmbe,
+ .align = align_cmbe,
.attr_group = &cmf_attr_group_ext,
};
@@ -803,14 +1059,20 @@ cmb_show_avg_sample_interval(struct device *dev, struct device_attribute *attr,
struct ccw_device *cdev;
long interval;
unsigned long count;
+ struct cmb_data *cmb_data;
cdev = to_ccwdev(dev);
- interval = get_clock() - cdev->private->cmb_start_time;
count = cmf_read(cdev, cmb_sample_count);
- if (count)
+ spin_lock_irq(cdev->ccwlock);
+ cmb_data = cdev->private->cmb;
+ if (count) {
+ interval = cmb_data->last_update -
+ cdev->private->cmb_start_time;
+ interval = (interval * 1000) >> 12;
interval /= count;
- else
+ } else
interval = -1;
+ spin_unlock_irq(cdev->ccwlock);
return sprintf(buf, "%ld\n", interval);
}
@@ -823,7 +1085,10 @@ cmb_show_avg_utilization(struct device *dev, struct device_attribute *attr, char
int ret;
ret = cmf_readall(to_ccwdev(dev), &data);
- if (ret)
+ if (ret == -EAGAIN || ret == -ENODEV)
+ /* No data (yet/currently) available to use for calculation. */
+ return sprintf(buf, "n/a\n");
+ else if (ret)
return ret;
utilization = data.device_connect_time +
@@ -876,7 +1141,7 @@ static struct attribute *cmf_attributes[] = {
&dev_attr_avg_device_disconnect_time.attr,
&dev_attr_avg_control_unit_queuing_time.attr,
&dev_attr_avg_device_active_only_time.attr,
- 0,
+ NULL,
};
static struct attribute_group cmf_attr_group = {
@@ -896,7 +1161,7 @@ static struct attribute *cmf_attributes_ext[] = {
&dev_attr_avg_device_active_only_time.attr,
&dev_attr_avg_device_busy_time.attr,
&dev_attr_avg_initial_command_response_time.attr,
- 0,
+ NULL,
};
static struct attribute_group cmf_attr_group_ext = {
@@ -982,6 +1247,13 @@ cmf_readall(struct ccw_device *cdev, struct cmbdata *data)
return cmbops->readall(cdev, data);
}
+/* Reenable cmf when a disconnected device becomes available again. */
+int cmf_reenable(struct ccw_device *cdev)
+{
+ cmbops->reset(cdev);
+ return cmbops->set(cdev, 2);
+}
+
static int __init
init_cmf(void)
{
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 74ea8aac4b7d..7086a74e9871 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -19,9 +19,11 @@
#include "cio_debug.h"
#include "ioasm.h"
#include "chsc.h"
+#include "device.h"
int need_rescan = 0;
int css_init_done = 0;
+static int need_reprobe = 0;
static int max_ssid = 0;
struct channel_subsystem *css[__MAX_CSSID + 1];
@@ -106,6 +108,24 @@ css_subchannel_release(struct device *dev)
extern int css_get_ssd_info(struct subchannel *sch);
+
+int css_sch_device_register(struct subchannel *sch)
+{
+ int ret;
+
+ mutex_lock(&sch->reg_mutex);
+ ret = device_register(&sch->dev);
+ mutex_unlock(&sch->reg_mutex);
+ return ret;
+}
+
+void css_sch_device_unregister(struct subchannel *sch)
+{
+ mutex_lock(&sch->reg_mutex);
+ device_unregister(&sch->dev);
+ mutex_unlock(&sch->reg_mutex);
+}
+
static int
css_register_subchannel(struct subchannel *sch)
{
@@ -117,7 +137,7 @@ css_register_subchannel(struct subchannel *sch)
sch->dev.release = &css_subchannel_release;
/* make it known to the system */
- ret = device_register(&sch->dev);
+ ret = css_sch_device_register(sch);
if (ret)
printk (KERN_WARNING "%s: could not register %s\n",
__func__, sch->dev.bus_id);
@@ -162,136 +182,141 @@ get_subchannel_by_schid(struct subchannel_id schid)
return dev ? to_subchannel(dev) : NULL;
}
-
-static inline int
-css_get_subchannel_status(struct subchannel *sch, struct subchannel_id schid)
+static inline int css_get_subchannel_status(struct subchannel *sch)
{
struct schib schib;
- int cc;
- cc = stsch(schid, &schib);
- if (cc)
- return CIO_GONE;
- if (!schib.pmcw.dnv)
+ if (stsch(sch->schid, &schib) || !schib.pmcw.dnv)
return CIO_GONE;
- if (sch && sch->schib.pmcw.dnv &&
- (schib.pmcw.dev != sch->schib.pmcw.dev))
+ if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
return CIO_REVALIDATE;
- if (sch && !sch->lpm)
+ if (!sch->lpm)
return CIO_NO_PATH;
return CIO_OPER;
}
-
-static int
-css_evaluate_subchannel(struct subchannel_id schid, int slow)
+
+static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
{
int event, ret, disc;
- struct subchannel *sch;
unsigned long flags;
+ enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
- sch = get_subchannel_by_schid(schid);
- disc = sch ? device_is_disconnected(sch) : 0;
+ spin_lock_irqsave(&sch->lock, flags);
+ disc = device_is_disconnected(sch);
if (disc && slow) {
- if (sch)
- put_device(&sch->dev);
- return 0; /* Already processed. */
+ /* Disconnected devices are evaluated directly only.*/
+ spin_unlock_irqrestore(&sch->lock, flags);
+ return 0;
}
- /*
- * We've got a machine check, so running I/O won't get an interrupt.
- * Kill any pending timers.
- */
- if (sch)
- device_kill_pending_timer(sch);
+ /* No interrupt after machine check - kill pending timers. */
+ device_kill_pending_timer(sch);
if (!disc && !slow) {
- if (sch)
- put_device(&sch->dev);
- return -EAGAIN; /* Will be done on the slow path. */
+ /* Non-disconnected devices are evaluated on the slow path. */
+ spin_unlock_irqrestore(&sch->lock, flags);
+ return -EAGAIN;
}
- event = css_get_subchannel_status(sch, schid);
+ event = css_get_subchannel_status(sch);
CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
- schid.ssid, schid.sch_no, event,
- sch?(disc?"disconnected":"normal"):"unknown",
- slow?"slow":"fast");
+ 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:
- case CIO_GONE:
- if (!sch) {
- /* Never used this subchannel. Ignore. */
- ret = 0;
+ if (disc) {
+ /* Check if paths have become available. */
+ action = REPROBE;
break;
}
- if (disc && (event == CIO_NO_PATH)) {
- /*
- * Uargh, hack again. Because we don't get a machine
- * check on configure on, our path bookkeeping can
- * be out of date here (it's fine while we only do
- * logical varying or get chsc machine checks). We
- * need to force reprobing or we might miss devices
- * coming operational again. It won't do harm in real
- * no path situations.
- */
- spin_lock_irqsave(&sch->lock, flags);
- device_trigger_reprobe(sch);
+ /* 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 = 0;
- break;
- }
- if (sch->driver && sch->driver->notify &&
- sch->driver->notify(&sch->dev, event)) {
- cio_disable_subchannel(sch);
- device_set_disconnected(sch);
- ret = 0;
- break;
+ ret = sch->driver->notify(&sch->dev, event);
+ spin_lock_irqsave(&sch->lock, flags);
+ if (ret)
+ action = NONE;
}
- /*
- * Unregister subchannel.
- * The device will be killed automatically.
- */
- cio_disable_subchannel(sch);
- device_unregister(&sch->dev);
- /* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
- put_device(&sch->dev);
- ret = 0;
break;
case CIO_REVALIDATE:
- /*
- * Revalidation machine check. Sick.
- * We don't notify the driver since we have to throw the device
- * away in any case.
- */
- if (!disc) {
- device_unregister(&sch->dev);
- /* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
- put_device(&sch->dev);
- ret = css_probe_device(schid);
- } else {
- /*
- * We can't immediately deregister the disconnected
- * device since it might block.
- */
- spin_lock_irqsave(&sch->lock, flags);
- device_trigger_reprobe(sch);
- spin_unlock_irqrestore(&sch->lock, flags);
- ret = 0;
- }
+ /* 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) {
- spin_lock_irqsave(&sch->lock, flags);
+ if (disc)
/* Get device operational again. */
- device_trigger_reprobe(sch);
- spin_unlock_irqrestore(&sch->lock, flags);
- }
- ret = sch ? 0 : css_probe_device(schid);
+ 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);
+
+ /* Probe if necessary. */
+ if (action == UNREGISTER_PROBE)
+ ret = css_probe_device(sch->schid);
+ break;
+ case REPROBE:
+ device_trigger_reprobe(sch);
break;
default:
- BUG();
- ret = 0;
+ break;
}
+ spin_unlock_irqrestore(&sch->lock, flags);
+
+ return ret;
+}
+
+static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
+{
+ struct schib schib;
+
+ if (!slow) {
+ /* Will be done on the slow path. */
+ return -EAGAIN;
+ }
+ if (stsch(schid, &schib) || !schib.pmcw.dnv) {
+ /* Unusable - ignore. */
+ return 0;
+ }
+ CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, "
+ "slow path.\n", schid.ssid, schid.sch_no, CIO_OPER);
+
+ return css_probe_device(schid);
+}
+
+static int css_evaluate_subchannel(struct subchannel_id schid, int slow)
+{
+ struct subchannel *sch;
+ int ret;
+
+ sch = get_subchannel_by_schid(schid);
+ if (sch) {
+ ret = css_evaluate_known_subchannel(sch, slow);
+ put_device(&sch->dev);
+ } else
+ ret = css_evaluate_new_subchannel(schid, slow);
+
return ret;
}
@@ -339,6 +364,67 @@ typedef void (*workfunc)(void *);
DECLARE_WORK(slow_path_work, (workfunc)css_trigger_slow_path, NULL);
struct workqueue_struct *slow_path_wq;
+/* Reprobe subchannel if unregistered. */
+static int reprobe_subchannel(struct subchannel_id schid, void *data)
+{
+ struct subchannel *sch;
+ int ret;
+
+ CIO_DEBUG(KERN_INFO, 6, "cio: reprobe 0.%x.%04x\n",
+ schid.ssid, schid.sch_no);
+ if (need_reprobe)
+ return -EAGAIN;
+
+ sch = get_subchannel_by_schid(schid);
+ if (sch) {
+ /* Already known. */
+ put_device(&sch->dev);
+ return 0;
+ }
+
+ ret = css_probe_device(schid);
+ switch (ret) {
+ case 0:
+ break;
+ case -ENXIO:
+ case -ENOMEM:
+ /* These should abort looping */
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/* Work function used to reprobe all unregistered subchannels. */
+static void reprobe_all(void *data)
+{
+ int ret;
+
+ CIO_MSG_EVENT(2, "reprobe start\n");
+
+ need_reprobe = 0;
+ /* Make sure initial subchannel scan is done. */
+ wait_event(ccw_device_init_wq,
+ atomic_read(&ccw_device_init_count) == 0);
+ ret = for_each_subchannel(reprobe_subchannel, NULL);
+
+ CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret,
+ need_reprobe);
+}
+
+DECLARE_WORK(css_reprobe_work, reprobe_all, NULL);
+
+/* Schedule reprobing of all unregistered subchannels. */
+void css_schedule_reprobe(void)
+{
+ need_reprobe = 1;
+ queue_work(ccw_device_work, &css_reprobe_work);
+}
+
+EXPORT_SYMBOL_GPL(css_schedule_reprobe);
+
/*
* Rescan for new devices. FIXME: This is slow.
* This function is called when we have lost CRWs due to overflows and we have
@@ -542,9 +628,13 @@ init_channel_subsystem (void)
ret = device_register(&css[i]->device);
if (ret)
goto out_free;
- if (css_characteristics_avail && css_chsc_characteristics.secm)
- device_create_file(&css[i]->device,
- &dev_attr_cm_enable);
+ if (css_characteristics_avail &&
+ css_chsc_characteristics.secm) {
+ ret = device_create_file(&css[i]->device,
+ &dev_attr_cm_enable);
+ if (ret)
+ goto out_device;
+ }
}
css_init_done = 1;
@@ -552,6 +642,8 @@ init_channel_subsystem (void)
for_each_subchannel(__init_channel_subsystem, NULL);
return 0;
+out_device:
+ device_unregister(&css[i]->device);
out_free:
kfree(css[i]);
out_unregister:
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index e210f89a2449..8aabb4adeb5f 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -100,7 +100,7 @@ struct ccw_device_private {
struct qdio_irq *qdio_data;
struct irb irb; /* device status */
struct senseid senseid; /* SenseID info */
- struct pgid pgid; /* path group ID */
+ struct pgid pgid[8]; /* path group IDs per chpid*/
struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */
struct work_struct kick_work;
wait_queue_head_t wait_q;
@@ -136,6 +136,8 @@ extern struct bus_type css_bus_type;
extern struct css_driver io_subchannel_driver;
extern int css_probe_device(struct subchannel_id);
+extern int css_sch_device_register(struct subchannel *);
+extern void css_sch_device_unregister(struct subchannel *);
extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
extern int css_init_done;
extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 8e3053c2a451..688945662c15 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -8,7 +8,6 @@
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
@@ -53,55 +52,83 @@ ccw_bus_match (struct device * dev, struct device_driver * drv)
return 1;
}
-/*
- * Hotplugging interface for ccw devices.
- * Heavily modeled on pci and usb hotplug.
- */
-static int
-ccw_uevent (struct device *dev, char **envp, int num_envp,
- char *buffer, int buffer_size)
+/* Store modalias string delimited by prefix/suffix string into buffer with
+ * specified size. Return length of resulting string (excluding trailing '\0')
+ * even if string doesn't fit buffer (snprintf semantics). */
+static int snprint_alias(char *buf, size_t size, const char *prefix,
+ struct ccw_device_id *id, const char *suffix)
{
- struct ccw_device *cdev = to_ccwdev(dev);
- int i = 0;
- int length = 0;
+ int len;
- if (!cdev)
- return -ENODEV;
+ len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type,
+ id->cu_model);
+ if (len > size)
+ return len;
+ buf += len;
+ size -= len;
+
+ if (id->dev_type != 0)
+ len += snprintf(buf, size, "dt%04Xdm%02X%s", id->dev_type,
+ id->dev_model, suffix);
+ else
+ len += snprintf(buf, size, "dtdm%s", suffix);
- /* what we want to pass to /sbin/hotplug */
+ return len;
+}
- envp[i++] = buffer;
- length += scnprintf(buffer, buffer_size - length, "CU_TYPE=%04X",
- cdev->id.cu_type);
- if ((buffer_size - length <= 0) || (i >= num_envp))
- return -ENOMEM;
- ++length;
- buffer += length;
+/* Set up environment variables for ccw device uevent. Return 0 on success,
+ * non-zero otherwise. */
+static int ccw_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct ccw_device_id *id = &(cdev->id);
+ int i = 0;
+ int len;
+ /* CU_TYPE= */
+ len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1;
+ if (len > buffer_size || i >= num_envp)
+ return -ENOMEM;
envp[i++] = buffer;
- length += scnprintf(buffer, buffer_size - length, "CU_MODEL=%02X",
- cdev->id.cu_model);
- if ((buffer_size - length <= 0) || (i >= num_envp))
+ buffer += len;
+ buffer_size -= len;
+
+ /* CU_MODEL= */
+ len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1;
+ if (len > buffer_size || i >= num_envp)
return -ENOMEM;
- ++length;
- buffer += length;
+ envp[i++] = buffer;
+ buffer += len;
+ buffer_size -= len;
/* The next two can be zero, that's ok for us */
- envp[i++] = buffer;
- length += scnprintf(buffer, buffer_size - length, "DEV_TYPE=%04X",
- cdev->id.dev_type);
- if ((buffer_size - length <= 0) || (i >= num_envp))
+ /* DEV_TYPE= */
+ len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1;
+ if (len > buffer_size || i >= num_envp)
return -ENOMEM;
- ++length;
- buffer += length;
+ envp[i++] = buffer;
+ buffer += len;
+ buffer_size -= len;
+ /* DEV_MODEL= */
+ len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X",
+ (unsigned char) id->dev_model) + 1;
+ if (len > buffer_size || i >= num_envp)
+ return -ENOMEM;
envp[i++] = buffer;
- length += scnprintf(buffer, buffer_size - length, "DEV_MODEL=%02X",
- cdev->id.dev_model);
- if ((buffer_size - length <= 0) || (i >= num_envp))
+ buffer += len;
+ buffer_size -= len;
+
+ /* MODALIAS= */
+ len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1;
+ if (len > buffer_size || i >= num_envp)
return -ENOMEM;
+ envp[i++] = buffer;
+ buffer += len;
+ buffer_size -= len;
- envp[i] = 0;
+ envp[i] = NULL;
return 0;
}
@@ -133,8 +160,8 @@ struct css_driver io_subchannel_driver = {
struct workqueue_struct *ccw_device_work;
struct workqueue_struct *ccw_device_notify_work;
-static wait_queue_head_t ccw_device_init_wq;
-static atomic_t ccw_device_init_count;
+wait_queue_head_t ccw_device_init_wq;
+atomic_t ccw_device_init_count;
static int __init
init_ccw_bus_type (void)
@@ -252,16 +279,11 @@ modalias_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct ccw_device *cdev = to_ccwdev(dev);
struct ccw_device_id *id = &(cdev->id);
- int ret;
+ int len;
- ret = sprintf(buf, "ccw:t%04Xm%02X",
- id->cu_type, id->cu_model);
- if (id->dev_type != 0)
- ret += sprintf(buf + ret, "dt%04Xdm%02X\n",
- id->dev_type, id->dev_model);
- else
- ret += sprintf(buf + ret, "dtdm\n");
- return ret;
+ len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1;
+
+ return len > PAGE_SIZE ? PAGE_SIZE : len;
}
static ssize_t
@@ -281,7 +303,7 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
* 'throw away device'.
*/
sch = to_subchannel(cdev->dev.parent);
- device_unregister(&sch->dev);
+ css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
@@ -557,12 +579,11 @@ get_disc_ccwdev_by_devno(unsigned int devno, unsigned int ssid,
struct ccw_device *sibling)
{
struct device *dev;
- struct match_data data = {
- .devno = devno,
- .ssid = ssid,
- .sibling = sibling,
- };
+ struct match_data data;
+ data.devno = devno;
+ data.ssid = ssid;
+ data.sibling = sibling;
dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno);
return dev ? to_ccwdev(dev) : NULL;
@@ -626,7 +647,7 @@ ccw_device_do_unreg_rereg(void *data)
other_sch->schib.pmcw.intparm = 0;
cio_modify(other_sch);
}
- device_unregister(&other_sch->dev);
+ css_sch_device_unregister(other_sch);
}
}
/* Update ssd info here. */
@@ -710,7 +731,7 @@ ccw_device_call_sch_unregister(void *data)
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
- device_unregister(&sch->dev);
+ css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
@@ -836,10 +857,8 @@ io_subchannel_probe (struct subchannel *sch)
return -ENOMEM;
}
atomic_set(&cdev->private->onoff, 0);
- cdev->dev = (struct device) {
- .parent = &sch->dev,
- .release = ccw_device_release,
- };
+ cdev->dev.parent = &sch->dev;
+ cdev->dev.release = ccw_device_release;
INIT_LIST_HEAD(&cdev->private->kick_work.entry);
/* Do first half of device_register. */
device_initialize(&cdev->dev);
@@ -978,9 +997,7 @@ ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
int rc;
/* Initialize the ccw_device structure. */
- cdev->dev = (struct device) {
- .parent = &sch->dev,
- };
+ cdev->dev.parent= &sch->dev;
rc = io_subchannel_recog(cdev, sch);
if (rc)
return rc;
@@ -1058,7 +1075,7 @@ get_ccwdev_by_busid(struct ccw_driver *cdrv, const char *bus_id)
__ccwdev_check_busid);
put_driver(drv);
- return dev ? to_ccwdev(dev) : 0;
+ return dev ? to_ccwdev(dev) : NULL;
}
/************************** device driver handling ************************/
@@ -1083,7 +1100,7 @@ ccw_device_probe (struct device *dev)
ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV;
if (ret) {
- cdev->drv = 0;
+ cdev->drv = NULL;
return ret;
}
@@ -1114,7 +1131,7 @@ ccw_device_remove (struct device *dev)
ret, cdev->dev.bus_id);
}
ccw_device_set_timeout(cdev, 0);
- cdev->drv = 0;
+ cdev->drv = NULL;
return 0;
}
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 11587ebb7289..00be9a5b4acd 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -1,6 +1,10 @@
#ifndef S390_DEVICE_H
#define S390_DEVICE_H
+#include <asm/ccwdev.h>
+#include <asm/atomic.h>
+#include <linux/wait.h>
+
/*
* states of the device statemachine
*/
@@ -23,6 +27,7 @@ enum dev_state {
DEV_STATE_DISCONNECTED,
DEV_STATE_DISCONNECTED_SENSE_ID,
DEV_STATE_CMFCHANGE,
+ DEV_STATE_CMFUPDATE,
/* last element! */
NR_DEV_STATES
};
@@ -67,6 +72,8 @@ dev_fsm_final_state(struct ccw_device *cdev)
extern struct workqueue_struct *ccw_device_work;
extern struct workqueue_struct *ccw_device_notify_work;
+extern wait_queue_head_t ccw_device_init_wq;
+extern atomic_t ccw_device_init_count;
void io_subchannel_recog_done(struct ccw_device *cdev);
@@ -112,5 +119,8 @@ int ccw_device_stlck(struct ccw_device *);
void ccw_device_set_timeout(struct ccw_device *, int);
extern struct subchannel_id ccw_device_get_subchannel_id(struct ccw_device *);
+/* Channel measurement facility related */
void retry_set_schib(struct ccw_device *cdev);
+void cmf_retry_copy_block(struct ccw_device *);
+int cmf_reenable(struct ccw_device *);
#endif
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 49ec562d7f60..dace46fc32e8 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -9,7 +9,6 @@
*/
#include <linux/module.h>
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/string.h>
@@ -153,7 +152,8 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
if (cdev->private->iretry) {
cdev->private->iretry--;
ret = cio_halt(sch);
- return (ret == 0) ? -EBUSY : ret;
+ if (ret != -EBUSY)
+ return (ret == 0) ? -EBUSY : ret;
}
/* halt io unsuccessful. */
cdev->private->iretry = 255; /* 255 clear retries. */
@@ -232,10 +232,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
*/
old_lpm = sch->lpm;
stsch(sch->schid, &sch->schib);
- sch->lpm = sch->schib.pmcw.pim &
- sch->schib.pmcw.pam &
- sch->schib.pmcw.pom &
- sch->opm;
+ sch->lpm = sch->schib.pmcw.pam & sch->opm;
/* Check since device may again have become not operational. */
if (!sch->schib.pmcw.dnv)
state = DEV_STATE_NOT_OPER;
@@ -267,12 +264,11 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
notify = 1;
}
/* fill out sense information */
- cdev->id = (struct ccw_device_id) {
- .cu_type = cdev->private->senseid.cu_type,
- .cu_model = cdev->private->senseid.cu_model,
- .dev_type = cdev->private->senseid.dev_type,
- .dev_model = cdev->private->senseid.dev_model,
- };
+ memset(&cdev->id, 0, sizeof(cdev->id));
+ cdev->id.cu_type = cdev->private->senseid.cu_type;
+ cdev->id.cu_model = cdev->private->senseid.cu_model;
+ cdev->id.dev_type = cdev->private->senseid.dev_type;
+ cdev->id.dev_model = cdev->private->senseid.dev_model;
if (notify) {
cdev->private->state = DEV_STATE_OFFLINE;
if (same_dev) {
@@ -336,8 +332,11 @@ ccw_device_oper_notify(void *data)
if (!ret)
/* Driver doesn't want device back. */
ccw_device_do_unreg_rereg((void *)cdev);
- else
+ else {
+ /* Reenable channel measurements, if needed. */
+ cmf_reenable(cdev);
wake_up(&cdev->private->wait_q);
+ }
}
/*
@@ -376,6 +375,56 @@ ccw_device_done(struct ccw_device *cdev, int state)
put_device (&cdev->dev);
}
+static inline int cmp_pgid(struct pgid *p1, struct pgid *p2)
+{
+ char *c1;
+ char *c2;
+
+ c1 = (char *)p1;
+ c2 = (char *)p2;
+
+ return memcmp(c1 + 1, c2 + 1, sizeof(struct pgid) - 1);
+}
+
+static void __ccw_device_get_common_pgid(struct ccw_device *cdev)
+{
+ int i;
+ int last;
+
+ last = 0;
+ for (i = 0; i < 8; i++) {
+ if (cdev->private->pgid[i].inf.ps.state1 == SNID_STATE1_RESET)
+ /* No PGID yet */
+ continue;
+ if (cdev->private->pgid[last].inf.ps.state1 ==
+ SNID_STATE1_RESET) {
+ /* First non-zero PGID */
+ last = i;
+ continue;
+ }
+ if (cmp_pgid(&cdev->private->pgid[i],
+ &cdev->private->pgid[last]) == 0)
+ /* Non-conflicting PGIDs */
+ continue;
+
+ /* PGID mismatch, can't pathgroup. */
+ CIO_MSG_EVENT(0, "SNID - pgid mismatch for device "
+ "0.%x.%04x, can't pathgroup\n",
+ cdev->private->ssid, cdev->private->devno);
+ cdev->private->options.pgroup = 0;
+ return;
+ }
+ if (cdev->private->pgid[last].inf.ps.state1 ==
+ SNID_STATE1_RESET)
+ /* No previous pgid found */
+ memcpy(&cdev->private->pgid[0], &css[0]->global_pgid,
+ sizeof(struct pgid));
+ else
+ /* Use existing pgid */
+ memcpy(&cdev->private->pgid[0], &cdev->private->pgid[last],
+ sizeof(struct pgid));
+}
+
/*
* Function called from device_pgid.c after sense path ground has completed.
*/
@@ -386,24 +435,26 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err)
sch = to_subchannel(cdev->dev.parent);
switch (err) {
- case 0:
- /* Start Path Group verification. */
- sch->vpm = 0; /* Start with no path groups set. */
- cdev->private->state = DEV_STATE_VERIFY;
- ccw_device_verify_start(cdev);
+ case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */
+ cdev->private->options.pgroup = 0;
+ break;
+ case 0: /* success */
+ case -EACCES: /* partial success, some paths not operational */
+ /* Check if all pgids are equal or 0. */
+ __ccw_device_get_common_pgid(cdev);
break;
case -ETIME: /* Sense path group id stopped by timeout. */
case -EUSERS: /* device is reserved for someone else. */
ccw_device_done(cdev, DEV_STATE_BOXED);
- break;
- case -EOPNOTSUPP: /* path grouping not supported, just set online. */
- cdev->private->options.pgroup = 0;
- ccw_device_done(cdev, DEV_STATE_ONLINE);
- break;
+ return;
default:
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
- break;
+ return;
}
+ /* Start Path Group verification. */
+ cdev->private->state = DEV_STATE_VERIFY;
+ cdev->private->flags.doverify = 0;
+ ccw_device_verify_start(cdev);
}
/*
@@ -502,7 +553,19 @@ ccw_device_nopath_notify(void *data)
void
ccw_device_verify_done(struct ccw_device *cdev, int err)
{
- cdev->private->flags.doverify = 0;
+ struct subchannel *sch;
+
+ sch = to_subchannel(cdev->dev.parent);
+ /* Update schib - pom may have changed. */
+ stsch(sch->schid, &sch->schib);
+ /* Update lpm with verified path mask. */
+ sch->lpm = sch->vpm;
+ /* Repeat path verification? */
+ if (cdev->private->flags.doverify) {
+ cdev->private->flags.doverify = 0;
+ ccw_device_verify_start(cdev);
+ return;
+ }
switch (err) {
case -EOPNOTSUPP: /* path grouping not supported, just set online. */
cdev->private->options.pgroup = 0;
@@ -511,12 +574,10 @@ 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 = (struct scsw) {
- .cc = 1,
- .fctl = SCSW_FCTL_START_FUNC,
- .actl = SCSW_ACTL_START_PEND,
- .stctl = SCSW_STCTL_STATUS_PEND,
- };
+ 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->flags.fake_irb = 0;
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
@@ -560,8 +621,10 @@ ccw_device_online(struct ccw_device *cdev)
}
/* Do we want to do path grouping? */
if (!cdev->private->options.pgroup) {
- /* No, set state online immediately. */
- ccw_device_done(cdev, DEV_STATE_ONLINE);
+ /* Start initial path verification. */
+ cdev->private->state = DEV_STATE_VERIFY;
+ cdev->private->flags.doverify = 0;
+ ccw_device_verify_start(cdev);
return 0;
}
/* Do a SensePGID first. */
@@ -703,8 +766,6 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
{
struct subchannel *sch;
- if (!cdev->private->options.pgroup)
- return;
if (cdev->private->state == DEV_STATE_W4SENSE) {
cdev->private->flags.doverify = 1;
return;
@@ -717,6 +778,7 @@ 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)) {
/*
* No final status yet or final status not yet delivered
@@ -728,6 +790,7 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
}
/* Device is idle, we can do the path verification. */
cdev->private->state = DEV_STATE_VERIFY;
+ cdev->private->flags.doverify = 0;
ccw_device_verify_start(cdev);
}
@@ -861,6 +924,8 @@ ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event)
irb = (struct irb *) __LC_IRB;
/* Accumulate status. We don't do basic sense. */
ccw_device_accumulate_irb(cdev, irb);
+ /* Remember to clear irb to avoid residuals. */
+ memset(&cdev->private->irb, 0, sizeof(struct irb));
/* Try to start delayed device verification. */
ccw_device_online_verify(cdev, 0);
/* Note: Don't call handler for cio initiated clear! */
@@ -988,11 +1053,10 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event)
}
static void
-ccw_device_wait4io_verify(struct ccw_device *cdev, enum dev_event dev_event)
+ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event)
{
- /* When the I/O has terminated, we have to start verification. */
- if (cdev->private->options.pgroup)
- cdev->private->flags.doverify = 1;
+ /* Start verification after current task finished. */
+ cdev->private->flags.doverify = 1;
}
static void
@@ -1057,10 +1121,7 @@ device_trigger_reprobe(struct subchannel *sch)
* The pim, pam, pom values may not be accurate, but they are the best
* we have before performing device selection :/
*/
- sch->lpm = sch->schib.pmcw.pim &
- sch->schib.pmcw.pam &
- sch->schib.pmcw.pom &
- sch->opm;
+ sch->lpm = sch->schib.pmcw.pam & sch->opm;
/* Re-set some bits in the pmcw that were lost. */
sch->schib.pmcw.isc = 3;
sch->schib.pmcw.csense = 1;
@@ -1093,6 +1154,13 @@ ccw_device_change_cmfstate(struct ccw_device *cdev, enum dev_event dev_event)
dev_fsm_event(cdev, dev_event);
}
+static void ccw_device_update_cmfblock(struct ccw_device *cdev,
+ enum dev_event dev_event)
+{
+ cmf_retry_copy_block(cdev);
+ cdev->private->state = DEV_STATE_ONLINE;
+ dev_fsm_event(cdev, dev_event);
+}
static void
ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event)
@@ -1177,7 +1245,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_NOTOPER] = ccw_device_online_notoper,
[DEV_EVENT_INTERRUPT] = ccw_device_verify_irq,
[DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout,
- [DEV_EVENT_VERIFY] = ccw_device_nop,
+ [DEV_EVENT_VERIFY] = ccw_device_delay_verify,
},
[DEV_STATE_ONLINE] = {
[DEV_EVENT_NOTOPER] = ccw_device_online_notoper,
@@ -1220,7 +1288,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_NOTOPER] = ccw_device_online_notoper,
[DEV_EVENT_INTERRUPT] = ccw_device_wait4io_irq,
[DEV_EVENT_TIMEOUT] = ccw_device_wait4io_timeout,
- [DEV_EVENT_VERIFY] = ccw_device_wait4io_verify,
+ [DEV_EVENT_VERIFY] = ccw_device_delay_verify,
},
[DEV_STATE_QUIESCE] = {
[DEV_EVENT_NOTOPER] = ccw_device_quiesce_done,
@@ -1233,7 +1301,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_NOTOPER] = ccw_device_nop,
[DEV_EVENT_INTERRUPT] = ccw_device_start_id,
[DEV_EVENT_TIMEOUT] = ccw_device_bug,
- [DEV_EVENT_VERIFY] = ccw_device_nop,
+ [DEV_EVENT_VERIFY] = ccw_device_start_id,
},
[DEV_STATE_DISCONNECTED_SENSE_ID] = {
[DEV_EVENT_NOTOPER] = ccw_device_recog_notoper,
@@ -1247,6 +1315,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_TIMEOUT] = ccw_device_change_cmfstate,
[DEV_EVENT_VERIFY] = ccw_device_change_cmfstate,
},
+ [DEV_STATE_CMFUPDATE] = {
+ [DEV_EVENT_NOTOPER] = ccw_device_update_cmfblock,
+ [DEV_EVENT_INTERRUPT] = ccw_device_update_cmfblock,
+ [DEV_EVENT_TIMEOUT] = ccw_device_update_cmfblock,
+ [DEV_EVENT_VERIFY] = ccw_device_update_cmfblock,
+ },
};
/*
diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c
index e60b2d8103b8..1398367b5f68 100644
--- a/drivers/s390/cio/device_id.c
+++ b/drivers/s390/cio/device_id.c
@@ -10,7 +10,6 @@
*/
#include <linux/module.h>
-#include <linux/config.h>
#include <linux/init.h>
#include <asm/ccwdev.h>
@@ -43,18 +42,15 @@ diag210(struct diag210 * addr)
spin_lock_irqsave(&diag210_lock, flags);
diag210_tmp = *addr;
- asm volatile (
- " lhi %0,-1\n"
- " sam31\n"
- " diag %1,0,0x210\n"
- "0: ipm %0\n"
- " srl %0,28\n"
- "1: sam64\n"
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
- : "=&d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory" );
+ asm volatile(
+ " lhi %0,-1\n"
+ " sam31\n"
+ " diag %1,0,0x210\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1: sam64\n"
+ EX_TABLE(0b,1b)
+ : "=&d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory");
*addr = diag210_tmp;
spin_unlock_irqrestore(&diag210_lock, flags);
@@ -67,17 +63,14 @@ diag210(struct diag210 * addr)
{
int ccode;
- asm volatile (
- " lhi %0,-1\n"
- " diag %1,0,0x210\n"
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " lhi %0,-1\n"
+ " diag %1,0,0x210\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
- : "=&d" (ccode) : "a" (__pa(addr)) : "cc", "memory" );
+ EX_TABLE(0b,1b)
+ : "=&d" (ccode) : "a" (__pa(addr)) : "cc", "memory");
return ccode;
}
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 795abb5a65ba..93a897eebfff 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -6,7 +6,6 @@
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
@@ -78,7 +77,8 @@ ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
return -ENODEV;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
- if (cdev->private->state == DEV_STATE_VERIFY) {
+ if (cdev->private->state == DEV_STATE_VERIFY ||
+ cdev->private->state == DEV_STATE_CLEAR_VERIFY) {
/* Remember to fake irb when finished. */
if (!cdev->private->flags.fake_irb) {
cdev->private->flags.fake_irb = 1;
@@ -96,6 +96,12 @@ ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
ret = cio_set_options (sch, flags);
if (ret)
return ret;
+ /* Adjust requested path mask to excluded varied off paths. */
+ if (lpm) {
+ lpm &= sch->opm;
+ if (lpm == 0)
+ return -EACCES;
+ }
ret = cio_start_key (sch, cpa, lpm, key);
if (ret == 0)
cdev->private->intparm = intparm;
@@ -250,7 +256,7 @@ ccw_device_get_path_mask(struct ccw_device *cdev)
if (!sch)
return 0;
else
- return sch->vpm;
+ return sch->lpm;
}
static void
@@ -263,6 +269,9 @@ ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb)
/* Abuse intparm for error reporting. */
if (IS_ERR(irb))
cdev->private->intparm = -EIO;
+ else if (irb->scsw.cc == 1)
+ /* Retry for deferred condition code. */
+ cdev->private->intparm = -EAGAIN;
else if ((irb->scsw.dstat !=
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
(irb->scsw.cstat != 0)) {
@@ -270,7 +279,8 @@ ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb)
* We didn't get channel end / device end. Check if path
* verification has been started; we can retry after it has
* finished. We also retry unit checks except for command reject
- * or intervention required.
+ * or intervention required. Also check for long busy
+ * conditions.
*/
if (cdev->private->flags.doverify ||
cdev->private->state == DEV_STATE_VERIFY)
@@ -279,6 +289,10 @@ ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb)
!(irb->ecw[0] &
(SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ)))
cdev->private->intparm = -EAGAIN;
+ else if ((irb->scsw.dstat & DEV_STAT_ATTENTION) &&
+ (irb->scsw.dstat & DEV_STAT_DEV_END) &&
+ (irb->scsw.dstat & DEV_STAT_UNIT_EXCEP))
+ cdev->private->intparm = -EAGAIN;
else
cdev->private->intparm = -EIO;
@@ -296,7 +310,7 @@ __ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, _
sch = to_subchannel(cdev->dev.parent);
do {
ret = cio_start (sch, ccw, lpm);
- if ((ret == -EBUSY) || (ret == -EACCES)) {
+ if (ret == -EBUSY) {
/* Try again later. */
spin_unlock_irq(&sch->lock);
msleep(10);
@@ -425,6 +439,13 @@ read_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lp
if (!ciw || ciw->cmd == 0)
return -EOPNOTSUPP;
+ /* Adjust requested path mask to excluded varied off paths. */
+ if (lpm) {
+ lpm &= sch->opm;
+ if (lpm == 0)
+ return -EACCES;
+ }
+
rcd_ccw = kzalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
if (!rcd_ccw)
return -ENOMEM;
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 85b1020a1fcc..8ca2d078848c 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -9,7 +9,6 @@
* Path Group ID functions.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -25,6 +24,21 @@
#include "ioasm.h"
/*
+ * Helper function called from interrupt context to decide whether an
+ * operation should be tried again.
+ */
+static int __ccw_device_should_retry(struct scsw *scsw)
+{
+ /* CC is only valid if start function bit is set. */
+ if ((scsw->fctl & SCSW_FCTL_START_FUNC) && scsw->cc == 1)
+ return 1;
+ /* No more activity. For sense and set PGID we stubbornly try again. */
+ if (!scsw->actl)
+ return 1;
+ return 0;
+}
+
+/*
* Start Sense Path Group ID helper function. Used in ccw_device_recog
* and ccw_device_sense_pgid.
*/
@@ -34,12 +48,17 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
struct subchannel *sch;
struct ccw1 *ccw;
int ret;
+ int i;
sch = to_subchannel(cdev->dev.parent);
+ /* Return if we already checked on all paths. */
+ if (cdev->private->imask == 0)
+ return (sch->lpm == 0) ? -ENODEV : -EACCES;
+ i = 8 - ffs(cdev->private->imask);
+
/* Setup sense path group id channel program. */
ccw = cdev->private->iccws;
ccw->cmd_code = CCW_CMD_SENSE_PGID;
- ccw->cda = (__u32) __pa (&cdev->private->pgid);
ccw->count = sizeof (struct pgid);
ccw->flags = CCW_FLAG_SLI;
@@ -49,6 +68,7 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
ret = -ENODEV;
while (cdev->private->imask != 0) {
/* Try every path multiple times. */
+ ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
if (cdev->private->iretry > 0) {
cdev->private->iretry--;
ret = cio_start (sch, cdev->private->iccws,
@@ -65,7 +85,9 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
}
cdev->private->imask >>= 1;
cdev->private->iretry = 5;
+ i++;
}
+
return ret;
}
@@ -77,7 +99,7 @@ ccw_device_sense_pgid_start(struct ccw_device *cdev)
cdev->private->state = DEV_STATE_SENSE_PGID;
cdev->private->imask = 0x80;
cdev->private->iretry = 5;
- memset (&cdev->private->pgid, 0, sizeof (struct pgid));
+ memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid));
ret = __ccw_device_sense_pgid_start(cdev);
if (ret && ret != -EBUSY)
ccw_device_sense_pgid_done(cdev, ret);
@@ -92,6 +114,7 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
{
struct subchannel *sch;
struct irb *irb;
+ int i;
sch = to_subchannel(cdev->dev.parent);
irb = &cdev->private->irb;
@@ -125,7 +148,8 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
sch->schid.sch_no, sch->orb.lpm);
return -EACCES;
}
- if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
+ i = 8 - ffs(cdev->private->imask);
+ if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
"is reserved by someone else\n",
cdev->private->devno, sch->schid.ssid,
@@ -146,10 +170,10 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
int ret;
irb = (struct irb *) __LC_IRB;
- /* Retry sense pgid for cc=1. */
+
if (irb->scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if (irb->scsw.cc == 1) {
+ if (__ccw_device_should_retry(&irb->scsw)) {
ret = __ccw_device_sense_pgid_start(cdev);
if (ret && ret != -EBUSY)
ccw_device_sense_pgid_done(cdev, ret);
@@ -163,12 +187,6 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
memset(&cdev->private->irb, 0, sizeof(struct irb));
switch (ret) {
/* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
- case 0: /* Sense Path Group ID successful. */
- if (cdev->private->pgid.inf.ps.state1 == SNID_STATE1_RESET)
- memcpy(&cdev->private->pgid, &css[0]->global_pgid,
- sizeof(struct pgid));
- ccw_device_sense_pgid_done(cdev, 0);
- break;
case -EOPNOTSUPP: /* Sense Path Group ID not supported */
ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
break;
@@ -177,13 +195,15 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
break;
case -EACCES: /* channel is not operational. */
sch->lpm &= ~cdev->private->imask;
+ /* Fall through. */
+ case 0: /* Sense Path Group ID successful. */
cdev->private->imask >>= 1;
cdev->private->iretry = 5;
/* Fall through. */
case -EAGAIN: /* Try again. */
ret = __ccw_device_sense_pgid_start(cdev);
if (ret != 0 && ret != -EBUSY)
- ccw_device_sense_pgid_done(cdev, -ENODEV);
+ ccw_device_sense_pgid_done(cdev, ret);
break;
case -EUSERS: /* device is reserved for someone else. */
ccw_device_sense_pgid_done(cdev, -EUSERS);
@@ -204,20 +224,20 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
sch = to_subchannel(cdev->dev.parent);
/* Setup sense path group id channel program. */
- cdev->private->pgid.inf.fc = func;
+ cdev->private->pgid[0].inf.fc = func;
ccw = cdev->private->iccws;
if (!cdev->private->flags.pgid_single) {
- cdev->private->pgid.inf.fc |= SPID_FUNC_MULTI_PATH;
+ cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH;
ccw->cmd_code = CCW_CMD_SUSPEND_RECONN;
ccw->cda = 0;
ccw->count = 0;
ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC;
ccw++;
} else
- cdev->private->pgid.inf.fc |= SPID_FUNC_SINGLE_PATH;
+ cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH;
ccw->cmd_code = CCW_CMD_SET_PGID;
- ccw->cda = (__u32) __pa (&cdev->private->pgid);
+ ccw->cda = (__u32) __pa (&cdev->private->pgid[0]);
ccw->count = sizeof (struct pgid);
ccw->flags = CCW_FLAG_SLI;
@@ -225,18 +245,17 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
memset(&cdev->private->irb, 0, sizeof(struct irb));
/* Try multiple times. */
- ret = -ENODEV;
+ ret = -EACCES;
if (cdev->private->iretry > 0) {
cdev->private->iretry--;
ret = cio_start (sch, cdev->private->iccws,
cdev->private->imask);
- /* ret is 0, -EBUSY, -EACCES or -ENODEV */
- if ((ret != -EACCES) && (ret != -ENODEV))
+ /* We expect an interrupt in case of success or busy
+ * indication. */
+ if ((ret == 0) || (ret == -EBUSY))
return ret;
}
- /* PGID command failed on this path. Switch it off. */
- sch->lpm &= ~cdev->private->imask;
- sch->vpm &= ~cdev->private->imask;
+ /* PGID command failed on this path. */
CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
"0.%x.%04x, lpm %02X, became 'not operational'\n",
cdev->private->devno, sch->schid.ssid,
@@ -245,6 +264,47 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
}
/*
+ * Helper function to send a nop ccw down a path.
+ */
+static int __ccw_device_do_nop(struct ccw_device *cdev)
+{
+ struct subchannel *sch;
+ struct ccw1 *ccw;
+ int ret;
+
+ sch = to_subchannel(cdev->dev.parent);
+
+ /* Setup nop channel program. */
+ ccw = cdev->private->iccws;
+ ccw->cmd_code = CCW_CMD_NOOP;
+ ccw->cda = 0;
+ ccw->count = 0;
+ ccw->flags = CCW_FLAG_SLI;
+
+ /* Reset device status. */
+ memset(&cdev->private->irb, 0, sizeof(struct irb));
+
+ /* Try multiple times. */
+ ret = -EACCES;
+ if (cdev->private->iretry > 0) {
+ cdev->private->iretry--;
+ ret = cio_start (sch, cdev->private->iccws,
+ cdev->private->imask);
+ /* We expect an interrupt in case of success or busy
+ * indication. */
+ if ((ret == 0) || (ret == -EBUSY))
+ return ret;
+ }
+ /* nop command failed on this path. */
+ CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel "
+ "0.%x.%04x, lpm %02X, became 'not operational'\n",
+ cdev->private->devno, sch->schid.ssid,
+ sch->schid.sch_no, cdev->private->imask);
+ return ret;
+}
+
+
+/*
* Called from interrupt context to check if a valid answer
* to Set Path Group ID was received.
*/
@@ -283,28 +343,59 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
return 0;
}
+/*
+ * Called from interrupt context to check the path status after a nop has
+ * been send.
+ */
+static int __ccw_device_check_nop(struct ccw_device *cdev)
+{
+ struct subchannel *sch;
+ struct irb *irb;
+
+ sch = to_subchannel(cdev->dev.parent);
+ irb = &cdev->private->irb;
+ if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
+ return -ETIME;
+ if (irb->scsw.cc == 3) {
+ CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x,"
+ " lpm %02X, became 'not operational'\n",
+ cdev->private->devno, sch->schid.ssid,
+ sch->schid.sch_no, cdev->private->imask);
+ return -EACCES;
+ }
+ return 0;
+}
+
static void
__ccw_device_verify_start(struct ccw_device *cdev)
{
struct subchannel *sch;
- __u8 imask, func;
+ __u8 func;
int ret;
sch = to_subchannel(cdev->dev.parent);
- while (sch->vpm != sch->lpm) {
- /* Find first unequal bit in vpm vs. lpm */
- for (imask = 0x80; imask != 0; imask >>= 1)
- if ((sch->vpm & imask) != (sch->lpm & imask))
- break;
- cdev->private->imask = imask;
- func = (sch->vpm & imask) ?
- SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
- ret = __ccw_device_do_pgid(cdev, func);
+ /* Repeat for all paths. */
+ for (; cdev->private->imask; cdev->private->imask >>= 1,
+ cdev->private->iretry = 5) {
+ if ((cdev->private->imask & sch->schib.pmcw.pam) == 0)
+ /* Path not available, try next. */
+ continue;
+ if (cdev->private->options.pgroup) {
+ if (sch->opm & cdev->private->imask)
+ func = SPID_FUNC_ESTABLISH;
+ else
+ func = SPID_FUNC_RESIGN;
+ ret = __ccw_device_do_pgid(cdev, func);
+ } else
+ ret = __ccw_device_do_nop(cdev);
+ /* We expect an interrupt in case of success or busy
+ * indication. */
if (ret == 0 || ret == -EBUSY)
return;
- cdev->private->iretry = 5;
+ /* Permanent path failure, try next. */
}
- ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
+ /* Done with all paths. */
+ ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV);
}
/*
@@ -318,26 +409,29 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
int ret;
irb = (struct irb *) __LC_IRB;
- /* Retry set pgid for cc=1. */
+
if (irb->scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if (irb->scsw.cc == 1)
+ if (__ccw_device_should_retry(&irb->scsw))
__ccw_device_verify_start(cdev);
return;
}
if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
return;
sch = to_subchannel(cdev->dev.parent);
- ret = __ccw_device_check_pgid(cdev);
+ if (cdev->private->options.pgroup)
+ ret = __ccw_device_check_pgid(cdev);
+ else
+ ret = __ccw_device_check_nop(cdev);
memset(&cdev->private->irb, 0, sizeof(struct irb));
+
switch (ret) {
/* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
case 0:
- /* Establish or Resign Path Group done. Update vpm. */
- if ((sch->lpm & cdev->private->imask) != 0)
- sch->vpm |= cdev->private->imask;
- else
- sch->vpm &= ~cdev->private->imask;
+ /* Path verification ccw finished successfully, update lpm. */
+ sch->vpm |= sch->opm & cdev->private->imask;
+ /* Go on with next path. */
+ cdev->private->imask >>= 1;
cdev->private->iretry = 5;
__ccw_device_verify_start(cdev);
break;
@@ -346,11 +440,14 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
* One of those strange devices which claim to be able
* to do multipathing but not for Set Path Group ID.
*/
- if (cdev->private->flags.pgid_single) {
- ccw_device_verify_done(cdev, -EOPNOTSUPP);
- break;
- }
- cdev->private->flags.pgid_single = 1;
+ if (cdev->private->flags.pgid_single)
+ cdev->private->options.pgroup = 0;
+ else
+ cdev->private->flags.pgid_single = 1;
+ /* Retry */
+ sch->vpm = 0;
+ cdev->private->imask = 0x80;
+ cdev->private->iretry = 5;
/* fall through. */
case -EAGAIN: /* Try again. */
__ccw_device_verify_start(cdev);
@@ -359,8 +456,7 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
ccw_device_verify_done(cdev, -ETIME);
break;
case -EACCES: /* channel is not operational. */
- sch->lpm &= ~cdev->private->imask;
- sch->vpm &= ~cdev->private->imask;
+ cdev->private->imask >>= 1;
cdev->private->iretry = 5;
__ccw_device_verify_start(cdev);
break;
@@ -373,19 +469,17 @@ ccw_device_verify_start(struct ccw_device *cdev)
struct subchannel *sch = to_subchannel(cdev->dev.parent);
cdev->private->flags.pgid_single = 0;
+ cdev->private->imask = 0x80;
cdev->private->iretry = 5;
- /*
- * Update sch->lpm with current values to catch paths becoming
- * available again.
- */
+
+ /* Start with empty vpm. */
+ sch->vpm = 0;
+
+ /* Get current pam. */
if (stsch(sch->schid, &sch->schib)) {
ccw_device_verify_done(cdev, -ENODEV);
return;
}
- sch->lpm = sch->schib.pmcw.pim &
- sch->schib.pmcw.pam &
- sch->schib.pmcw.pom &
- sch->opm;
__ccw_device_verify_start(cdev);
}
@@ -419,10 +513,10 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
int ret;
irb = (struct irb *) __LC_IRB;
- /* Retry set pgid for cc=1. */
+
if (irb->scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if (irb->scsw.cc == 1)
+ if (__ccw_device_should_retry(&irb->scsw))
__ccw_device_disband_start(cdev);
return;
}
@@ -434,7 +528,6 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
switch (ret) {
/* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
case 0: /* disband successful. */
- sch->vpm = 0;
ccw_device_disband_done(cdev, ret);
break;
case -EOPNOTSUPP:
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c
index 6c762b43f921..caf148d5caad 100644
--- a/drivers/s390/cio/device_status.c
+++ b/drivers/s390/cio/device_status.c
@@ -9,7 +9,6 @@
* Status accumulation and basic sense functions.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -68,8 +67,7 @@ ccw_device_path_notoper(struct ccw_device *cdev)
sch->schib.pmcw.pnom);
sch->lpm &= ~sch->schib.pmcw.pnom;
- if (cdev->private->options.pgroup)
- cdev->private->flags.doverify = 1;
+ cdev->private->flags.doverify = 1;
}
/*
@@ -181,7 +179,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb)
cdev_irb->esw.esw0.erw.auth = irb->esw.esw0.erw.auth;
/* Copy path verification required flag. */
cdev_irb->esw.esw0.erw.pvrf = irb->esw.esw0.erw.pvrf;
- if (irb->esw.esw0.erw.pvrf && cdev->private->options.pgroup)
+ if (irb->esw.esw0.erw.pvrf)
cdev->private->flags.doverify = 1;
/* Copy concurrent sense bit. */
cdev_irb->esw.esw0.erw.cons = irb->esw.esw0.erw.cons;
@@ -355,7 +353,7 @@ ccw_device_accumulate_basic_sense(struct ccw_device *cdev, struct irb *irb)
}
/* Check if path verification is required. */
if (ccw_device_accumulate_esw_valid(irb) &&
- irb->esw.esw0.erw.pvrf && cdev->private->options.pgroup)
+ irb->esw.esw0.erw.pvrf)
cdev->private->flags.doverify = 1;
}
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 95a9462f9a91..ad6d82940069 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -25,106 +25,74 @@ struct tpi_info {
static inline int stsch(struct subchannel_id schid,
volatile struct schib *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " stsch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " stsch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int stsch_err(struct subchannel_id schid,
volatile struct schib *addr)
{
- int ccode;
+ register struct subchannel_id reg1 asm ("1") = schid;
+ int ccode = -EIO;
- __asm__ __volatile__(
- " lhi %0,%3\n"
- " lr 1,%1\n"
- " stsch 0(%2)\n"
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " stsch 0(%2)\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifdef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (ccode)
- : "d" (schid), "a" (addr), "K" (-EIO), "m" (*addr)
- : "cc", "1" );
+ EX_TABLE(0b,1b)
+ : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int msch(struct subchannel_id schid,
volatile struct schib *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " msch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " msch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int msch_err(struct subchannel_id schid,
volatile struct schib *addr)
{
- int ccode;
+ register struct subchannel_id reg1 asm ("1") = schid;
+ int ccode = -EIO;
- __asm__ __volatile__(
- " lhi %0,%3\n"
- " lr 1,%1\n"
- " msch 0(%2)\n"
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " msch 0(%2)\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifdef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (ccode)
- : "d" (schid), "a" (addr), "K" (-EIO), "m" (*addr)
- : "cc", "1" );
+ EX_TABLE(0b,1b)
+ : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int tsch(struct subchannel_id schid,
volatile struct irb *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " tsch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " tsch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
@@ -132,89 +100,77 @@ static inline int tpi( volatile struct tpi_info *addr)
{
int ccode;
- __asm__ __volatile__(
- " tpi 0(%1)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " tpi 0(%1)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int ssch(struct subchannel_id schid,
volatile struct orb *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " ssch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " ssch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int rsch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " rsch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " rsch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
static inline int csch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " csch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " csch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
static inline int hsch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " hsch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " hsch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
static inline int xsch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " .insn rre,0xb2760000,%1,0\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " .insn rre,0xb2760000,%1,0\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
@@ -223,41 +179,27 @@ static inline int chsc(void *chsc_area)
typedef struct { char _[4096]; } addr_type;
int cc;
- __asm__ __volatile__ (
- ".insn rre,0xb25f0000,%2,0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
+ asm volatile(
+ " .insn rre,0xb25f0000,%2,0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
: "=d" (cc), "=m" (*(addr_type *) chsc_area)
: "d" (chsc_area), "m" (*(addr_type *) chsc_area)
- : "cc" );
-
+ : "cc");
return cc;
}
-static inline int iac( void)
-{
- int ccode;
-
- __asm__ __volatile__(
- " iac 1\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : : "cc", "1" );
- return ccode;
-}
-
static inline int rchp(int chpid)
{
+ register unsigned int reg1 asm ("1") = chpid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " rchp\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (chpid)
- : "cc", "1" );
+ asm volatile(
+ " lr 1,%1\n"
+ " rchp\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c
index 96f519281d92..cde822d8b5c8 100644
--- a/drivers/s390/cio/qdio.c
+++ b/drivers/s390/cio/qdio.c
@@ -30,7 +30,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -116,7 +115,7 @@ qdio_min(int a,int b)
static inline __u64
qdio_get_micros(void)
{
- return (get_clock() >> 10); /* time>>12 is microseconds */
+ return (get_clock() >> 12); /* time>>12 is microseconds */
}
/*
@@ -1130,7 +1129,7 @@ out:
#ifdef QDIO_USE_PROCESSING_STATE
if (last_position>=0)
- set_slsb(q, &last_position, SLSB_P_INPUT_NOT_INIT, &count);
+ 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));
@@ -2736,7 +2735,7 @@ qdio_free(struct ccw_device *cdev)
QDIO_DBF_TEXT1(0,trace,dbf_text);
QDIO_DBF_TEXT0(0,setup,dbf_text);
- cdev->private->qdio_data = 0;
+ cdev->private->qdio_data = NULL;
up(&irq_ptr->setting_up_sema);
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index ceb3ab31ee08..49bb9e371c32 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -191,49 +191,49 @@ enum qdio_irq_states {
#if QDIO_VERBOSE_LEVEL>8
#define QDIO_PRINT_STUPID(x...) printk( KERN_DEBUG QDIO_PRINTK_HEADER x)
#else
-#define QDIO_PRINT_STUPID(x...)
+#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...)
+#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...)
+#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...)
+#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...)
+#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...)
+#define QDIO_PRINT_CRIT(x...) do { } while (0)
#endif
#if QDIO_VERBOSE_LEVEL>2
#define QDIO_PRINT_ALERT(x...) printk( QDIO_PRINTK_HEADER x)
#else
-#define QDIO_PRINT_ALERT(x...)
+#define QDIO_PRINT_ALERT(x...) do { } while (0)
#endif
#if QDIO_VERBOSE_LEVEL>1
#define QDIO_PRINT_EMERG(x...) printk( QDIO_PRINTK_HEADER x)
#else
-#define QDIO_PRINT_EMERG(x...)
+#define QDIO_PRINT_EMERG(x...) do { } while (0)
#endif
#define HEXDUMP16(importance,header,ptr) \
@@ -274,12 +274,11 @@ do_sqbs(unsigned long sch, unsigned char state, int queue,
register unsigned long _sch asm ("1") = sch;
unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
- asm volatile (
- " .insn rsy,0xeb000000008A,%1,0,0(%2)\n\t"
- : "+d" (_ccq), "+d" (_queuestart)
- : "d" ((unsigned long)state), "d" (_sch)
- : "memory", "cc"
- );
+ 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;
@@ -299,12 +298,11 @@ do_eqbs(unsigned long sch, unsigned char *state, int queue,
unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
unsigned long _state = 0;
- asm volatile (
- " .insn rrf,0xB99c0000,%1,%2,0,0 \n\t"
- : "+d" (_ccq), "+d" (_queuestart), "+d" (_state)
- : "d" (_sch)
- : "memory", "cc"
- );
+ asm volatile(
+ " .insn rrf,0xB99c0000,%1,%2,0,0"
+ : "+d" (_ccq), "+d" (_queuestart), "+d" (_state)
+ : "d" (_sch)
+ : "memory", "cc" );
*count = _ccq & 0xff;
*start = _queuestart & 0xff;
*state = _state & 0xff;
@@ -319,69 +317,35 @@ do_eqbs(unsigned long sch, unsigned char *state, int queue,
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;
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 0,2 \n\t"
- "lr 1,%1 \n\t"
- "lr 2,%2 \n\t"
- "lr 3,%3 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
+ asm volatile(
+ " siga 0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
: "=d" (cc)
- : "d" (schid), "d" (mask1), "d" (mask2)
- : "cc", "0", "1", "2", "3"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "lghi 0,2 \n\t"
- "llgfr 1,%1 \n\t"
- "llgfr 2,%2 \n\t"
- "llgfr 3,%3 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- : "=d" (cc)
- : "d" (schid), "d" (mask1), "d" (mask2)
- : "cc", "0", "1", "2", "3"
- );
-#endif /* CONFIG_64BIT */
+ : "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;
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 0,1 \n\t"
- "lr 1,%1 \n\t"
- "lr 2,%2 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- : "=d" (cc)
- : "d" (schid), "d" (mask)
- : "cc", "0", "1", "2", "memory"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "lghi 0,1 \n\t"
- "llgfr 1,%1 \n\t"
- "llgfr 2,%2 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
+ asm volatile(
+ " siga 0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
: "=d" (cc)
- : "d" (schid), "d" (mask)
- : "cc", "0", "1", "2", "memory"
- );
-#endif /* CONFIG_64BIT */
-
+ : "d" (reg0), "d" (reg1), "d" (reg2) : "cc", "memory");
return cc;
}
@@ -389,93 +353,35 @@ 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;
- __u32 busy_bit;
-
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 0,0 \n\t"
- "lr 1,%2 \n\t"
- "lr 2,%3 \n\t"
- "siga 0 \n\t"
- "0:"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- "srl 0,31 \n\t"
- "lr %1,0 \n\t"
- "1: \n\t"
- ".section .fixup,\"ax\"\n\t"
- "2: \n\t"
- "lhi %0,%4 \n\t"
- "bras 1,3f \n\t"
- ".long 1b \n\t"
- "3: \n\t"
- "l 1,0(1) \n\t"
- "br 1 \n\t"
- ".previous \n\t"
- ".section __ex_table,\"a\"\n\t"
- ".align 4 \n\t"
- ".long 0b,2b \n\t"
- ".previous \n\t"
- : "=d" (cc), "=d" (busy_bit)
- : "d" (schid), "d" (mask),
- "i" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION)
- : "cc", "0", "1", "2", "memory"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "llgfr 0,%5 \n\t"
- "lgr 1,%2 \n\t"
- "llgfr 2,%3 \n\t"
- "siga 0 \n\t"
- "0:"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- "srl 0,31 \n\t"
- "llgfr %1,0 \n\t"
- "1: \n\t"
- ".section .fixup,\"ax\"\n\t"
- "lghi %0,%4 \n\t"
- "jg 1b \n\t"
- ".previous\n\t"
- ".section __ex_table,\"a\"\n\t"
- ".align 8 \n\t"
- ".quad 0b,1b \n\t"
- ".previous \n\t"
- : "=d" (cc), "=d" (busy_bit)
- : "d" (schid), "d" (mask),
- "i" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION), "d" (fc)
- : "cc", "0", "1", "2", "memory"
- );
-#endif /* CONFIG_64BIT */
-
- (*bb) = busy_bit;
+
+ 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)
{
-
- unsigned long time;
-
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 1,3 \n\t"
- ".insn rre,0xb2650000,2,0 \n\t"
- "lr %0,3 \n\t"
- : "=d" (time) : : "cc", "1", "2", "3"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "lghi 1,3 \n\t"
- ".insn rre,0xb2650000,2,0 \n\t"
- "lgr %0,3 \n\t"
- : "=d" (time) : : "cc", "1", "2", "3"
- );
-#endif /* CONFIG_64BIT */
-
- return time;
+ 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;
}
/*
diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile
index 15edebbead7f..f0a12d2eb780 100644
--- a/drivers/s390/crypto/Makefile
+++ b/drivers/s390/crypto/Makefile
@@ -2,5 +2,16 @@
# S/390 crypto devices
#
-z90crypt-objs := z90main.o z90hardware.o
-obj-$(CONFIG_Z90CRYPT) += z90crypt.o
+ifdef CONFIG_ZCRYPT_MONOLITHIC
+
+z90crypt-objs := zcrypt_mono.o ap_bus.o zcrypt_api.o \
+ zcrypt_pcica.o zcrypt_pcicc.o zcrypt_pcixcc.o zcrypt_cex2a.o
+obj-$(CONFIG_ZCRYPT) += z90crypt.o
+
+else
+
+ap-objs := ap_bus.o
+obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o zcrypt_pcicc.o zcrypt_pcixcc.o
+obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o
+
+endif
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
new file mode 100644
index 000000000000..6ed0985c0c91
--- /dev/null
+++ b/drivers/s390/crypto/ap_bus.c
@@ -0,0 +1,1221 @@
+/*
+ * linux/drivers/s390/crypto/ap_bus.c
+ *
+ * Copyright (C) 2006 IBM Corporation
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *
+ * Adjunct processor bus.
+ *
+ * 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/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/notifier.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <asm/s390_rdev.h>
+
+#include "ap_bus.h"
+
+/* Some prototypes. */
+static void ap_scan_bus(void *);
+static void ap_poll_all(unsigned long);
+static void ap_poll_timeout(unsigned long);
+static int ap_poll_thread_start(void);
+static void ap_poll_thread_stop(void);
+
+/**
+ * Module description.
+ */
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("Adjunct Processor Bus driver, "
+ "Copyright 2006 IBM Corporation");
+MODULE_LICENSE("GPL");
+
+/**
+ * Module parameter
+ */
+int ap_domain_index = -1; /* Adjunct Processor Domain Index */
+module_param_named(domain, ap_domain_index, int, 0000);
+MODULE_PARM_DESC(domain, "domain index for ap devices");
+EXPORT_SYMBOL(ap_domain_index);
+
+static int ap_thread_flag = 1;
+module_param_named(poll_thread, ap_thread_flag, int, 0000);
+MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on).");
+
+static struct device *ap_root_device = NULL;
+
+/**
+ * Workqueue & timer for bus rescan.
+ */
+static struct workqueue_struct *ap_work_queue;
+static struct timer_list ap_config_timer;
+static int ap_config_time = AP_CONFIG_TIME;
+static DECLARE_WORK(ap_config_work, ap_scan_bus, NULL);
+
+/**
+ * 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);
+
+/**
+ * Test if ap instructions are available.
+ *
+ * Returns 0 if the ap instructions are installed.
+ */
+static inline int ap_instructions_available(void)
+{
+ register unsigned long reg0 asm ("0") = AP_MKQID(0,0);
+ register unsigned long reg1 asm ("1") = -ENODEV;
+ register unsigned long reg2 asm ("2") = 0UL;
+
+ asm volatile(
+ " .long 0xb2af0000\n" /* PQAP(TAPQ) */
+ "0: la %1,0\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (reg0), "+d" (reg1), "+d" (reg2) : : "cc" );
+ return reg1;
+}
+
+/**
+ * Test adjunct processor queue.
+ * @qid: the ap queue number
+ * @queue_depth: pointer to queue depth value
+ * @device_type: pointer to device type value
+ *
+ * Returns ap queue status structure.
+ */
+static inline struct ap_queue_status
+ap_test_queue(ap_qid_t qid, int *queue_depth, int *device_type)
+{
+ register unsigned long reg0 asm ("0") = qid;
+ register struct ap_queue_status reg1 asm ("1");
+ register unsigned long reg2 asm ("2") = 0UL;
+
+ asm volatile(".long 0xb2af0000" /* PQAP(TAPQ) */
+ : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc");
+ *device_type = (int) (reg2 >> 24);
+ *queue_depth = (int) (reg2 & 0xff);
+ return reg1;
+}
+
+/**
+ * Reset adjunct processor queue.
+ * @qid: the ap queue number
+ *
+ * Returns ap queue status structure.
+ */
+static inline struct ap_queue_status ap_reset_queue(ap_qid_t qid)
+{
+ register unsigned long reg0 asm ("0") = qid | 0x01000000UL;
+ register struct ap_queue_status reg1 asm ("1");
+ register unsigned long reg2 asm ("2") = 0UL;
+
+ asm volatile(
+ ".long 0xb2af0000" /* PQAP(RAPQ) */
+ : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc");
+ return reg1;
+}
+
+/**
+ * Send message to adjunct processor queue.
+ * @qid: the ap queue number
+ * @psmid: the program supplied message identifier
+ * @msg: the message text
+ * @length: the message length
+ *
+ * Returns ap queue status structure.
+ *
+ * Condition code 1 on NQAP can't happen because the L bit is 1.
+ *
+ * Condition code 2 on NQAP also means the send is incomplete,
+ * because a segment boundary was reached. The NQAP is repeated.
+ */
+static inline struct ap_queue_status
+__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
+{
+ typedef struct { char _[length]; } msgblock;
+ register unsigned long reg0 asm ("0") = qid | 0x40000000UL;
+ register struct ap_queue_status reg1 asm ("1");
+ register unsigned long reg2 asm ("2") = (unsigned long) msg;
+ register unsigned long reg3 asm ("3") = (unsigned long) length;
+ register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32);
+ register unsigned long reg5 asm ("5") = (unsigned int) psmid;
+
+ asm volatile (
+ "0: .long 0xb2ad0042\n" /* DQAP */
+ " brc 2,0b"
+ : "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3)
+ : "d" (reg4), "d" (reg5), "m" (*(msgblock *) msg)
+ : "cc" );
+ return reg1;
+}
+
+int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
+{
+ struct ap_queue_status status;
+
+ status = __ap_send(qid, psmid, msg, length);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ return 0;
+ case AP_RESPONSE_Q_FULL:
+ return -EBUSY;
+ default: /* Device is gone. */
+ return -ENODEV;
+ }
+}
+EXPORT_SYMBOL(ap_send);
+
+/*
+ * Receive message from adjunct processor queue.
+ * @qid: the ap queue number
+ * @psmid: pointer to program supplied message identifier
+ * @msg: the message text
+ * @length: the message length
+ *
+ * Returns ap queue status structure.
+ *
+ * Condition code 1 on DQAP means the receive has taken place
+ * but only partially. The response is incomplete, hence the
+ * DQAP is repeated.
+ *
+ * Condition code 2 on DQAP also means the receive is incomplete,
+ * this time because a segment boundary was reached. Again, the
+ * DQAP is repeated.
+ *
+ * Note that gpr2 is used by the DQAP instruction to keep track of
+ * any 'residual' length, in case the instruction gets interrupted.
+ * Hence it gets zeroed before the instruction.
+ */
+static inline struct ap_queue_status
+__ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length)
+{
+ typedef struct { char _[length]; } msgblock;
+ register unsigned long reg0 asm("0") = qid | 0x80000000UL;
+ register struct ap_queue_status reg1 asm ("1");
+ register unsigned long reg2 asm("2") = 0UL;
+ register unsigned long reg4 asm("4") = (unsigned long) msg;
+ register unsigned long reg5 asm("5") = (unsigned long) length;
+ register unsigned long reg6 asm("6") = 0UL;
+ register unsigned long reg7 asm("7") = 0UL;
+
+
+ asm volatile(
+ "0: .long 0xb2ae0064\n"
+ " brc 6,0b\n"
+ : "+d" (reg0), "=d" (reg1), "+d" (reg2),
+ "+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7),
+ "=m" (*(msgblock *) msg) : : "cc" );
+ *psmid = (((unsigned long long) reg6) << 32) + reg7;
+ return reg1;
+}
+
+int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length)
+{
+ struct ap_queue_status status;
+
+ status = __ap_recv(qid, psmid, msg, length);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ return 0;
+ case AP_RESPONSE_NO_PENDING_REPLY:
+ if (status.queue_empty)
+ return -ENOENT;
+ return -EBUSY;
+ default:
+ return -ENODEV;
+ }
+}
+EXPORT_SYMBOL(ap_recv);
+
+/**
+ * Check if an AP queue is available. The test is repeated for
+ * AP_MAX_RESET times.
+ * @qid: the ap queue number
+ * @queue_depth: pointer to queue depth value
+ * @device_type: pointer to device type value
+ */
+static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type)
+{
+ struct ap_queue_status status;
+ int t_depth, t_device_type, rc, i;
+
+ rc = -EBUSY;
+ for (i = 0; i < AP_MAX_RESET; i++) {
+ status = ap_test_queue(qid, &t_depth, &t_device_type);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ *queue_depth = t_depth + 1;
+ *device_type = t_device_type;
+ rc = 0;
+ break;
+ case AP_RESPONSE_Q_NOT_AVAIL:
+ rc = -ENODEV;
+ break;
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ break;
+ case AP_RESPONSE_DECONFIGURED:
+ rc = -ENODEV;
+ break;
+ case AP_RESPONSE_CHECKSTOPPED:
+ rc = -ENODEV;
+ break;
+ case AP_RESPONSE_BUSY:
+ break;
+ default:
+ BUG();
+ }
+ if (rc != -EBUSY)
+ break;
+ if (i < AP_MAX_RESET - 1)
+ udelay(5);
+ }
+ return rc;
+}
+
+/**
+ * Reset an AP queue and wait for it to become available again.
+ * @qid: the ap queue number
+ */
+static int ap_init_queue(ap_qid_t qid)
+{
+ struct ap_queue_status status;
+ int rc, dummy, i;
+
+ rc = -ENODEV;
+ status = ap_reset_queue(qid);
+ for (i = 0; i < AP_MAX_RESET; i++) {
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ if (status.queue_empty)
+ rc = 0;
+ break;
+ case AP_RESPONSE_Q_NOT_AVAIL:
+ case AP_RESPONSE_DECONFIGURED:
+ case AP_RESPONSE_CHECKSTOPPED:
+ i = AP_MAX_RESET; /* return with -ENODEV */
+ break;
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ case AP_RESPONSE_BUSY:
+ default:
+ break;
+ }
+ if (rc != -ENODEV)
+ break;
+ if (i < AP_MAX_RESET - 1) {
+ udelay(5);
+ status = ap_test_queue(qid, &dummy, &dummy);
+ }
+ }
+ return rc;
+}
+
+/**
+ * AP device related attributes.
+ */
+static ssize_t ap_hwtype_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->device_type);
+}
+static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL);
+
+static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->queue_depth);
+}
+static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL);
+
+static ssize_t ap_request_count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ int rc;
+
+ spin_lock_bh(&ap_dev->lock);
+ rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->total_request_count);
+ spin_unlock_bh(&ap_dev->lock);
+ return rc;
+}
+
+static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL);
+
+static ssize_t ap_modalias_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "ap:t%02X", to_ap_dev(dev)->device_type);
+}
+
+static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL);
+
+static struct attribute *ap_dev_attrs[] = {
+ &dev_attr_hwtype.attr,
+ &dev_attr_depth.attr,
+ &dev_attr_request_count.attr,
+ &dev_attr_modalias.attr,
+ NULL
+};
+static struct attribute_group ap_dev_attr_group = {
+ .attrs = ap_dev_attrs
+};
+
+/**
+ * AP bus driver registration/unregistration.
+ */
+static int ap_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ struct ap_driver *ap_drv = to_ap_drv(drv);
+ struct ap_device_id *id;
+
+ /**
+ * Compare device type of the device with the list of
+ * supported types of the device_driver.
+ */
+ for (id = ap_drv->ids; id->match_flags; id++) {
+ if ((id->match_flags & AP_DEVICE_ID_MATCH_DEVICE_TYPE) &&
+ (id->dev_type != ap_dev->device_type))
+ continue;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * uevent function for AP devices. It sets up a single environment
+ * variable DEV_TYPE which contains the hardware device type.
+ */
+static int ap_uevent (struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ int length;
+
+ if (!ap_dev)
+ return -ENODEV;
+
+ /* Set up DEV_TYPE environment variable. */
+ envp[0] = buffer;
+ length = scnprintf(buffer, buffer_size, "DEV_TYPE=%04X",
+ ap_dev->device_type);
+ if (buffer_size - length <= 0)
+ return -ENOMEM;
+ envp[1] = 0;
+ return 0;
+}
+
+static struct bus_type ap_bus_type = {
+ .name = "ap",
+ .match = &ap_bus_match,
+ .uevent = &ap_uevent,
+};
+
+static int ap_device_probe(struct device *dev)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ struct ap_driver *ap_drv = to_ap_drv(dev->driver);
+ int rc;
+
+ ap_dev->drv = ap_drv;
+ rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV;
+ if (rc)
+ ap_dev->unregistered = 1;
+ return rc;
+}
+
+/**
+ * Flush all requests from the request/pending queue of an AP device.
+ * @ap_dev: pointer to the AP device.
+ */
+static inline void __ap_flush_queue(struct ap_device *ap_dev)
+{
+ struct ap_message *ap_msg, *next;
+
+ list_for_each_entry_safe(ap_msg, next, &ap_dev->pendingq, list) {
+ list_del_init(&ap_msg->list);
+ ap_dev->pendingq_count--;
+ ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+ }
+ list_for_each_entry_safe(ap_msg, next, &ap_dev->requestq, list) {
+ list_del_init(&ap_msg->list);
+ ap_dev->requestq_count--;
+ ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+ }
+}
+
+void ap_flush_queue(struct ap_device *ap_dev)
+{
+ spin_lock_bh(&ap_dev->lock);
+ __ap_flush_queue(ap_dev);
+ spin_unlock_bh(&ap_dev->lock);
+}
+EXPORT_SYMBOL(ap_flush_queue);
+
+static int ap_device_remove(struct device *dev)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ struct ap_driver *ap_drv = ap_dev->drv;
+
+ spin_lock_bh(&ap_dev->lock);
+ __ap_flush_queue(ap_dev);
+ /**
+ * set ->unregistered to 1 while holding the lock. This prevents
+ * new messages to be put on the queue from now on.
+ */
+ ap_dev->unregistered = 1;
+ spin_unlock_bh(&ap_dev->lock);
+ if (ap_drv->remove)
+ ap_drv->remove(ap_dev);
+ return 0;
+}
+
+int ap_driver_register(struct ap_driver *ap_drv, struct module *owner,
+ char *name)
+{
+ struct device_driver *drv = &ap_drv->driver;
+
+ drv->bus = &ap_bus_type;
+ drv->probe = ap_device_probe;
+ drv->remove = ap_device_remove;
+ drv->owner = owner;
+ drv->name = name;
+ return driver_register(drv);
+}
+EXPORT_SYMBOL(ap_driver_register);
+
+void ap_driver_unregister(struct ap_driver *ap_drv)
+{
+ driver_unregister(&ap_drv->driver);
+}
+EXPORT_SYMBOL(ap_driver_unregister);
+
+/**
+ * AP bus attributes.
+ */
+static ssize_t ap_domain_show(struct bus_type *bus, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", ap_domain_index);
+}
+
+static BUS_ATTR(ap_domain, 0444, ap_domain_show, NULL);
+
+static ssize_t ap_config_time_show(struct bus_type *bus, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time);
+}
+
+static ssize_t ap_config_time_store(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ int time;
+
+ if (sscanf(buf, "%d\n", &time) != 1 || time < 5 || time > 120)
+ return -EINVAL;
+ ap_config_time = time;
+ if (!timer_pending(&ap_config_timer) ||
+ !mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ)) {
+ ap_config_timer.expires = jiffies + ap_config_time * HZ;
+ add_timer(&ap_config_timer);
+ }
+ return count;
+}
+
+static BUS_ATTR(config_time, 0644, ap_config_time_show, ap_config_time_store);
+
+static ssize_t ap_poll_thread_show(struct bus_type *bus, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", ap_poll_kthread ? 1 : 0);
+}
+
+static ssize_t ap_poll_thread_store(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ int flag, rc;
+
+ if (sscanf(buf, "%d\n", &flag) != 1)
+ return -EINVAL;
+ if (flag) {
+ rc = ap_poll_thread_start();
+ if (rc)
+ return rc;
+ }
+ else
+ ap_poll_thread_stop();
+ return count;
+}
+
+static BUS_ATTR(poll_thread, 0644, ap_poll_thread_show, ap_poll_thread_store);
+
+static struct bus_attribute *const ap_bus_attrs[] = {
+ &bus_attr_ap_domain,
+ &bus_attr_config_time,
+ &bus_attr_poll_thread,
+ NULL
+};
+
+/**
+ * Pick one of the 16 ap domains.
+ */
+static inline int ap_select_domain(void)
+{
+ int queue_depth, device_type, count, max_count, best_domain;
+ int rc, i, j;
+
+ /**
+ * We want to use a single domain. Either the one specified with
+ * the "domain=" parameter or the domain with the maximum number
+ * of devices.
+ */
+ if (ap_domain_index >= 0 && ap_domain_index < AP_DOMAINS)
+ /* Domain has already been selected. */
+ return 0;
+ best_domain = -1;
+ max_count = 0;
+ for (i = 0; i < AP_DOMAINS; i++) {
+ count = 0;
+ for (j = 0; j < AP_DEVICES; j++) {
+ ap_qid_t qid = AP_MKQID(j, i);
+ rc = ap_query_queue(qid, &queue_depth, &device_type);
+ if (rc)
+ continue;
+ count++;
+ }
+ if (count > max_count) {
+ max_count = count;
+ best_domain = i;
+ }
+ }
+ if (best_domain >= 0){
+ ap_domain_index = best_domain;
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/**
+ * Find the device type if query queue returned a device type of 0.
+ * @ap_dev: pointer to the AP device.
+ */
+static int ap_probe_device_type(struct ap_device *ap_dev)
+{
+ static unsigned char msg[] = {
+ 0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x00,0x43,0x43,0x41,0x2d,0x41,0x50,
+ 0x50,0x4c,0x20,0x20,0x20,0x01,0x01,0x01,
+ 0x00,0x00,0x00,0x00,0x50,0x4b,0x00,0x00,
+ 0x00,0x00,0x01,0x1c,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x05,0xb8,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x70,0x00,0x41,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x54,0x32,0x01,0x00,0xa0,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0xb8,0x05,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,
+ 0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20,
+ 0x50,0x4b,0x0a,0x00,0x50,0x4b,0x43,0x53,
+ 0x2d,0x31,0x2e,0x32,0x37,0x00,0x11,0x22,
+ 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,
+ 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,
+ 0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66,
+ 0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,
+ 0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,
+ 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,
+ 0x11,0x22,0x33,0x5d,0x00,0x5b,0x00,0x77,
+ 0x88,0x1e,0x00,0x00,0x57,0x00,0x00,0x00,
+ 0x00,0x04,0x00,0x00,0x4f,0x00,0x00,0x00,
+ 0x03,0x02,0x00,0x00,0x40,0x01,0x00,0x01,
+ 0xce,0x02,0x68,0x2d,0x5f,0xa9,0xde,0x0c,
+ 0xf6,0xd2,0x7b,0x58,0x4b,0xf9,0x28,0x68,
+ 0x3d,0xb4,0xf4,0xef,0x78,0xd5,0xbe,0x66,
+ 0x63,0x42,0xef,0xf8,0xfd,0xa4,0xf8,0xb0,
+ 0x8e,0x29,0xc2,0xc9,0x2e,0xd8,0x45,0xb8,
+ 0x53,0x8c,0x6f,0x4e,0x72,0x8f,0x6c,0x04,
+ 0x9c,0x88,0xfc,0x1e,0xc5,0x83,0x55,0x57,
+ 0xf7,0xdd,0xfd,0x4f,0x11,0x36,0x95,0x5d,
+ };
+ struct ap_queue_status status;
+ unsigned long long psmid;
+ char *reply;
+ int rc, i;
+
+ reply = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!reply) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ status = __ap_send(ap_dev->qid, 0x0102030405060708ULL,
+ msg, sizeof(msg));
+ if (status.response_code != AP_RESPONSE_NORMAL) {
+ rc = -ENODEV;
+ goto out_free;
+ }
+
+ /* Wait for the test message to complete. */
+ for (i = 0; i < 6; i++) {
+ mdelay(300);
+ status = __ap_recv(ap_dev->qid, &psmid, reply, 4096);
+ if (status.response_code == AP_RESPONSE_NORMAL &&
+ psmid == 0x0102030405060708ULL)
+ break;
+ }
+ if (i < 6) {
+ /* Got an answer. */
+ if (reply[0] == 0x00 && reply[1] == 0x86)
+ ap_dev->device_type = AP_DEVICE_TYPE_PCICC;
+ else
+ ap_dev->device_type = AP_DEVICE_TYPE_PCICA;
+ rc = 0;
+ } else
+ rc = -ENODEV;
+
+out_free:
+ free_page((unsigned long) reply);
+out:
+ return rc;
+}
+
+/**
+ * Scan the ap bus for new devices.
+ */
+static int __ap_scan_bus(struct device *dev, void *data)
+{
+ return to_ap_dev(dev)->qid == (ap_qid_t)(unsigned long) data;
+}
+
+static void ap_device_release(struct device *dev)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+
+ kfree(ap_dev);
+}
+
+static void ap_scan_bus(void *data)
+{
+ struct ap_device *ap_dev;
+ struct device *dev;
+ ap_qid_t qid;
+ int queue_depth, device_type;
+ int rc, i;
+
+ if (ap_select_domain() != 0)
+ return;
+ for (i = 0; i < AP_DEVICES; i++) {
+ qid = AP_MKQID(i, ap_domain_index);
+ dev = bus_find_device(&ap_bus_type, NULL,
+ (void *)(unsigned long)qid,
+ __ap_scan_bus);
+ if (dev) {
+ put_device(dev);
+ continue;
+ }
+ rc = ap_query_queue(qid, &queue_depth, &device_type);
+ if (rc)
+ continue;
+ rc = ap_init_queue(qid);
+ if (rc)
+ continue;
+ ap_dev = kzalloc(sizeof(*ap_dev), GFP_KERNEL);
+ if (!ap_dev)
+ break;
+ ap_dev->qid = qid;
+ ap_dev->queue_depth = queue_depth;
+ spin_lock_init(&ap_dev->lock);
+ INIT_LIST_HEAD(&ap_dev->pendingq);
+ INIT_LIST_HEAD(&ap_dev->requestq);
+ if (device_type == 0)
+ ap_probe_device_type(ap_dev);
+ else
+ ap_dev->device_type = device_type;
+
+ ap_dev->device.bus = &ap_bus_type;
+ ap_dev->device.parent = ap_root_device;
+ snprintf(ap_dev->device.bus_id, BUS_ID_SIZE, "card%02x",
+ AP_QID_DEVICE(ap_dev->qid));
+ ap_dev->device.release = ap_device_release;
+ rc = device_register(&ap_dev->device);
+ if (rc) {
+ kfree(ap_dev);
+ continue;
+ }
+ /* Add device attributes. */
+ rc = sysfs_create_group(&ap_dev->device.kobj,
+ &ap_dev_attr_group);
+ if (rc)
+ device_unregister(&ap_dev->device);
+ }
+}
+
+static void
+ap_config_timeout(unsigned long ptr)
+{
+ queue_work(ap_work_queue, &ap_config_work);
+ ap_config_timer.expires = jiffies + ap_config_time * HZ;
+ add_timer(&ap_config_timer);
+}
+
+/**
+ * Set up the timer to run the poll tasklet
+ */
+static inline void ap_schedule_poll_timer(void)
+{
+ if (timer_pending(&ap_poll_timer))
+ return;
+ mod_timer(&ap_poll_timer, jiffies + AP_POLL_TIME);
+}
+
+/**
+ * Receive pending reply messages from an AP device.
+ * @ap_dev: pointer to the AP device
+ * @flags: pointer to control flags, bit 2^0 is set if another poll is
+ * required, bit 2^1 is set if the poll timer needs to get armed
+ * Returns 0 if the device is still present, -ENODEV if not.
+ */
+static inline int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags)
+{
+ struct ap_queue_status status;
+ struct ap_message *ap_msg;
+
+ if (ap_dev->queue_count <= 0)
+ return 0;
+ status = __ap_recv(ap_dev->qid, &ap_dev->reply->psmid,
+ ap_dev->reply->message, ap_dev->reply->length);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ atomic_dec(&ap_poll_requests);
+ ap_dev->queue_count--;
+ list_for_each_entry(ap_msg, &ap_dev->pendingq, list) {
+ if (ap_msg->psmid != ap_dev->reply->psmid)
+ continue;
+ list_del_init(&ap_msg->list);
+ ap_dev->pendingq_count--;
+ ap_dev->drv->receive(ap_dev, ap_msg, ap_dev->reply);
+ break;
+ }
+ if (ap_dev->queue_count > 0)
+ *flags |= 1;
+ break;
+ case AP_RESPONSE_NO_PENDING_REPLY:
+ if (status.queue_empty) {
+ /* The card shouldn't forget requests but who knows. */
+ ap_dev->queue_count = 0;
+ list_splice_init(&ap_dev->pendingq, &ap_dev->requestq);
+ ap_dev->requestq_count += ap_dev->pendingq_count;
+ ap_dev->pendingq_count = 0;
+ } else
+ *flags |= 2;
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/**
+ * Send messages from the request queue to an AP device.
+ * @ap_dev: pointer to the AP device
+ * @flags: pointer to control flags, bit 2^0 is set if another poll is
+ * required, bit 2^1 is set if the poll timer needs to get armed
+ * Returns 0 if the device is still present, -ENODEV if not.
+ */
+static inline int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
+{
+ struct ap_queue_status status;
+ struct ap_message *ap_msg;
+
+ if (ap_dev->requestq_count <= 0 ||
+ ap_dev->queue_count >= ap_dev->queue_depth)
+ return 0;
+ /* Start the next request on the queue. */
+ ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list);
+ status = __ap_send(ap_dev->qid, ap_msg->psmid,
+ ap_msg->message, ap_msg->length);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ atomic_inc(&ap_poll_requests);
+ ap_dev->queue_count++;
+ list_move_tail(&ap_msg->list, &ap_dev->pendingq);
+ ap_dev->requestq_count--;
+ ap_dev->pendingq_count++;
+ if (ap_dev->queue_count < ap_dev->queue_depth &&
+ ap_dev->requestq_count > 0)
+ *flags |= 1;
+ *flags |= 2;
+ break;
+ case AP_RESPONSE_Q_FULL:
+ *flags |= 2;
+ break;
+ case AP_RESPONSE_MESSAGE_TOO_BIG:
+ return -EINVAL;
+ default:
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/**
+ * Poll AP device for pending replies and send new messages. If either
+ * ap_poll_read or ap_poll_write returns -ENODEV unregister the device.
+ * @ap_dev: pointer to the bus device
+ * @flags: pointer to control flags, bit 2^0 is set if another poll is
+ * required, bit 2^1 is set if the poll timer needs to get armed
+ * Returns 0.
+ */
+static inline int ap_poll_queue(struct ap_device *ap_dev, unsigned long *flags)
+{
+ int rc;
+
+ rc = ap_poll_read(ap_dev, flags);
+ if (rc)
+ return rc;
+ return ap_poll_write(ap_dev, flags);
+}
+
+/**
+ * Queue a message to a device.
+ * @ap_dev: pointer to the AP device
+ * @ap_msg: the message to be queued
+ */
+static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+ struct ap_queue_status status;
+
+ if (list_empty(&ap_dev->requestq) &&
+ ap_dev->queue_count < ap_dev->queue_depth) {
+ status = __ap_send(ap_dev->qid, ap_msg->psmid,
+ ap_msg->message, ap_msg->length);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ list_add_tail(&ap_msg->list, &ap_dev->pendingq);
+ atomic_inc(&ap_poll_requests);
+ ap_dev->pendingq_count++;
+ ap_dev->queue_count++;
+ ap_dev->total_request_count++;
+ break;
+ case AP_RESPONSE_Q_FULL:
+ list_add_tail(&ap_msg->list, &ap_dev->requestq);
+ ap_dev->requestq_count++;
+ ap_dev->total_request_count++;
+ return -EBUSY;
+ case AP_RESPONSE_MESSAGE_TOO_BIG:
+ ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL));
+ return -EINVAL;
+ default: /* Device is gone. */
+ ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+ return -ENODEV;
+ }
+ } else {
+ list_add_tail(&ap_msg->list, &ap_dev->requestq);
+ ap_dev->requestq_count++;
+ ap_dev->total_request_count++;
+ return -EBUSY;
+ }
+ ap_schedule_poll_timer();
+ return 0;
+}
+
+void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+ unsigned long flags;
+ int rc;
+
+ spin_lock_bh(&ap_dev->lock);
+ if (!ap_dev->unregistered) {
+ /* Make room on the queue by polling for finished requests. */
+ rc = ap_poll_queue(ap_dev, &flags);
+ if (!rc)
+ rc = __ap_queue_message(ap_dev, ap_msg);
+ if (!rc)
+ wake_up(&ap_poll_wait);
+ } else {
+ ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+ rc = 0;
+ }
+ spin_unlock_bh(&ap_dev->lock);
+ if (rc == -ENODEV)
+ device_unregister(&ap_dev->device);
+}
+EXPORT_SYMBOL(ap_queue_message);
+
+/**
+ * Cancel a crypto request. This is done by removing the request
+ * from the devive pendingq or requestq queue. Note that the
+ * request stays on the AP queue. When it finishes the message
+ * reply will be discarded because the psmid can't be found.
+ * @ap_dev: AP device that has the message queued
+ * @ap_msg: the message that is to be removed
+ */
+void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+ struct ap_message *tmp;
+
+ spin_lock_bh(&ap_dev->lock);
+ if (!list_empty(&ap_msg->list)) {
+ list_for_each_entry(tmp, &ap_dev->pendingq, list)
+ if (tmp->psmid == ap_msg->psmid) {
+ ap_dev->pendingq_count--;
+ goto found;
+ }
+ ap_dev->requestq_count--;
+ found:
+ list_del_init(&ap_msg->list);
+ }
+ spin_unlock_bh(&ap_dev->lock);
+}
+EXPORT_SYMBOL(ap_cancel_message);
+
+/**
+ * AP receive polling for finished AP requests
+ */
+static void ap_poll_timeout(unsigned long unused)
+{
+ tasklet_schedule(&ap_tasklet);
+}
+
+/**
+ * Poll all AP devices on the bus in a round robin fashion. Continue
+ * polling until bit 2^0 of the control flags is not set. If bit 2^1
+ * of the control flags has been set arm the poll timer.
+ */
+static int __ap_poll_all(struct device *dev, void *data)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ int rc;
+
+ spin_lock(&ap_dev->lock);
+ if (!ap_dev->unregistered) {
+ rc = ap_poll_queue(to_ap_dev(dev), (unsigned long *) data);
+ } else
+ rc = 0;
+ spin_unlock(&ap_dev->lock);
+ if (rc)
+ device_unregister(&ap_dev->device);
+ return 0;
+}
+
+static void ap_poll_all(unsigned long dummy)
+{
+ unsigned long flags;
+
+ do {
+ flags = 0;
+ bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all);
+ } while (flags & 1);
+ if (flags & 2)
+ ap_schedule_poll_timer();
+}
+
+/**
+ * AP bus poll thread. The purpose of this thread is to poll for
+ * finished requests in a loop if there is a "free" cpu - that is
+ * a cpu that doesn't have anything better to do. The polling stops
+ * as soon as there is another task or if all messages have been
+ * delivered.
+ */
+static int ap_poll_thread(void *data)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long flags;
+ int requests;
+
+ set_user_nice(current, -20);
+ while (1) {
+ if (need_resched()) {
+ schedule();
+ continue;
+ }
+ add_wait_queue(&ap_poll_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (kthread_should_stop())
+ break;
+ requests = atomic_read(&ap_poll_requests);
+ if (requests <= 0)
+ schedule();
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&ap_poll_wait, &wait);
+
+ local_bh_disable();
+ flags = 0;
+ bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all);
+ local_bh_enable();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&ap_poll_wait, &wait);
+ return 0;
+}
+
+static int ap_poll_thread_start(void)
+{
+ int rc;
+
+ mutex_lock(&ap_poll_thread_mutex);
+ if (!ap_poll_kthread) {
+ ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
+ rc = IS_ERR(ap_poll_kthread) ? PTR_ERR(ap_poll_kthread) : 0;
+ if (rc)
+ ap_poll_kthread = NULL;
+ }
+ else
+ rc = 0;
+ mutex_unlock(&ap_poll_thread_mutex);
+ return rc;
+}
+
+static void ap_poll_thread_stop(void)
+{
+ mutex_lock(&ap_poll_thread_mutex);
+ if (ap_poll_kthread) {
+ kthread_stop(ap_poll_kthread);
+ ap_poll_kthread = NULL;
+ }
+ mutex_unlock(&ap_poll_thread_mutex);
+}
+
+/**
+ * The module initialization code.
+ */
+int __init ap_module_init(void)
+{
+ int rc, i;
+
+ if (ap_domain_index < -1 || ap_domain_index >= AP_DOMAINS) {
+ printk(KERN_WARNING "Invalid param: domain = %d. "
+ " Not loading.\n", ap_domain_index);
+ return -EINVAL;
+ }
+ if (ap_instructions_available() != 0) {
+ printk(KERN_WARNING "AP instructions not installed.\n");
+ return -ENODEV;
+ }
+
+ /* Create /sys/bus/ap. */
+ rc = bus_register(&ap_bus_type);
+ if (rc)
+ goto out;
+ for (i = 0; ap_bus_attrs[i]; i++) {
+ rc = bus_create_file(&ap_bus_type, ap_bus_attrs[i]);
+ if (rc)
+ goto out_bus;
+ }
+
+ /* Create /sys/devices/ap. */
+ ap_root_device = s390_root_dev_register("ap");
+ rc = IS_ERR(ap_root_device) ? PTR_ERR(ap_root_device) : 0;
+ if (rc)
+ goto out_bus;
+
+ ap_work_queue = create_singlethread_workqueue("kapwork");
+ if (!ap_work_queue) {
+ rc = -ENOMEM;
+ goto out_root;
+ }
+
+ if (ap_select_domain() == 0)
+ ap_scan_bus(NULL);
+
+ /* Setup the ap bus rescan timer. */
+ init_timer(&ap_config_timer);
+ ap_config_timer.function = ap_config_timeout;
+ ap_config_timer.data = 0;
+ ap_config_timer.expires = jiffies + ap_config_time * HZ;
+ add_timer(&ap_config_timer);
+
+ /* Start the low priority AP bus poll thread. */
+ if (ap_thread_flag) {
+ rc = ap_poll_thread_start();
+ if (rc)
+ goto out_work;
+ }
+
+ return 0;
+
+out_work:
+ del_timer_sync(&ap_config_timer);
+ del_timer_sync(&ap_poll_timer);
+ destroy_workqueue(ap_work_queue);
+out_root:
+ s390_root_dev_unregister(ap_root_device);
+out_bus:
+ while (i--)
+ bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
+ bus_unregister(&ap_bus_type);
+out:
+ return rc;
+}
+
+static int __ap_match_all(struct device *dev, void *data)
+{
+ return 1;
+}
+
+/**
+ * The module termination code
+ */
+void ap_module_exit(void)
+{
+ int i;
+ struct device *dev;
+
+ ap_poll_thread_stop();
+ del_timer_sync(&ap_config_timer);
+ del_timer_sync(&ap_poll_timer);
+ destroy_workqueue(ap_work_queue);
+ s390_root_dev_unregister(ap_root_device);
+ while ((dev = bus_find_device(&ap_bus_type, NULL, NULL,
+ __ap_match_all)))
+ {
+ device_unregister(dev);
+ put_device(dev);
+ }
+ for (i = 0; ap_bus_attrs[i]; i++)
+ bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
+ bus_unregister(&ap_bus_type);
+}
+
+#ifndef CONFIG_ZCRYPT_MONOLITHIC
+module_init(ap_module_init);
+module_exit(ap_module_exit);
+#endif
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
new file mode 100644
index 000000000000..83b69c01cd6e
--- /dev/null
+++ b/drivers/s390/crypto/ap_bus.h
@@ -0,0 +1,158 @@
+/*
+ * linux/drivers/s390/crypto/ap_bus.h
+ *
+ * Copyright (C) 2006 IBM Corporation
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *
+ * Adjunct processor bus header file.
+ *
+ * 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.
+ */
+
+#ifndef _AP_BUS_H_
+#define _AP_BUS_H_
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/types.h>
+
+#define AP_DEVICES 64 /* Number of AP devices. */
+#define AP_DOMAINS 16 /* Number of AP domains. */
+#define AP_MAX_RESET 90 /* Maximum number of resets. */
+#define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */
+#define AP_POLL_TIME 1 /* Time in ticks between receive polls. */
+
+extern int ap_domain_index;
+
+/**
+ * The ap_qid_t identifier of an ap queue. It contains a
+ * 6 bit device index and a 4 bit queue index (domain).
+ */
+typedef unsigned int ap_qid_t;
+
+#define AP_MKQID(_device,_queue) (((_device) & 63) << 8 | ((_queue) & 15))
+#define AP_QID_DEVICE(_qid) (((_qid) >> 8) & 63)
+#define AP_QID_QUEUE(_qid) ((_qid) & 15)
+
+/**
+ * The ap queue status word is returned by all three AP functions
+ * (PQAP, NQAP and DQAP). There's a set of flags in the first
+ * byte, followed by a 1 byte response code.
+ */
+struct ap_queue_status {
+ unsigned int queue_empty : 1;
+ unsigned int replies_waiting : 1;
+ unsigned int queue_full : 1;
+ unsigned int pad1 : 5;
+ unsigned int response_code : 8;
+ unsigned int pad2 : 16;
+};
+
+#define AP_RESPONSE_NORMAL 0x00
+#define AP_RESPONSE_Q_NOT_AVAIL 0x01
+#define AP_RESPONSE_RESET_IN_PROGRESS 0x02
+#define AP_RESPONSE_DECONFIGURED 0x03
+#define AP_RESPONSE_CHECKSTOPPED 0x04
+#define AP_RESPONSE_BUSY 0x05
+#define AP_RESPONSE_Q_FULL 0x10
+#define AP_RESPONSE_NO_PENDING_REPLY 0x10
+#define AP_RESPONSE_INDEX_TOO_BIG 0x11
+#define AP_RESPONSE_NO_FIRST_PART 0x13
+#define AP_RESPONSE_MESSAGE_TOO_BIG 0x15
+
+/**
+ * Known device types
+ */
+#define AP_DEVICE_TYPE_PCICC 3
+#define AP_DEVICE_TYPE_PCICA 4
+#define AP_DEVICE_TYPE_PCIXCC 5
+#define AP_DEVICE_TYPE_CEX2A 6
+#define AP_DEVICE_TYPE_CEX2C 7
+
+struct ap_device;
+struct ap_message;
+
+struct ap_driver {
+ struct device_driver driver;
+ struct ap_device_id *ids;
+
+ int (*probe)(struct ap_device *);
+ void (*remove)(struct ap_device *);
+ /* receive is called from tasklet context */
+ void (*receive)(struct ap_device *, struct ap_message *,
+ struct ap_message *);
+};
+
+#define to_ap_drv(x) container_of((x), struct ap_driver, driver)
+
+int ap_driver_register(struct ap_driver *, struct module *, char *);
+void ap_driver_unregister(struct ap_driver *);
+
+struct ap_device {
+ struct device device;
+ struct ap_driver *drv; /* Pointer to AP device driver. */
+ spinlock_t lock; /* Per device lock. */
+
+ ap_qid_t qid; /* AP queue id. */
+ int queue_depth; /* AP queue depth.*/
+ int device_type; /* AP device type. */
+ int unregistered; /* marks AP device as unregistered */
+
+ int queue_count; /* # messages currently on AP queue. */
+
+ struct list_head pendingq; /* List of message sent to AP queue. */
+ int pendingq_count; /* # requests on pendingq list. */
+ struct list_head requestq; /* List of message yet to be sent. */
+ int requestq_count; /* # requests on requestq list. */
+ int total_request_count; /* # requests ever for this AP device. */
+
+ struct ap_message *reply; /* Per device reply message. */
+
+ void *private; /* ap driver private pointer. */
+};
+
+#define to_ap_dev(x) container_of((x), struct ap_device, device)
+
+struct ap_message {
+ struct list_head list; /* Request queueing. */
+ unsigned long long psmid; /* Message id. */
+ void *message; /* Pointer to message buffer. */
+ size_t length; /* Message length. */
+
+ void *private; /* ap driver private pointer. */
+};
+
+#define AP_DEVICE(dt) \
+ .dev_type=(dt), \
+ .match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE,
+
+/**
+ * Note: don't use ap_send/ap_recv after using ap_queue_message
+ * for the first time. Otherwise the ap message queue will get
+ * confused.
+ */
+int ap_send(ap_qid_t, unsigned long long, void *, size_t);
+int ap_recv(ap_qid_t, unsigned long long *, void *, size_t);
+
+void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg);
+void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg);
+void ap_flush_queue(struct ap_device *ap_dev);
+
+int ap_module_init(void);
+void ap_module_exit(void);
+
+#endif /* _AP_BUS_H_ */
diff --git a/drivers/s390/crypto/z90common.h b/drivers/s390/crypto/z90common.h
deleted file mode 100644
index dbbcda3c846a..000000000000
--- a/drivers/s390/crypto/z90common.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * linux/drivers/s390/crypto/z90common.h
- *
- * z90crypt 1.3.3
- *
- * Copyright (C) 2001, 2005 IBM Corporation
- * Author(s): Robert Burroughs (burrough@us.ibm.com)
- * Eric Rossman (edrossma@us.ibm.com)
- *
- * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- *
- * 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.
- */
-
-#ifndef _Z90COMMON_H_
-#define _Z90COMMON_H_
-
-
-#define RESPBUFFSIZE 256
-#define PCI_FUNC_KEY_DECRYPT 0x5044
-#define PCI_FUNC_KEY_ENCRYPT 0x504B
-extern int ext_bitlens;
-
-enum devstat {
- DEV_GONE,
- DEV_ONLINE,
- DEV_QUEUE_FULL,
- DEV_EMPTY,
- DEV_NO_WORK,
- DEV_BAD_MESSAGE,
- DEV_TSQ_EXCEPTION,
- DEV_RSQ_EXCEPTION,
- DEV_SEN_EXCEPTION,
- DEV_REC_EXCEPTION
-};
-
-enum hdstat {
- HD_NOT_THERE,
- HD_BUSY,
- HD_DECONFIGURED,
- HD_CHECKSTOPPED,
- HD_ONLINE,
- HD_TSQ_EXCEPTION
-};
-
-#define Z90C_NO_DEVICES 1
-#define Z90C_AMBIGUOUS_DOMAIN 2
-#define Z90C_INCORRECT_DOMAIN 3
-#define ENOTINIT 4
-
-#define SEN_BUSY 7
-#define SEN_USER_ERROR 8
-#define SEN_QUEUE_FULL 11
-#define SEN_NOT_AVAIL 16
-#define SEN_PAD_ERROR 17
-#define SEN_RETRY 18
-#define SEN_RELEASED 24
-
-#define REC_EMPTY 4
-#define REC_BUSY 6
-#define REC_OPERAND_INV 8
-#define REC_OPERAND_SIZE 9
-#define REC_EVEN_MOD 10
-#define REC_NO_WORK 11
-#define REC_HARDWAR_ERR 12
-#define REC_NO_RESPONSE 13
-#define REC_RETRY_DEV 14
-#define REC_USER_GONE 15
-#define REC_BAD_MESSAGE 16
-#define REC_INVALID_PAD 17
-#define REC_USE_PCICA 18
-
-#define WRONG_DEVICE_TYPE 20
-
-#define REC_FATAL_ERROR 32
-#define SEN_FATAL_ERROR 33
-#define TSQ_FATAL_ERROR 34
-#define RSQ_FATAL_ERROR 35
-
-#define Z90CRYPT_NUM_TYPES 6
-#define PCICA 0
-#define PCICC 1
-#define PCIXCC_MCL2 2
-#define PCIXCC_MCL3 3
-#define CEX2C 4
-#define CEX2A 5
-#define NILDEV -1
-#define ANYDEV -1
-#define PCIXCC_UNK -2
-
-enum hdevice_type {
- PCICC_HW = 3,
- PCICA_HW = 4,
- PCIXCC_HW = 5,
- CEX2A_HW = 6,
- CEX2C_HW = 7
-};
-
-struct CPRBX {
- unsigned short cprb_len;
- unsigned char cprb_ver_id;
- unsigned char pad_000[3];
- unsigned char func_id[2];
- unsigned char cprb_flags[4];
- unsigned int req_parml;
- unsigned int req_datal;
- unsigned int rpl_msgbl;
- unsigned int rpld_parml;
- unsigned int rpl_datal;
- unsigned int rpld_datal;
- unsigned int req_extbl;
- unsigned char pad_001[4];
- unsigned int rpld_extbl;
- unsigned char req_parmb[16];
- unsigned char req_datab[16];
- unsigned char rpl_parmb[16];
- unsigned char rpl_datab[16];
- unsigned char req_extb[16];
- unsigned char rpl_extb[16];
- unsigned short ccp_rtcode;
- unsigned short ccp_rscode;
- unsigned int mac_data_len;
- unsigned char logon_id[8];
- unsigned char mac_value[8];
- unsigned char mac_content_flgs;
- unsigned char pad_002;
- unsigned short domain;
- unsigned char pad_003[12];
- unsigned char pad_004[36];
-};
-
-#ifndef DEV_NAME
-#define DEV_NAME "z90crypt"
-#endif
-#define PRINTK(fmt, args...) \
- printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-#define PRINTKN(fmt, args...) \
- printk(KERN_DEBUG DEV_NAME ": " fmt, ## args)
-#define PRINTKW(fmt, args...) \
- printk(KERN_WARNING DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-#define PRINTKC(fmt, args...) \
- printk(KERN_CRIT DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-
-#ifdef Z90CRYPT_DEBUG
-#define PDEBUG(fmt, args...) \
- printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-#else
-#define PDEBUG(fmt, args...) do {} while (0)
-#endif
-
-#define UMIN(a,b) ((a) < (b) ? (a) : (b))
-#define IS_EVEN(x) ((x) == (2 * ((x) / 2)))
-
-#endif
diff --git a/drivers/s390/crypto/z90crypt.h b/drivers/s390/crypto/z90crypt.h
deleted file mode 100644
index 0ca1d126ccb6..000000000000
--- a/drivers/s390/crypto/z90crypt.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * linux/drivers/s390/crypto/z90crypt.h
- *
- * z90crypt 1.3.3 (kernel-private header)
- *
- * Copyright (C) 2001, 2005 IBM Corporation
- * Author(s): Robert Burroughs (burrough@us.ibm.com)
- * Eric Rossman (edrossma@us.ibm.com)
- *
- * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- *
- * 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.
- */
-
-#ifndef _Z90CRYPT_H_
-#define _Z90CRYPT_H_
-
-#include <asm/z90crypt.h>
-
-/**
- * local errno definitions
- */
-#define ENOBUFF 129 // filp->private_data->...>work_elem_p->buffer is NULL
-#define EWORKPEND 130 // user issues ioctl while another pending
-#define ERELEASED 131 // user released while ioctl pending
-#define EQUIESCE 132 // z90crypt quiescing (no more work allowed)
-#define ETIMEOUT 133 // request timed out
-#define EUNKNOWN 134 // some unrecognized error occured (retry may succeed)
-#define EGETBUFF 135 // Error getting buffer or hardware lacks capability
- // (retry in software)
-
-/**
- * DEPRECATED STRUCTURES
- */
-
-/**
- * This structure is DEPRECATED and the corresponding ioctl() has been
- * replaced with individual ioctl()s for each piece of data!
- * This structure will NOT survive past version 1.3.1, so switch to the
- * new ioctl()s.
- */
-#define MASK_LENGTH 64 // mask length
-struct ica_z90_status {
- int totalcount;
- int leedslitecount; // PCICA
- int leeds2count; // PCICC
- // int PCIXCCCount; is not in struct for backward compatibility
- int requestqWaitCount;
- int pendingqWaitCount;
- int totalOpenCount;
- int cryptoDomain;
- // status: 0=not there, 1=PCICA, 2=PCICC, 3=PCIXCC_MCL2, 4=PCIXCC_MCL3,
- // 5=CEX2C
- unsigned char status[MASK_LENGTH];
- // qdepth: # work elements waiting for each device
- unsigned char qdepth[MASK_LENGTH];
-};
-
-#endif /* _Z90CRYPT_H_ */
diff --git a/drivers/s390/crypto/z90hardware.c b/drivers/s390/crypto/z90hardware.c
deleted file mode 100644
index be60795f4a74..000000000000
--- a/drivers/s390/crypto/z90hardware.c
+++ /dev/null
@@ -1,2531 +0,0 @@
-/*
- * linux/drivers/s390/crypto/z90hardware.c
- *
- * z90crypt 1.3.3
- *
- * Copyright (C) 2001, 2005 IBM Corporation
- * Author(s): Robert Burroughs (burrough@us.ibm.com)
- * Eric Rossman (edrossma@us.ibm.com)
- *
- * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- *
- * 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 <asm/uaccess.h>
-#include <linux/compiler.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include "z90crypt.h"
-#include "z90common.h"
-
-struct cca_token_hdr {
- unsigned char token_identifier;
- unsigned char version;
- unsigned short token_length;
- unsigned char reserved[4];
-};
-
-#define CCA_TKN_HDR_ID_EXT 0x1E
-
-struct cca_private_ext_ME_sec {
- unsigned char section_identifier;
- unsigned char version;
- unsigned short section_length;
- unsigned char private_key_hash[20];
- unsigned char reserved1[4];
- unsigned char key_format;
- unsigned char reserved2;
- unsigned char key_name_hash[20];
- unsigned char key_use_flags[4];
- unsigned char reserved3[6];
- unsigned char reserved4[24];
- unsigned char confounder[24];
- unsigned char exponent[128];
- unsigned char modulus[128];
-};
-
-#define CCA_PVT_USAGE_ALL 0x80
-
-struct cca_public_sec {
- unsigned char section_identifier;
- unsigned char version;
- unsigned short section_length;
- unsigned char reserved[2];
- unsigned short exponent_len;
- unsigned short modulus_bit_len;
- unsigned short modulus_byte_len;
- unsigned char exponent[3];
-};
-
-struct cca_private_ext_ME {
- struct cca_token_hdr pvtMEHdr;
- struct cca_private_ext_ME_sec pvtMESec;
- struct cca_public_sec pubMESec;
-};
-
-struct cca_public_key {
- struct cca_token_hdr pubHdr;
- struct cca_public_sec pubSec;
-};
-
-struct cca_pvt_ext_CRT_sec {
- unsigned char section_identifier;
- unsigned char version;
- unsigned short section_length;
- unsigned char private_key_hash[20];
- unsigned char reserved1[4];
- unsigned char key_format;
- unsigned char reserved2;
- unsigned char key_name_hash[20];
- unsigned char key_use_flags[4];
- unsigned short p_len;
- unsigned short q_len;
- unsigned short dp_len;
- unsigned short dq_len;
- unsigned short u_len;
- unsigned short mod_len;
- unsigned char reserved3[4];
- unsigned short pad_len;
- unsigned char reserved4[52];
- unsigned char confounder[8];
-};
-
-#define CCA_PVT_EXT_CRT_SEC_ID_PVT 0x08
-#define CCA_PVT_EXT_CRT_SEC_FMT_CL 0x40
-
-struct cca_private_ext_CRT {
- struct cca_token_hdr pvtCrtHdr;
- struct cca_pvt_ext_CRT_sec pvtCrtSec;
- struct cca_public_sec pubCrtSec;
-};
-
-struct ap_status_word {
- unsigned char q_stat_flags;
- unsigned char response_code;
- unsigned char reserved[2];
-};
-
-#define AP_Q_STATUS_EMPTY 0x80
-#define AP_Q_STATUS_REPLIES_WAITING 0x40
-#define AP_Q_STATUS_ARRAY_FULL 0x20
-
-#define AP_RESPONSE_NORMAL 0x00
-#define AP_RESPONSE_Q_NOT_AVAIL 0x01
-#define AP_RESPONSE_RESET_IN_PROGRESS 0x02
-#define AP_RESPONSE_DECONFIGURED 0x03
-#define AP_RESPONSE_CHECKSTOPPED 0x04
-#define AP_RESPONSE_BUSY 0x05
-#define AP_RESPONSE_Q_FULL 0x10
-#define AP_RESPONSE_NO_PENDING_REPLY 0x10
-#define AP_RESPONSE_INDEX_TOO_BIG 0x11
-#define AP_RESPONSE_NO_FIRST_PART 0x13
-#define AP_RESPONSE_MESSAGE_TOO_BIG 0x15
-
-#define AP_MAX_CDX_BITL 4
-#define AP_RQID_RESERVED_BITL 4
-#define SKIP_BITL (AP_MAX_CDX_BITL + AP_RQID_RESERVED_BITL)
-
-struct type4_hdr {
- unsigned char reserved1;
- unsigned char msg_type_code;
- unsigned short msg_len;
- unsigned char request_code;
- unsigned char msg_fmt;
- unsigned short reserved2;
-};
-
-#define TYPE4_TYPE_CODE 0x04
-#define TYPE4_REQU_CODE 0x40
-
-#define TYPE4_SME_LEN 0x0188
-#define TYPE4_LME_LEN 0x0308
-#define TYPE4_SCR_LEN 0x01E0
-#define TYPE4_LCR_LEN 0x03A0
-
-#define TYPE4_SME_FMT 0x00
-#define TYPE4_LME_FMT 0x10
-#define TYPE4_SCR_FMT 0x40
-#define TYPE4_LCR_FMT 0x50
-
-struct type4_sme {
- struct type4_hdr header;
- unsigned char message[128];
- unsigned char exponent[128];
- unsigned char modulus[128];
-};
-
-struct type4_lme {
- struct type4_hdr header;
- unsigned char message[256];
- unsigned char exponent[256];
- unsigned char modulus[256];
-};
-
-struct type4_scr {
- struct type4_hdr header;
- unsigned char message[128];
- unsigned char dp[72];
- unsigned char dq[64];
- unsigned char p[72];
- unsigned char q[64];
- unsigned char u[72];
-};
-
-struct type4_lcr {
- struct type4_hdr header;
- unsigned char message[256];
- unsigned char dp[136];
- unsigned char dq[128];
- unsigned char p[136];
- unsigned char q[128];
- unsigned char u[136];
-};
-
-union type4_msg {
- struct type4_sme sme;
- struct type4_lme lme;
- struct type4_scr scr;
- struct type4_lcr lcr;
-};
-
-struct type84_hdr {
- unsigned char reserved1;
- unsigned char code;
- unsigned short len;
- unsigned char reserved2[4];
-};
-
-#define TYPE84_RSP_CODE 0x84
-
-struct type6_hdr {
- unsigned char reserved1;
- unsigned char type;
- unsigned char reserved2[2];
- unsigned char right[4];
- unsigned char reserved3[2];
- unsigned char reserved4[2];
- unsigned char apfs[4];
- unsigned int offset1;
- unsigned int offset2;
- unsigned int offset3;
- unsigned int offset4;
- unsigned char agent_id[16];
- unsigned char rqid[2];
- unsigned char reserved5[2];
- unsigned char function_code[2];
- unsigned char reserved6[2];
- unsigned int ToCardLen1;
- unsigned int ToCardLen2;
- unsigned int ToCardLen3;
- unsigned int ToCardLen4;
- unsigned int FromCardLen1;
- unsigned int FromCardLen2;
- unsigned int FromCardLen3;
- unsigned int FromCardLen4;
-};
-
-struct CPRB {
- unsigned char cprb_len[2];
- unsigned char cprb_ver_id;
- unsigned char pad_000;
- unsigned char srpi_rtcode[4];
- unsigned char srpi_verb;
- unsigned char flags;
- unsigned char func_id[2];
- unsigned char checkpoint_flag;
- unsigned char resv2;
- unsigned char req_parml[2];
- unsigned char req_parmp[4];
- unsigned char req_datal[4];
- unsigned char req_datap[4];
- unsigned char rpl_parml[2];
- unsigned char pad_001[2];
- unsigned char rpl_parmp[4];
- unsigned char rpl_datal[4];
- unsigned char rpl_datap[4];
- unsigned char ccp_rscode[2];
- unsigned char ccp_rtcode[2];
- unsigned char repd_parml[2];
- unsigned char mac_data_len[2];
- unsigned char repd_datal[4];
- unsigned char req_pc[2];
- unsigned char res_origin[8];
- unsigned char mac_value[8];
- unsigned char logon_id[8];
- unsigned char usage_domain[2];
- unsigned char resv3[18];
- unsigned char svr_namel[2];
- unsigned char svr_name[8];
-};
-
-struct type6_msg {
- struct type6_hdr header;
- struct CPRB CPRB;
-};
-
-struct type86_hdr {
- unsigned char reserved1;
- unsigned char type;
- unsigned char format;
- unsigned char reserved2;
- unsigned char reply_code;
- unsigned char reserved3[3];
-};
-
-#define TYPE86_RSP_CODE 0x86
-#define TYPE86_FMT2 0x02
-
-struct type86_fmt2_msg {
- struct type86_hdr header;
- unsigned char reserved[4];
- unsigned char apfs[4];
- unsigned int count1;
- unsigned int offset1;
- unsigned int count2;
- unsigned int offset2;
- unsigned int count3;
- unsigned int offset3;
- unsigned int count4;
- unsigned int offset4;
-};
-
-static struct type6_hdr static_type6_hdr = {
- 0x00,
- 0x06,
- {0x00,0x00},
- {0x00,0x00,0x00,0x00},
- {0x00,0x00},
- {0x00,0x00},
- {0x00,0x00,0x00,0x00},
- 0x00000058,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- {0x01,0x00,0x43,0x43,0x41,0x2D,0x41,0x50,
- 0x50,0x4C,0x20,0x20,0x20,0x01,0x01,0x01},
- {0x00,0x00},
- {0x00,0x00},
- {0x50,0x44},
- {0x00,0x00},
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000
-};
-
-static struct type6_hdr static_type6_hdrX = {
- 0x00,
- 0x06,
- {0x00,0x00},
- {0x00,0x00,0x00,0x00},
- {0x00,0x00},
- {0x00,0x00},
- {0x00,0x00,0x00,0x00},
- 0x00000058,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- {0x43,0x41,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00},
- {0x00,0x00},
- {0x50,0x44},
- {0x00,0x00},
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000
-};
-
-static struct CPRB static_cprb = {
- {0x70,0x00},
- 0x41,
- 0x00,
- {0x00,0x00,0x00,0x00},
- 0x00,
- 0x00,
- {0x54,0x32},
- 0x01,
- 0x00,
- {0x00,0x00},
- {0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00},
- {0x00,0x00},
- {0x00,0x00},
- {0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00},
- {0x00,0x00},
- {0x00,0x00},
- {0x00,0x00},
- {0x00,0x00},
- {0x00,0x00,0x00,0x00},
- {0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00},
- {0x08,0x00},
- {0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20}
-};
-
-struct function_and_rules_block {
- unsigned char function_code[2];
- unsigned char ulen[2];
- unsigned char only_rule[8];
-};
-
-static struct function_and_rules_block static_pkd_function_and_rules = {
- {0x50,0x44},
- {0x0A,0x00},
- {'P','K','C','S','-','1','.','2'}
-};
-
-static struct function_and_rules_block static_pke_function_and_rules = {
- {0x50,0x4B},
- {0x0A,0x00},
- {'P','K','C','S','-','1','.','2'}
-};
-
-struct T6_keyBlock_hdr {
- unsigned char blen[2];
- unsigned char ulen[2];
- unsigned char flags[2];
-};
-
-static struct T6_keyBlock_hdr static_T6_keyBlock_hdr = {
- {0x89,0x01},
- {0x87,0x01},
- {0x00}
-};
-
-static struct CPRBX static_cprbx = {
- 0x00DC,
- 0x02,
- {0x00,0x00,0x00},
- {0x54,0x32},
- {0x00,0x00,0x00,0x00},
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- {0x00,0x00,0x00,0x00},
- 0x00000000,
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- 0x0000,
- 0x0000,
- 0x00000000,
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- 0x00,
- 0x00,
- 0x0000,
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
-};
-
-static struct function_and_rules_block static_pkd_function_and_rulesX_MCL2 = {
- {0x50,0x44},
- {0x00,0x0A},
- {'P','K','C','S','-','1','.','2'}
-};
-
-static struct function_and_rules_block static_pke_function_and_rulesX_MCL2 = {
- {0x50,0x4B},
- {0x00,0x0A},
- {'Z','E','R','O','-','P','A','D'}
-};
-
-static struct function_and_rules_block static_pkd_function_and_rulesX = {
- {0x50,0x44},
- {0x00,0x0A},
- {'Z','E','R','O','-','P','A','D'}
-};
-
-static struct function_and_rules_block static_pke_function_and_rulesX = {
- {0x50,0x4B},
- {0x00,0x0A},
- {'M','R','P',' ',' ',' ',' ',' '}
-};
-
-static unsigned char static_PKE_function_code[2] = {0x50, 0x4B};
-
-struct T6_keyBlock_hdrX {
- unsigned short blen;
- unsigned short ulen;
- unsigned char flags[2];
-};
-
-static unsigned char static_pad[256] = {
-0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD,0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57,
-0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B,0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39,
-0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5,0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D,
-0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB,0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F,
-0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9,0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45,
-0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9,0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F,
-0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD,0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D,
-0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD,0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9,
-0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B,0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B,
-0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B,0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD,
-0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7,0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1,
-0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3,0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23,
-0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55,0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43,
-0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F,0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F,
-0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5,0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD,
-0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41,0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09
-};
-
-static struct cca_private_ext_ME static_pvt_me_key = {
- {
- 0x1E,
- 0x00,
- 0x0183,
- {0x00,0x00,0x00,0x00}
- },
-
- {
- 0x02,
- 0x00,
- 0x016C,
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00},
- 0x00,
- 0x00,
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00},
- {0x80,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
- },
-
- {
- 0x04,
- 0x00,
- 0x000F,
- {0x00,0x00},
- 0x0003,
- 0x0000,
- 0x0000,
- {0x01,0x00,0x01}
- }
-};
-
-static struct cca_public_key static_public_key = {
- {
- 0x1E,
- 0x00,
- 0x0000,
- {0x00,0x00,0x00,0x00}
- },
-
- {
- 0x04,
- 0x00,
- 0x0000,
- {0x00,0x00},
- 0x0000,
- 0x0000,
- 0x0000,
- {0x01,0x00,0x01}
- }
-};
-
-#define FIXED_TYPE6_ME_LEN 0x0000025F
-
-#define FIXED_TYPE6_ME_EN_LEN 0x000000F0
-
-#define FIXED_TYPE6_ME_LENX 0x000002CB
-
-#define FIXED_TYPE6_ME_EN_LENX 0x0000015C
-
-static struct cca_public_sec static_cca_pub_sec = {
- 0x04,
- 0x00,
- 0x000f,
- {0x00,0x00},
- 0x0003,
- 0x0000,
- 0x0000,
- {0x01,0x00,0x01}
-};
-
-#define FIXED_TYPE6_CR_LEN 0x00000177
-
-#define FIXED_TYPE6_CR_LENX 0x000001E3
-
-#define MAX_RESPONSE_SIZE 0x00000710
-
-#define MAX_RESPONSEX_SIZE 0x0000077C
-
-#define RESPONSE_CPRB_SIZE 0x000006B8
-#define RESPONSE_CPRBX_SIZE 0x00000724
-
-struct type50_hdr {
- u8 reserved1;
- u8 msg_type_code;
- u16 msg_len;
- u8 reserved2;
- u8 ignored;
- u16 reserved3;
-};
-
-#define TYPE50_TYPE_CODE 0x50
-
-#define TYPE50_MEB1_LEN (sizeof(struct type50_meb1_msg))
-#define TYPE50_MEB2_LEN (sizeof(struct type50_meb2_msg))
-#define TYPE50_CRB1_LEN (sizeof(struct type50_crb1_msg))
-#define TYPE50_CRB2_LEN (sizeof(struct type50_crb2_msg))
-
-#define TYPE50_MEB1_FMT 0x0001
-#define TYPE50_MEB2_FMT 0x0002
-#define TYPE50_CRB1_FMT 0x0011
-#define TYPE50_CRB2_FMT 0x0012
-
-struct type50_meb1_msg {
- struct type50_hdr header;
- u16 keyblock_type;
- u8 reserved[6];
- u8 exponent[128];
- u8 modulus[128];
- u8 message[128];
-};
-
-struct type50_meb2_msg {
- struct type50_hdr header;
- u16 keyblock_type;
- u8 reserved[6];
- u8 exponent[256];
- u8 modulus[256];
- u8 message[256];
-};
-
-struct type50_crb1_msg {
- struct type50_hdr header;
- u16 keyblock_type;
- u8 reserved[6];
- u8 p[64];
- u8 q[64];
- u8 dp[64];
- u8 dq[64];
- u8 u[64];
- u8 message[128];
-};
-
-struct type50_crb2_msg {
- struct type50_hdr header;
- u16 keyblock_type;
- u8 reserved[6];
- u8 p[128];
- u8 q[128];
- u8 dp[128];
- u8 dq[128];
- u8 u[128];
- u8 message[256];
-};
-
-union type50_msg {
- struct type50_meb1_msg meb1;
- struct type50_meb2_msg meb2;
- struct type50_crb1_msg crb1;
- struct type50_crb2_msg crb2;
-};
-
-struct type80_hdr {
- u8 reserved1;
- u8 type;
- u16 len;
- u8 code;
- u8 reserved2[3];
- u8 reserved3[8];
-};
-
-#define TYPE80_RSP_CODE 0x80
-
-struct error_hdr {
- unsigned char reserved1;
- unsigned char type;
- unsigned char reserved2[2];
- unsigned char reply_code;
- unsigned char reserved3[3];
-};
-
-#define TYPE82_RSP_CODE 0x82
-#define TYPE88_RSP_CODE 0x88
-
-#define REP82_ERROR_MACHINE_FAILURE 0x10
-#define REP82_ERROR_PREEMPT_FAILURE 0x12
-#define REP82_ERROR_CHECKPT_FAILURE 0x14
-#define REP82_ERROR_MESSAGE_TYPE 0x20
-#define REP82_ERROR_INVALID_COMM_CD 0x21
-#define REP82_ERROR_INVALID_MSG_LEN 0x23
-#define REP82_ERROR_RESERVD_FIELD 0x24
-#define REP82_ERROR_FORMAT_FIELD 0x29
-#define REP82_ERROR_INVALID_COMMAND 0x30
-#define REP82_ERROR_MALFORMED_MSG 0x40
-#define REP82_ERROR_RESERVED_FIELDO 0x50
-#define REP82_ERROR_WORD_ALIGNMENT 0x60
-#define REP82_ERROR_MESSAGE_LENGTH 0x80
-#define REP82_ERROR_OPERAND_INVALID 0x82
-#define REP82_ERROR_OPERAND_SIZE 0x84
-#define REP82_ERROR_EVEN_MOD_IN_OPND 0x85
-#define REP82_ERROR_RESERVED_FIELD 0x88
-#define REP82_ERROR_TRANSPORT_FAIL 0x90
-#define REP82_ERROR_PACKET_TRUNCATED 0xA0
-#define REP82_ERROR_ZERO_BUFFER_LEN 0xB0
-
-#define REP88_ERROR_MODULE_FAILURE 0x10
-#define REP88_ERROR_MODULE_TIMEOUT 0x11
-#define REP88_ERROR_MODULE_NOTINIT 0x13
-#define REP88_ERROR_MODULE_NOTAVAIL 0x14
-#define REP88_ERROR_MODULE_DISABLED 0x15
-#define REP88_ERROR_MODULE_IN_DIAGN 0x17
-#define REP88_ERROR_FASTPATH_DISABLD 0x19
-#define REP88_ERROR_MESSAGE_TYPE 0x20
-#define REP88_ERROR_MESSAGE_MALFORMD 0x22
-#define REP88_ERROR_MESSAGE_LENGTH 0x23
-#define REP88_ERROR_RESERVED_FIELD 0x24
-#define REP88_ERROR_KEY_TYPE 0x34
-#define REP88_ERROR_INVALID_KEY 0x82
-#define REP88_ERROR_OPERAND 0x84
-#define REP88_ERROR_OPERAND_EVEN_MOD 0x85
-
-#define CALLER_HEADER 12
-
-static inline int
-testq(int q_nr, int *q_depth, int *dev_type, struct ap_status_word *stat)
-{
- int ccode;
-
- asm volatile
-#ifdef CONFIG_64BIT
- (" llgfr 0,%4 \n"
- " slgr 1,1 \n"
- " lgr 2,1 \n"
- "0: .long 0xb2af0000 \n"
- "1: ipm %0 \n"
- " srl %0,28 \n"
- " iihh %0,0 \n"
- " iihl %0,0 \n"
- " lgr %1,1 \n"
- " lgr %3,2 \n"
- " srl %3,24 \n"
- " sll 2,24 \n"
- " srl 2,24 \n"
- " lgr %2,2 \n"
- "2: \n"
- ".section .fixup,\"ax\" \n"
- "3: \n"
- " lhi %0,%h5 \n"
- " jg 2b \n"
- ".previous \n"
- ".section __ex_table,\"a\" \n"
- " .align 8 \n"
- " .quad 0b,3b \n"
- " .quad 1b,3b \n"
- ".previous"
- :"=d" (ccode),"=d" (*stat),"=d" (*q_depth), "=d" (*dev_type)
- :"d" (q_nr), "K" (DEV_TSQ_EXCEPTION)
- :"cc","0","1","2","memory");
-#else
- (" lr 0,%4 \n"
- " slr 1,1 \n"
- " lr 2,1 \n"
- "0: .long 0xb2af0000 \n"
- "1: ipm %0 \n"
- " srl %0,28 \n"
- " lr %1,1 \n"
- " lr %3,2 \n"
- " srl %3,24 \n"
- " sll 2,24 \n"
- " srl 2,24 \n"
- " lr %2,2 \n"
- "2: \n"
- ".section .fixup,\"ax\" \n"
- "3: \n"
- " lhi %0,%h5 \n"
- " bras 1,4f \n"
- " .long 2b \n"
- "4: \n"
- " l 1,0(1) \n"
- " br 1 \n"
- ".previous \n"
- ".section __ex_table,\"a\" \n"
- " .align 4 \n"
- " .long 0b,3b \n"
- " .long 1b,3b \n"
- ".previous"
- :"=d" (ccode),"=d" (*stat),"=d" (*q_depth), "=d" (*dev_type)
- :"d" (q_nr), "K" (DEV_TSQ_EXCEPTION)
- :"cc","0","1","2","memory");
-#endif
- return ccode;
-}
-
-static inline int
-resetq(int q_nr, struct ap_status_word *stat_p)
-{
- int ccode;
-
- asm volatile
-#ifdef CONFIG_64BIT
- (" llgfr 0,%2 \n"
- " lghi 1,1 \n"
- " sll 1,24 \n"
- " or 0,1 \n"
- " slgr 1,1 \n"
- " lgr 2,1 \n"
- "0: .long 0xb2af0000 \n"
- "1: ipm %0 \n"
- " srl %0,28 \n"
- " iihh %0,0 \n"
- " iihl %0,0 \n"
- " lgr %1,1 \n"
- "2: \n"
- ".section .fixup,\"ax\" \n"
- "3: \n"
- " lhi %0,%h3 \n"
- " jg 2b \n"
- ".previous \n"
- ".section __ex_table,\"a\" \n"
- " .align 8 \n"
- " .quad 0b,3b \n"
- " .quad 1b,3b \n"
- ".previous"
- :"=d" (ccode),"=d" (*stat_p)
- :"d" (q_nr), "K" (DEV_RSQ_EXCEPTION)
- :"cc","0","1","2","memory");
-#else
- (" lr 0,%2 \n"
- " lhi 1,1 \n"
- " sll 1,24 \n"
- " or 0,1 \n"
- " slr 1,1 \n"
- " lr 2,1 \n"
- "0: .long 0xb2af0000 \n"
- "1: ipm %0 \n"
- " srl %0,28 \n"
- " lr %1,1 \n"
- "2: \n"
- ".section .fixup,\"ax\" \n"
- "3: \n"
- " lhi %0,%h3 \n"
- " bras 1,4f \n"
- " .long 2b \n"
- "4: \n"
- " l 1,0(1) \n"
- " br 1 \n"
- ".previous \n"
- ".section __ex_table,\"a\" \n"
- " .align 4 \n"
- " .long 0b,3b \n"
- " .long 1b,3b \n"
- ".previous"
- :"=d" (ccode),"=d" (*stat_p)
- :"d" (q_nr), "K" (DEV_RSQ_EXCEPTION)
- :"cc","0","1","2","memory");
-#endif
- return ccode;
-}
-
-static inline int
-sen(int msg_len, unsigned char *msg_ext, struct ap_status_word *stat)
-{
- int ccode;
-
- asm volatile
-#ifdef CONFIG_64BIT
- (" lgr 6,%3 \n"
- " llgfr 7,%2 \n"
- " llgt 0,0(6) \n"
- " lghi 1,64 \n"
- " sll 1,24 \n"
- " or 0,1 \n"
- " la 6,4(6) \n"
- " llgt 2,0(6) \n"
- " llgt 3,4(6) \n"
- " la 6,8(6) \n"
- " slr 1,1 \n"
- "0: .long 0xb2ad0026 \n"
- "1: brc 2,0b \n"
- " ipm %0 \n"
- " srl %0,28 \n"
- " iihh %0,0 \n"
- " iihl %0,0 \n"
- " lgr %1,1 \n"
- "2: \n"
- ".section .fixup,\"ax\" \n"
- "3: \n"
- " lhi %0,%h4 \n"
- " jg 2b \n"
- ".previous \n"
- ".section __ex_table,\"a\" \n"
- " .align 8 \n"
- " .quad 0b,3b \n"
- " .quad 1b,3b \n"
- ".previous"
- :"=d" (ccode),"=d" (*stat)
- :"d" (msg_len),"a" (msg_ext), "K" (DEV_SEN_EXCEPTION)
- :"cc","0","1","2","3","6","7","memory");
-#else
- (" lr 6,%3 \n"
- " lr 7,%2 \n"
- " l 0,0(6) \n"
- " lhi 1,64 \n"
- " sll 1,24 \n"
- " or 0,1 \n"
- " la 6,4(6) \n"
- " l 2,0(6) \n"
- " l 3,4(6) \n"
- " la 6,8(6) \n"
- " slr 1,1 \n"
- "0: .long 0xb2ad0026 \n"
- "1: brc 2,0b \n"
- " ipm %0 \n"
- " srl %0,28 \n"
- " lr %1,1 \n"
- "2: \n"
- ".section .fixup,\"ax\" \n"
- "3: \n"
- " lhi %0,%h4 \n"
- " bras 1,4f \n"
- " .long 2b \n"
- "4: \n"
- " l 1,0(1) \n"
- " br 1 \n"
- ".previous \n"
- ".section __ex_table,\"a\" \n"
- " .align 4 \n"
- " .long 0b,3b \n"
- " .long 1b,3b \n"
- ".previous"
- :"=d" (ccode),"=d" (*stat)
- :"d" (msg_len),"a" (msg_ext), "K" (DEV_SEN_EXCEPTION)
- :"cc","0","1","2","3","6","7","memory");
-#endif
- return ccode;
-}
-
-static inline int
-rec(int q_nr, int buff_l, unsigned char *rsp, unsigned char *id,
- struct ap_status_word *st)
-{
- int ccode;
-
- asm volatile
-#ifdef CONFIG_64BIT
- (" llgfr 0,%2 \n"
- " lgr 3,%4 \n"
- " lgr 6,%3 \n"
- " llgfr 7,%5 \n"
- " lghi 1,128 \n"
- " sll 1,24 \n"
- " or 0,1 \n"
- " slgr 1,1 \n"
- " lgr 2,1 \n"
- " lgr 4,1 \n"
- " lgr 5,1 \n"
- "0: .long 0xb2ae0046 \n"
- "1: brc 2,0b \n"
- " brc 4,0b \n"
- " ipm %0 \n"
- " srl %0,28 \n"
- " iihh %0,0 \n"
- " iihl %0,0 \n"
- " lgr %1,1 \n"
- " st 4,0(3) \n"
- " st 5,4(3) \n"
- "2: \n"
- ".section .fixup,\"ax\" \n"
- "3: \n"
- " lhi %0,%h6 \n"
- " jg 2b \n"
- ".previous \n"
- ".section __ex_table,\"a\" \n"
- " .align 8 \n"
- " .quad 0b,3b \n"
- " .quad 1b,3b \n"
- ".previous"
- :"=d"(ccode),"=d"(*st)
- :"d" (q_nr), "d" (rsp), "d" (id), "d" (buff_l), "K" (DEV_REC_EXCEPTION)
- :"cc","0","1","2","3","4","5","6","7","memory");
-#else
- (" lr 0,%2 \n"
- " lr 3,%4 \n"
- " lr 6,%3 \n"
- " lr 7,%5 \n"
- " lhi 1,128 \n"
- " sll 1,24 \n"
- " or 0,1 \n"
- " slr 1,1 \n"
- " lr 2,1 \n"
- " lr 4,1 \n"
- " lr 5,1 \n"
- "0: .long 0xb2ae0046 \n"
- "1: brc 2,0b \n"
- " brc 4,0b \n"
- " ipm %0 \n"
- " srl %0,28 \n"
- " lr %1,1 \n"
- " st 4,0(3) \n"
- " st 5,4(3) \n"
- "2: \n"
- ".section .fixup,\"ax\" \n"
- "3: \n"
- " lhi %0,%h6 \n"
- " bras 1,4f \n"
- " .long 2b \n"
- "4: \n"
- " l 1,0(1) \n"
- " br 1 \n"
- ".previous \n"
- ".section __ex_table,\"a\" \n"
- " .align 4 \n"
- " .long 0b,3b \n"
- " .long 1b,3b \n"
- ".previous"
- :"=d"(ccode),"=d"(*st)
- :"d" (q_nr), "d" (rsp), "d" (id), "d" (buff_l), "K" (DEV_REC_EXCEPTION)
- :"cc","0","1","2","3","4","5","6","7","memory");
-#endif
- return ccode;
-}
-
-static inline void
-itoLe2(int *i_p, unsigned char *lechars)
-{
- *lechars = *((unsigned char *) i_p + sizeof(int) - 1);
- *(lechars + 1) = *((unsigned char *) i_p + sizeof(int) - 2);
-}
-
-static inline void
-le2toI(unsigned char *lechars, int *i_p)
-{
- unsigned char *ic_p;
- *i_p = 0;
- ic_p = (unsigned char *) i_p;
- *(ic_p + 2) = *(lechars + 1);
- *(ic_p + 3) = *(lechars);
-}
-
-static inline int
-is_empty(unsigned char *ptr, int len)
-{
- return !memcmp(ptr, (unsigned char *) &static_pvt_me_key+60, len);
-}
-
-enum hdstat
-query_online(int deviceNr, int cdx, int resetNr, int *q_depth, int *dev_type)
-{
- int q_nr, i, t_depth, t_dev_type;
- enum devstat ccode;
- struct ap_status_word stat_word;
- enum hdstat stat;
- int break_out;
-
- q_nr = (deviceNr << SKIP_BITL) + cdx;
- stat = HD_BUSY;
- ccode = testq(q_nr, &t_depth, &t_dev_type, &stat_word);
- PDEBUG("ccode %d response_code %02X\n", ccode, stat_word.response_code);
- break_out = 0;
- for (i = 0; i < resetNr; i++) {
- if (ccode > 3) {
- PRINTKC("Exception testing device %d\n", i);
- return HD_TSQ_EXCEPTION;
- }
- switch (ccode) {
- case 0:
- PDEBUG("t_dev_type %d\n", t_dev_type);
- break_out = 1;
- stat = HD_ONLINE;
- *q_depth = t_depth + 1;
- switch (t_dev_type) {
- case PCICA_HW:
- *dev_type = PCICA;
- break;
- case PCICC_HW:
- *dev_type = PCICC;
- break;
- case PCIXCC_HW:
- *dev_type = PCIXCC_UNK;
- break;
- case CEX2C_HW:
- *dev_type = CEX2C;
- break;
- case CEX2A_HW:
- *dev_type = CEX2A;
- break;
- default:
- *dev_type = NILDEV;
- break;
- }
- PDEBUG("available device %d: Q depth = %d, dev "
- "type = %d, stat = %02X%02X%02X%02X\n",
- deviceNr, *q_depth, *dev_type,
- stat_word.q_stat_flags,
- stat_word.response_code,
- stat_word.reserved[0],
- stat_word.reserved[1]);
- break;
- case 3:
- switch (stat_word.response_code) {
- case AP_RESPONSE_NORMAL:
- stat = HD_ONLINE;
- break_out = 1;
- *q_depth = t_depth + 1;
- *dev_type = t_dev_type;
- PDEBUG("cc3, available device "
- "%d: Q depth = %d, dev "
- "type = %d, stat = "
- "%02X%02X%02X%02X\n",
- deviceNr, *q_depth,
- *dev_type,
- stat_word.q_stat_flags,
- stat_word.response_code,
- stat_word.reserved[0],
- stat_word.reserved[1]);
- break;
- case AP_RESPONSE_Q_NOT_AVAIL:
- stat = HD_NOT_THERE;
- break_out = 1;
- break;
- case AP_RESPONSE_RESET_IN_PROGRESS:
- PDEBUG("device %d in reset\n",
- deviceNr);
- break;
- case AP_RESPONSE_DECONFIGURED:
- stat = HD_DECONFIGURED;
- break_out = 1;
- break;
- case AP_RESPONSE_CHECKSTOPPED:
- stat = HD_CHECKSTOPPED;
- break_out = 1;
- break;
- case AP_RESPONSE_BUSY:
- PDEBUG("device %d busy\n",
- deviceNr);
- break;
- default:
- break;
- }
- break;
- default:
- stat = HD_NOT_THERE;
- break_out = 1;
- break;
- }
- if (break_out)
- break;
-
- udelay(5);
-
- ccode = testq(q_nr, &t_depth, &t_dev_type, &stat_word);
- }
- return stat;
-}
-
-enum devstat
-reset_device(int deviceNr, int cdx, int resetNr)
-{
- int q_nr, ccode = 0, dummy_qdepth, dummy_devType, i;
- struct ap_status_word stat_word;
- enum devstat stat;
- int break_out;
-
- q_nr = (deviceNr << SKIP_BITL) + cdx;
- stat = DEV_GONE;
- ccode = resetq(q_nr, &stat_word);
- if (ccode > 3)
- return DEV_RSQ_EXCEPTION;
-
- break_out = 0;
- for (i = 0; i < resetNr; i++) {
- switch (ccode) {
- case 0:
- stat = DEV_ONLINE;
- if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY)
- break_out = 1;
- break;
- case 3:
- switch (stat_word.response_code) {
- case AP_RESPONSE_NORMAL:
- stat = DEV_ONLINE;
- if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY)
- break_out = 1;
- break;
- case AP_RESPONSE_Q_NOT_AVAIL:
- case AP_RESPONSE_DECONFIGURED:
- case AP_RESPONSE_CHECKSTOPPED:
- stat = DEV_GONE;
- break_out = 1;
- break;
- case AP_RESPONSE_RESET_IN_PROGRESS:
- case AP_RESPONSE_BUSY:
- default:
- break;
- }
- break;
- default:
- stat = DEV_GONE;
- break_out = 1;
- break;
- }
- if (break_out == 1)
- break;
- udelay(5);
-
- ccode = testq(q_nr, &dummy_qdepth, &dummy_devType, &stat_word);
- if (ccode > 3) {
- stat = DEV_TSQ_EXCEPTION;
- break;
- }
- }
- PDEBUG("Number of testq's needed for reset: %d\n", i);
-
- if (i >= resetNr) {
- stat = DEV_GONE;
- }
-
- return stat;
-}
-
-#ifdef DEBUG_HYDRA_MSGS
-static inline void
-print_buffer(unsigned char *buffer, int bufflen)
-{
- int i;
- for (i = 0; i < bufflen; i += 16) {
- PRINTK("%04X: %02X%02X%02X%02X %02X%02X%02X%02X "
- "%02X%02X%02X%02X %02X%02X%02X%02X\n", i,
- buffer[i+0], buffer[i+1], buffer[i+2], buffer[i+3],
- buffer[i+4], buffer[i+5], buffer[i+6], buffer[i+7],
- buffer[i+8], buffer[i+9], buffer[i+10], buffer[i+11],
- buffer[i+12], buffer[i+13], buffer[i+14], buffer[i+15]);
- }
-}
-#endif
-
-enum devstat
-send_to_AP(int dev_nr, int cdx, int msg_len, unsigned char *msg_ext)
-{
- struct ap_status_word stat_word;
- enum devstat stat;
- int ccode;
- u32 *q_nr_p = (u32 *)msg_ext;
-
- *q_nr_p = (dev_nr << SKIP_BITL) + cdx;
- PDEBUG("msg_len passed to sen: %d\n", msg_len);
- PDEBUG("q number passed to sen: %02x%02x%02x%02x\n",
- msg_ext[0], msg_ext[1], msg_ext[2], msg_ext[3]);
- stat = DEV_GONE;
-
-#ifdef DEBUG_HYDRA_MSGS
- PRINTK("Request header: %02X%02X%02X%02X %02X%02X%02X%02X "
- "%02X%02X%02X%02X\n",
- msg_ext[0], msg_ext[1], msg_ext[2], msg_ext[3],
- msg_ext[4], msg_ext[5], msg_ext[6], msg_ext[7],
- msg_ext[8], msg_ext[9], msg_ext[10], msg_ext[11]);
- print_buffer(msg_ext+CALLER_HEADER, msg_len);
-#endif
-
- ccode = sen(msg_len, msg_ext, &stat_word);
- if (ccode > 3)
- return DEV_SEN_EXCEPTION;
-
- PDEBUG("nq cc: %u, st: %02x%02x%02x%02x\n",
- ccode, stat_word.q_stat_flags, stat_word.response_code,
- stat_word.reserved[0], stat_word.reserved[1]);
- switch (ccode) {
- case 0:
- stat = DEV_ONLINE;
- break;
- case 1:
- stat = DEV_GONE;
- break;
- case 3:
- switch (stat_word.response_code) {
- case AP_RESPONSE_NORMAL:
- stat = DEV_ONLINE;
- break;
- case AP_RESPONSE_Q_FULL:
- stat = DEV_QUEUE_FULL;
- break;
- default:
- stat = DEV_GONE;
- break;
- }
- break;
- default:
- stat = DEV_GONE;
- break;
- }
-
- return stat;
-}
-
-enum devstat
-receive_from_AP(int dev_nr, int cdx, int resplen, unsigned char *resp,
- unsigned char *psmid)
-{
- int ccode;
- struct ap_status_word stat_word;
- enum devstat stat;
-
- memset(resp, 0x00, 8);
-
- ccode = rec((dev_nr << SKIP_BITL) + cdx, resplen, resp, psmid,
- &stat_word);
- if (ccode > 3)
- return DEV_REC_EXCEPTION;
-
- PDEBUG("dq cc: %u, st: %02x%02x%02x%02x\n",
- ccode, stat_word.q_stat_flags, stat_word.response_code,
- stat_word.reserved[0], stat_word.reserved[1]);
-
- stat = DEV_GONE;
- switch (ccode) {
- case 0:
- stat = DEV_ONLINE;
-#ifdef DEBUG_HYDRA_MSGS
- print_buffer(resp, resplen);
-#endif
- break;
- case 3:
- switch (stat_word.response_code) {
- case AP_RESPONSE_NORMAL:
- stat = DEV_ONLINE;
- break;
- case AP_RESPONSE_NO_PENDING_REPLY:
- if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY)
- stat = DEV_EMPTY;
- else
- stat = DEV_NO_WORK;
- break;
- case AP_RESPONSE_INDEX_TOO_BIG:
- case AP_RESPONSE_NO_FIRST_PART:
- case AP_RESPONSE_MESSAGE_TOO_BIG:
- stat = DEV_BAD_MESSAGE;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
-
- return stat;
-}
-
-static inline int
-pad_msg(unsigned char *buffer, int totalLength, int msgLength)
-{
- int pad_len;
-
- for (pad_len = 0; pad_len < (totalLength - msgLength); pad_len++)
- if (buffer[pad_len] != 0x00)
- break;
- pad_len -= 3;
- if (pad_len < 8)
- return SEN_PAD_ERROR;
-
- buffer[0] = 0x00;
- buffer[1] = 0x02;
-
- memcpy(buffer+2, static_pad, pad_len);
-
- buffer[pad_len + 2] = 0x00;
-
- return 0;
-}
-
-static inline int
-is_common_public_key(unsigned char *key, int len)
-{
- int i;
-
- for (i = 0; i < len; i++)
- if (key[i])
- break;
- key += i;
- len -= i;
- if (((len == 1) && (key[0] == 3)) ||
- ((len == 3) && (key[0] == 1) && (key[1] == 0) && (key[2] == 1)))
- return 1;
-
- return 0;
-}
-
-static int
-ICAMEX_msg_to_type4MEX_msg(struct ica_rsa_modexpo *icaMex_p, int *z90cMsg_l_p,
- union type4_msg *z90cMsg_p)
-{
- int mod_len, msg_size, mod_tgt_len, exp_tgt_len, inp_tgt_len;
- unsigned char *mod_tgt, *exp_tgt, *inp_tgt;
- union type4_msg *tmp_type4_msg;
-
- mod_len = icaMex_p->inputdatalength;
-
- msg_size = ((mod_len <= 128) ? TYPE4_SME_LEN : TYPE4_LME_LEN) +
- CALLER_HEADER;
-
- memset(z90cMsg_p, 0, msg_size);
-
- tmp_type4_msg = (union type4_msg *)
- ((unsigned char *) z90cMsg_p + CALLER_HEADER);
-
- tmp_type4_msg->sme.header.msg_type_code = TYPE4_TYPE_CODE;
- tmp_type4_msg->sme.header.request_code = TYPE4_REQU_CODE;
-
- if (mod_len <= 128) {
- tmp_type4_msg->sme.header.msg_fmt = TYPE4_SME_FMT;
- tmp_type4_msg->sme.header.msg_len = TYPE4_SME_LEN;
- mod_tgt = tmp_type4_msg->sme.modulus;
- mod_tgt_len = sizeof(tmp_type4_msg->sme.modulus);
- exp_tgt = tmp_type4_msg->sme.exponent;
- exp_tgt_len = sizeof(tmp_type4_msg->sme.exponent);
- inp_tgt = tmp_type4_msg->sme.message;
- inp_tgt_len = sizeof(tmp_type4_msg->sme.message);
- } else {
- tmp_type4_msg->lme.header.msg_fmt = TYPE4_LME_FMT;
- tmp_type4_msg->lme.header.msg_len = TYPE4_LME_LEN;
- mod_tgt = tmp_type4_msg->lme.modulus;
- mod_tgt_len = sizeof(tmp_type4_msg->lme.modulus);
- exp_tgt = tmp_type4_msg->lme.exponent;
- exp_tgt_len = sizeof(tmp_type4_msg->lme.exponent);
- inp_tgt = tmp_type4_msg->lme.message;
- inp_tgt_len = sizeof(tmp_type4_msg->lme.message);
- }
-
- mod_tgt += (mod_tgt_len - mod_len);
- if (copy_from_user(mod_tgt, icaMex_p->n_modulus, mod_len))
- return SEN_RELEASED;
- if (is_empty(mod_tgt, mod_len))
- return SEN_USER_ERROR;
- exp_tgt += (exp_tgt_len - mod_len);
- if (copy_from_user(exp_tgt, icaMex_p->b_key, mod_len))
- return SEN_RELEASED;
- if (is_empty(exp_tgt, mod_len))
- return SEN_USER_ERROR;
- inp_tgt += (inp_tgt_len - mod_len);
- if (copy_from_user(inp_tgt, icaMex_p->inputdata, mod_len))
- return SEN_RELEASED;
- if (is_empty(inp_tgt, mod_len))
- return SEN_USER_ERROR;
-
- *z90cMsg_l_p = msg_size - CALLER_HEADER;
-
- return 0;
-}
-
-static int
-ICACRT_msg_to_type4CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p,
- int *z90cMsg_l_p, union type4_msg *z90cMsg_p)
-{
- int mod_len, short_len, long_len, tmp_size, p_tgt_len, q_tgt_len,
- dp_tgt_len, dq_tgt_len, u_tgt_len, inp_tgt_len;
- unsigned char *p_tgt, *q_tgt, *dp_tgt, *dq_tgt, *u_tgt, *inp_tgt;
- union type4_msg *tmp_type4_msg;
-
- mod_len = icaMsg_p->inputdatalength;
- short_len = mod_len / 2;
- long_len = mod_len / 2 + 8;
-
- tmp_size = ((mod_len <= 128) ? TYPE4_SCR_LEN : TYPE4_LCR_LEN) +
- CALLER_HEADER;
-
- memset(z90cMsg_p, 0, tmp_size);
-
- tmp_type4_msg = (union type4_msg *)
- ((unsigned char *) z90cMsg_p + CALLER_HEADER);
-
- tmp_type4_msg->scr.header.msg_type_code = TYPE4_TYPE_CODE;
- tmp_type4_msg->scr.header.request_code = TYPE4_REQU_CODE;
- if (mod_len <= 128) {
- tmp_type4_msg->scr.header.msg_fmt = TYPE4_SCR_FMT;
- tmp_type4_msg->scr.header.msg_len = TYPE4_SCR_LEN;
- p_tgt = tmp_type4_msg->scr.p;
- p_tgt_len = sizeof(tmp_type4_msg->scr.p);
- q_tgt = tmp_type4_msg->scr.q;
- q_tgt_len = sizeof(tmp_type4_msg->scr.q);
- dp_tgt = tmp_type4_msg->scr.dp;
- dp_tgt_len = sizeof(tmp_type4_msg->scr.dp);
- dq_tgt = tmp_type4_msg->scr.dq;
- dq_tgt_len = sizeof(tmp_type4_msg->scr.dq);
- u_tgt = tmp_type4_msg->scr.u;
- u_tgt_len = sizeof(tmp_type4_msg->scr.u);
- inp_tgt = tmp_type4_msg->scr.message;
- inp_tgt_len = sizeof(tmp_type4_msg->scr.message);
- } else {
- tmp_type4_msg->lcr.header.msg_fmt = TYPE4_LCR_FMT;
- tmp_type4_msg->lcr.header.msg_len = TYPE4_LCR_LEN;
- p_tgt = tmp_type4_msg->lcr.p;
- p_tgt_len = sizeof(tmp_type4_msg->lcr.p);
- q_tgt = tmp_type4_msg->lcr.q;
- q_tgt_len = sizeof(tmp_type4_msg->lcr.q);
- dp_tgt = tmp_type4_msg->lcr.dp;
- dp_tgt_len = sizeof(tmp_type4_msg->lcr.dp);
- dq_tgt = tmp_type4_msg->lcr.dq;
- dq_tgt_len = sizeof(tmp_type4_msg->lcr.dq);
- u_tgt = tmp_type4_msg->lcr.u;
- u_tgt_len = sizeof(tmp_type4_msg->lcr.u);
- inp_tgt = tmp_type4_msg->lcr.message;
- inp_tgt_len = sizeof(tmp_type4_msg->lcr.message);
- }
-
- p_tgt += (p_tgt_len - long_len);
- if (copy_from_user(p_tgt, icaMsg_p->np_prime, long_len))
- return SEN_RELEASED;
- if (is_empty(p_tgt, long_len))
- return SEN_USER_ERROR;
- q_tgt += (q_tgt_len - short_len);
- if (copy_from_user(q_tgt, icaMsg_p->nq_prime, short_len))
- return SEN_RELEASED;
- if (is_empty(q_tgt, short_len))
- return SEN_USER_ERROR;
- dp_tgt += (dp_tgt_len - long_len);
- if (copy_from_user(dp_tgt, icaMsg_p->bp_key, long_len))
- return SEN_RELEASED;
- if (is_empty(dp_tgt, long_len))
- return SEN_USER_ERROR;
- dq_tgt += (dq_tgt_len - short_len);
- if (copy_from_user(dq_tgt, icaMsg_p->bq_key, short_len))
- return SEN_RELEASED;
- if (is_empty(dq_tgt, short_len))
- return SEN_USER_ERROR;
- u_tgt += (u_tgt_len - long_len);
- if (copy_from_user(u_tgt, icaMsg_p->u_mult_inv, long_len))
- return SEN_RELEASED;
- if (is_empty(u_tgt, long_len))
- return SEN_USER_ERROR;
- inp_tgt += (inp_tgt_len - mod_len);
- if (copy_from_user(inp_tgt, icaMsg_p->inputdata, mod_len))
- return SEN_RELEASED;
- if (is_empty(inp_tgt, mod_len))
- return SEN_USER_ERROR;
-
- *z90cMsg_l_p = tmp_size - CALLER_HEADER;
-
- return 0;
-}
-
-static int
-ICAMEX_msg_to_type6MEX_de_msg(struct ica_rsa_modexpo *icaMsg_p, int cdx,
- int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
-{
- int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l;
- unsigned char *temp;
- struct type6_hdr *tp6Hdr_p;
- struct CPRB *cprb_p;
- struct cca_private_ext_ME *key_p;
- static int deprecated_msg_count = 0;
-
- mod_len = icaMsg_p->inputdatalength;
- tmp_size = FIXED_TYPE6_ME_LEN + mod_len;
- total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
- parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
- tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
-
- memset(z90cMsg_p, 0, tmp_size);
-
- temp = (unsigned char *)z90cMsg_p + CALLER_HEADER;
- memcpy(temp, &static_type6_hdr, sizeof(struct type6_hdr));
- tp6Hdr_p = (struct type6_hdr *)temp;
- tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4);
- tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
-
- temp += sizeof(struct type6_hdr);
- memcpy(temp, &static_cprb, sizeof(struct CPRB));
- cprb_p = (struct CPRB *) temp;
- cprb_p->usage_domain[0]= (unsigned char)cdx;
- itoLe2(&parmBlock_l, cprb_p->req_parml);
- itoLe2((int *)&(tp6Hdr_p->FromCardLen1), cprb_p->rpl_parml);
-
- temp += sizeof(struct CPRB);
- memcpy(temp, &static_pkd_function_and_rules,
- sizeof(struct function_and_rules_block));
-
- temp += sizeof(struct function_and_rules_block);
- vud_len = 2 + icaMsg_p->inputdatalength;
- itoLe2(&vud_len, temp);
-
- temp += 2;
- if (copy_from_user(temp, icaMsg_p->inputdata, mod_len))
- return SEN_RELEASED;
- if (is_empty(temp, mod_len))
- return SEN_USER_ERROR;
-
- temp += mod_len;
- memcpy(temp, &static_T6_keyBlock_hdr, sizeof(struct T6_keyBlock_hdr));
-
- temp += sizeof(struct T6_keyBlock_hdr);
- memcpy(temp, &static_pvt_me_key, sizeof(struct cca_private_ext_ME));
- key_p = (struct cca_private_ext_ME *)temp;
- temp = key_p->pvtMESec.exponent + sizeof(key_p->pvtMESec.exponent)
- - mod_len;
- if (copy_from_user(temp, icaMsg_p->b_key, mod_len))
- return SEN_RELEASED;
- if (is_empty(temp, mod_len))
- return SEN_USER_ERROR;
-
- if (is_common_public_key(temp, mod_len)) {
- if (deprecated_msg_count < 20) {
- PRINTK("Common public key used for modex decrypt\n");
- deprecated_msg_count++;
- if (deprecated_msg_count == 20)
- PRINTK("No longer issuing messages about common"
- " public key for modex decrypt.\n");
- }
- return SEN_NOT_AVAIL;
- }
-
- temp = key_p->pvtMESec.modulus + sizeof(key_p->pvtMESec.modulus)
- - mod_len;
- if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
- return SEN_RELEASED;
- if (is_empty(temp, mod_len))
- return SEN_USER_ERROR;
-
- key_p->pubMESec.modulus_bit_len = 8 * mod_len;
-
- *z90cMsg_l_p = tmp_size - CALLER_HEADER;
-
- return 0;
-}
-
-static int
-ICAMEX_msg_to_type6MEX_en_msg(struct ica_rsa_modexpo *icaMsg_p, int cdx,
- int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
-{
- int mod_len, vud_len, exp_len, key_len;
- int pad_len, tmp_size, total_CPRB_len, parmBlock_l, i;
- unsigned char *temp_exp, *exp_p, *temp;
- struct type6_hdr *tp6Hdr_p;
- struct CPRB *cprb_p;
- struct cca_public_key *key_p;
- struct T6_keyBlock_hdr *keyb_p;
-
- temp_exp = kmalloc(256, GFP_KERNEL);
- if (!temp_exp)
- return EGETBUFF;
- mod_len = icaMsg_p->inputdatalength;
- if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len)) {
- kfree(temp_exp);
- return SEN_RELEASED;
- }
- if (is_empty(temp_exp, mod_len)) {
- kfree(temp_exp);
- return SEN_USER_ERROR;
- }
-
- exp_p = temp_exp;
- for (i = 0; i < mod_len; i++)
- if (exp_p[i])
- break;
- if (i >= mod_len) {
- kfree(temp_exp);
- return SEN_USER_ERROR;
- }
-
- exp_len = mod_len - i;
- exp_p += i;
-
- PDEBUG("exp_len after computation: %08x\n", exp_len);
- tmp_size = FIXED_TYPE6_ME_EN_LEN + 2 * mod_len + exp_len;
- total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
- parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
- tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
-
- vud_len = 2 + mod_len;
- memset(z90cMsg_p, 0, tmp_size);
-
- temp = (unsigned char *)z90cMsg_p + CALLER_HEADER;
- memcpy(temp, &static_type6_hdr, sizeof(struct type6_hdr));
- tp6Hdr_p = (struct type6_hdr *)temp;
- tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4);
- tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
- memcpy(tp6Hdr_p->function_code, static_PKE_function_code,
- sizeof(static_PKE_function_code));
- temp += sizeof(struct type6_hdr);
- memcpy(temp, &static_cprb, sizeof(struct CPRB));
- cprb_p = (struct CPRB *) temp;
- cprb_p->usage_domain[0]= (unsigned char)cdx;
- itoLe2((int *)&(tp6Hdr_p->FromCardLen1), cprb_p->rpl_parml);
- temp += sizeof(struct CPRB);
- memcpy(temp, &static_pke_function_and_rules,
- sizeof(struct function_and_rules_block));
- temp += sizeof(struct function_and_rules_block);
- temp += 2;
- if (copy_from_user(temp, icaMsg_p->inputdata, mod_len)) {
- kfree(temp_exp);
- return SEN_RELEASED;
- }
- if (is_empty(temp, mod_len)) {
- kfree(temp_exp);
- return SEN_USER_ERROR;
- }
- if ((temp[0] != 0x00) || (temp[1] != 0x02)) {
- kfree(temp_exp);
- return SEN_NOT_AVAIL;
- }
- for (i = 2; i < mod_len; i++)
- if (temp[i] == 0x00)
- break;
- if ((i < 9) || (i > (mod_len - 2))) {
- kfree(temp_exp);
- return SEN_NOT_AVAIL;
- }
- pad_len = i + 1;
- vud_len = mod_len - pad_len;
- memmove(temp, temp+pad_len, vud_len);
- temp -= 2;
- vud_len += 2;
- itoLe2(&vud_len, temp);
- temp += (vud_len);
- keyb_p = (struct T6_keyBlock_hdr *)temp;
- temp += sizeof(struct T6_keyBlock_hdr);
- memcpy(temp, &static_public_key, sizeof(static_public_key));
- key_p = (struct cca_public_key *)temp;
- temp = key_p->pubSec.exponent;
- memcpy(temp, exp_p, exp_len);
- kfree(temp_exp);
- temp += exp_len;
- if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
- return SEN_RELEASED;
- if (is_empty(temp, mod_len))
- return SEN_USER_ERROR;
- key_p->pubSec.modulus_bit_len = 8 * mod_len;
- key_p->pubSec.modulus_byte_len = mod_len;
- key_p->pubSec.exponent_len = exp_len;
- key_p->pubSec.section_length = CALLER_HEADER + mod_len + exp_len;
- key_len = key_p->pubSec.section_length + sizeof(struct cca_token_hdr);
- key_p->pubHdr.token_length = key_len;
- key_len += 4;
- itoLe2(&key_len, keyb_p->ulen);
- key_len += 2;
- itoLe2(&key_len, keyb_p->blen);
- parmBlock_l -= pad_len;
- itoLe2(&parmBlock_l, cprb_p->req_parml);
- *z90cMsg_l_p = tmp_size - CALLER_HEADER;
-
- return 0;
-}
-
-static int
-ICACRT_msg_to_type6CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p, int cdx,
- int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
-{
- int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l, short_len;
- int long_len, pad_len, keyPartsLen, tmp_l;
- unsigned char *tgt_p, *temp;
- struct type6_hdr *tp6Hdr_p;
- struct CPRB *cprb_p;
- struct cca_token_hdr *keyHdr_p;
- struct cca_pvt_ext_CRT_sec *pvtSec_p;
- struct cca_public_sec *pubSec_p;
-
- mod_len = icaMsg_p->inputdatalength;
- short_len = mod_len / 2;
- long_len = 8 + short_len;
- keyPartsLen = 3 * long_len + 2 * short_len;
- pad_len = (8 - (keyPartsLen % 8)) % 8;
- keyPartsLen += pad_len + mod_len;
- tmp_size = FIXED_TYPE6_CR_LEN + keyPartsLen + mod_len;
- total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
- parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
- vud_len = 2 + mod_len;
- tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
-
- memset(z90cMsg_p, 0, tmp_size);
- tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
- memcpy(tgt_p, &static_type6_hdr, sizeof(struct type6_hdr));
- tp6Hdr_p = (struct type6_hdr *)tgt_p;
- tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4);
- tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
- tgt_p += sizeof(struct type6_hdr);
- cprb_p = (struct CPRB *) tgt_p;
- memcpy(tgt_p, &static_cprb, sizeof(struct CPRB));
- cprb_p->usage_domain[0]= *((unsigned char *)(&(cdx))+3);
- itoLe2(&parmBlock_l, cprb_p->req_parml);
- memcpy(cprb_p->rpl_parml, cprb_p->req_parml,
- sizeof(cprb_p->req_parml));
- tgt_p += sizeof(struct CPRB);
- memcpy(tgt_p, &static_pkd_function_and_rules,
- sizeof(struct function_and_rules_block));
- tgt_p += sizeof(struct function_and_rules_block);
- itoLe2(&vud_len, tgt_p);
- tgt_p += 2;
- if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, mod_len))
- return SEN_USER_ERROR;
- tgt_p += mod_len;
- tmp_l = sizeof(struct T6_keyBlock_hdr) + sizeof(struct cca_token_hdr) +
- sizeof(struct cca_pvt_ext_CRT_sec) + 0x0F + keyPartsLen;
- itoLe2(&tmp_l, tgt_p);
- temp = tgt_p + 2;
- tmp_l -= 2;
- itoLe2(&tmp_l, temp);
- tgt_p += sizeof(struct T6_keyBlock_hdr);
- keyHdr_p = (struct cca_token_hdr *)tgt_p;
- keyHdr_p->token_identifier = CCA_TKN_HDR_ID_EXT;
- tmp_l -= 4;
- keyHdr_p->token_length = tmp_l;
- tgt_p += sizeof(struct cca_token_hdr);
- pvtSec_p = (struct cca_pvt_ext_CRT_sec *)tgt_p;
- pvtSec_p->section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT;
- pvtSec_p->section_length =
- sizeof(struct cca_pvt_ext_CRT_sec) + keyPartsLen;
- pvtSec_p->key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL;
- pvtSec_p->key_use_flags[0] = CCA_PVT_USAGE_ALL;
- pvtSec_p->p_len = long_len;
- pvtSec_p->q_len = short_len;
- pvtSec_p->dp_len = long_len;
- pvtSec_p->dq_len = short_len;
- pvtSec_p->u_len = long_len;
- pvtSec_p->mod_len = mod_len;
- pvtSec_p->pad_len = pad_len;
- tgt_p += sizeof(struct cca_pvt_ext_CRT_sec);
- if (copy_from_user(tgt_p, icaMsg_p->np_prime, long_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, long_len))
- return SEN_USER_ERROR;
- tgt_p += long_len;
- if (copy_from_user(tgt_p, icaMsg_p->nq_prime, short_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, short_len))
- return SEN_USER_ERROR;
- tgt_p += short_len;
- if (copy_from_user(tgt_p, icaMsg_p->bp_key, long_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, long_len))
- return SEN_USER_ERROR;
- tgt_p += long_len;
- if (copy_from_user(tgt_p, icaMsg_p->bq_key, short_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, short_len))
- return SEN_USER_ERROR;
- tgt_p += short_len;
- if (copy_from_user(tgt_p, icaMsg_p->u_mult_inv, long_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, long_len))
- return SEN_USER_ERROR;
- tgt_p += long_len;
- tgt_p += pad_len;
- memset(tgt_p, 0xFF, mod_len);
- tgt_p += mod_len;
- memcpy(tgt_p, &static_cca_pub_sec, sizeof(struct cca_public_sec));
- pubSec_p = (struct cca_public_sec *) tgt_p;
- pubSec_p->modulus_bit_len = 8 * mod_len;
- *z90cMsg_l_p = tmp_size - CALLER_HEADER;
-
- return 0;
-}
-
-static int
-ICAMEX_msg_to_type6MEX_msgX(struct ica_rsa_modexpo *icaMsg_p, int cdx,
- int *z90cMsg_l_p, struct type6_msg *z90cMsg_p,
- int dev_type)
-{
- int mod_len, exp_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l;
- int key_len, i;
- unsigned char *temp_exp, *tgt_p, *temp, *exp_p;
- struct type6_hdr *tp6Hdr_p;
- struct CPRBX *cprbx_p;
- struct cca_public_key *key_p;
- struct T6_keyBlock_hdrX *keyb_p;
-
- temp_exp = kmalloc(256, GFP_KERNEL);
- if (!temp_exp)
- return EGETBUFF;
- mod_len = icaMsg_p->inputdatalength;
- if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len)) {
- kfree(temp_exp);
- return SEN_RELEASED;
- }
- if (is_empty(temp_exp, mod_len)) {
- kfree(temp_exp);
- return SEN_USER_ERROR;
- }
- exp_p = temp_exp;
- for (i = 0; i < mod_len; i++)
- if (exp_p[i])
- break;
- if (i >= mod_len) {
- kfree(temp_exp);
- return SEN_USER_ERROR;
- }
- exp_len = mod_len - i;
- exp_p += i;
- PDEBUG("exp_len after computation: %08x\n", exp_len);
- tmp_size = FIXED_TYPE6_ME_EN_LENX + 2 * mod_len + exp_len;
- total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
- parmBlock_l = total_CPRB_len - sizeof(struct CPRBX);
- tmp_size = tmp_size + CALLER_HEADER;
- vud_len = 2 + mod_len;
- memset(z90cMsg_p, 0, tmp_size);
- tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
- memcpy(tgt_p, &static_type6_hdrX, sizeof(struct type6_hdr));
- tp6Hdr_p = (struct type6_hdr *)tgt_p;
- tp6Hdr_p->ToCardLen1 = total_CPRB_len;
- tp6Hdr_p->FromCardLen1 = RESPONSE_CPRBX_SIZE;
- memcpy(tp6Hdr_p->function_code, static_PKE_function_code,
- sizeof(static_PKE_function_code));
- tgt_p += sizeof(struct type6_hdr);
- memcpy(tgt_p, &static_cprbx, sizeof(struct CPRBX));
- cprbx_p = (struct CPRBX *) tgt_p;
- cprbx_p->domain = (unsigned short)cdx;
- cprbx_p->rpl_msgbl = RESPONSE_CPRBX_SIZE;
- tgt_p += sizeof(struct CPRBX);
- if (dev_type == PCIXCC_MCL2)
- memcpy(tgt_p, &static_pke_function_and_rulesX_MCL2,
- sizeof(struct function_and_rules_block));
- else
- memcpy(tgt_p, &static_pke_function_and_rulesX,
- sizeof(struct function_and_rules_block));
- tgt_p += sizeof(struct function_and_rules_block);
-
- tgt_p += 2;
- if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len)) {
- kfree(temp_exp);
- return SEN_RELEASED;
- }
- if (is_empty(tgt_p, mod_len)) {
- kfree(temp_exp);
- return SEN_USER_ERROR;
- }
- tgt_p -= 2;
- *((short *)tgt_p) = (short) vud_len;
- tgt_p += vud_len;
- keyb_p = (struct T6_keyBlock_hdrX *)tgt_p;
- tgt_p += sizeof(struct T6_keyBlock_hdrX);
- memcpy(tgt_p, &static_public_key, sizeof(static_public_key));
- key_p = (struct cca_public_key *)tgt_p;
- temp = key_p->pubSec.exponent;
- memcpy(temp, exp_p, exp_len);
- kfree(temp_exp);
- temp += exp_len;
- if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
- return SEN_RELEASED;
- if (is_empty(temp, mod_len))
- return SEN_USER_ERROR;
- key_p->pubSec.modulus_bit_len = 8 * mod_len;
- key_p->pubSec.modulus_byte_len = mod_len;
- key_p->pubSec.exponent_len = exp_len;
- key_p->pubSec.section_length = CALLER_HEADER + mod_len + exp_len;
- key_len = key_p->pubSec.section_length + sizeof(struct cca_token_hdr);
- key_p->pubHdr.token_length = key_len;
- key_len += 4;
- keyb_p->ulen = (unsigned short)key_len;
- key_len += 2;
- keyb_p->blen = (unsigned short)key_len;
- cprbx_p->req_parml = parmBlock_l;
- *z90cMsg_l_p = tmp_size - CALLER_HEADER;
-
- return 0;
-}
-
-static int
-ICACRT_msg_to_type6CRT_msgX(struct ica_rsa_modexpo_crt *icaMsg_p, int cdx,
- int *z90cMsg_l_p, struct type6_msg *z90cMsg_p,
- int dev_type)
-{
- int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l, short_len;
- int long_len, pad_len, keyPartsLen, tmp_l;
- unsigned char *tgt_p, *temp;
- struct type6_hdr *tp6Hdr_p;
- struct CPRBX *cprbx_p;
- struct cca_token_hdr *keyHdr_p;
- struct cca_pvt_ext_CRT_sec *pvtSec_p;
- struct cca_public_sec *pubSec_p;
-
- mod_len = icaMsg_p->inputdatalength;
- short_len = mod_len / 2;
- long_len = 8 + short_len;
- keyPartsLen = 3 * long_len + 2 * short_len;
- pad_len = (8 - (keyPartsLen % 8)) % 8;
- keyPartsLen += pad_len + mod_len;
- tmp_size = FIXED_TYPE6_CR_LENX + keyPartsLen + mod_len;
- total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
- parmBlock_l = total_CPRB_len - sizeof(struct CPRBX);
- vud_len = 2 + mod_len;
- tmp_size = tmp_size + CALLER_HEADER;
- memset(z90cMsg_p, 0, tmp_size);
- tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
- memcpy(tgt_p, &static_type6_hdrX, sizeof(struct type6_hdr));
- tp6Hdr_p = (struct type6_hdr *)tgt_p;
- tp6Hdr_p->ToCardLen1 = total_CPRB_len;
- tp6Hdr_p->FromCardLen1 = RESPONSE_CPRBX_SIZE;
- tgt_p += sizeof(struct type6_hdr);
- cprbx_p = (struct CPRBX *) tgt_p;
- memcpy(tgt_p, &static_cprbx, sizeof(struct CPRBX));
- cprbx_p->domain = (unsigned short)cdx;
- cprbx_p->req_parml = parmBlock_l;
- cprbx_p->rpl_msgbl = parmBlock_l;
- tgt_p += sizeof(struct CPRBX);
- if (dev_type == PCIXCC_MCL2)
- memcpy(tgt_p, &static_pkd_function_and_rulesX_MCL2,
- sizeof(struct function_and_rules_block));
- else
- memcpy(tgt_p, &static_pkd_function_and_rulesX,
- sizeof(struct function_and_rules_block));
- tgt_p += sizeof(struct function_and_rules_block);
- *((short *)tgt_p) = (short) vud_len;
- tgt_p += 2;
- if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, mod_len))
- return SEN_USER_ERROR;
- tgt_p += mod_len;
- tmp_l = sizeof(struct T6_keyBlock_hdr) + sizeof(struct cca_token_hdr) +
- sizeof(struct cca_pvt_ext_CRT_sec) + 0x0F + keyPartsLen;
- *((short *)tgt_p) = (short) tmp_l;
- temp = tgt_p + 2;
- tmp_l -= 2;
- *((short *)temp) = (short) tmp_l;
- tgt_p += sizeof(struct T6_keyBlock_hdr);
- keyHdr_p = (struct cca_token_hdr *)tgt_p;
- keyHdr_p->token_identifier = CCA_TKN_HDR_ID_EXT;
- tmp_l -= 4;
- keyHdr_p->token_length = tmp_l;
- tgt_p += sizeof(struct cca_token_hdr);
- pvtSec_p = (struct cca_pvt_ext_CRT_sec *)tgt_p;
- pvtSec_p->section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT;
- pvtSec_p->section_length =
- sizeof(struct cca_pvt_ext_CRT_sec) + keyPartsLen;
- pvtSec_p->key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL;
- pvtSec_p->key_use_flags[0] = CCA_PVT_USAGE_ALL;
- pvtSec_p->p_len = long_len;
- pvtSec_p->q_len = short_len;
- pvtSec_p->dp_len = long_len;
- pvtSec_p->dq_len = short_len;
- pvtSec_p->u_len = long_len;
- pvtSec_p->mod_len = mod_len;
- pvtSec_p->pad_len = pad_len;
- tgt_p += sizeof(struct cca_pvt_ext_CRT_sec);
- if (copy_from_user(tgt_p, icaMsg_p->np_prime, long_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, long_len))
- return SEN_USER_ERROR;
- tgt_p += long_len;
- if (copy_from_user(tgt_p, icaMsg_p->nq_prime, short_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, short_len))
- return SEN_USER_ERROR;
- tgt_p += short_len;
- if (copy_from_user(tgt_p, icaMsg_p->bp_key, long_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, long_len))
- return SEN_USER_ERROR;
- tgt_p += long_len;
- if (copy_from_user(tgt_p, icaMsg_p->bq_key, short_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, short_len))
- return SEN_USER_ERROR;
- tgt_p += short_len;
- if (copy_from_user(tgt_p, icaMsg_p->u_mult_inv, long_len))
- return SEN_RELEASED;
- if (is_empty(tgt_p, long_len))
- return SEN_USER_ERROR;
- tgt_p += long_len;
- tgt_p += pad_len;
- memset(tgt_p, 0xFF, mod_len);
- tgt_p += mod_len;
- memcpy(tgt_p, &static_cca_pub_sec, sizeof(struct cca_public_sec));
- pubSec_p = (struct cca_public_sec *) tgt_p;
- pubSec_p->modulus_bit_len = 8 * mod_len;
- *z90cMsg_l_p = tmp_size - CALLER_HEADER;
-
- return 0;
-}
-
-static int
-ICAMEX_msg_to_type50MEX_msg(struct ica_rsa_modexpo *icaMex_p, int *z90cMsg_l_p,
- union type50_msg *z90cMsg_p)
-{
- int mod_len, msg_size, mod_tgt_len, exp_tgt_len, inp_tgt_len;
- unsigned char *mod_tgt, *exp_tgt, *inp_tgt;
- union type50_msg *tmp_type50_msg;
-
- mod_len = icaMex_p->inputdatalength;
-
- msg_size = ((mod_len <= 128) ? TYPE50_MEB1_LEN : TYPE50_MEB2_LEN) +
- CALLER_HEADER;
-
- memset(z90cMsg_p, 0, msg_size);
-
- tmp_type50_msg = (union type50_msg *)
- ((unsigned char *) z90cMsg_p + CALLER_HEADER);
-
- tmp_type50_msg->meb1.header.msg_type_code = TYPE50_TYPE_CODE;
-
- if (mod_len <= 128) {
- tmp_type50_msg->meb1.header.msg_len = TYPE50_MEB1_LEN;
- tmp_type50_msg->meb1.keyblock_type = TYPE50_MEB1_FMT;
- mod_tgt = tmp_type50_msg->meb1.modulus;
- mod_tgt_len = sizeof(tmp_type50_msg->meb1.modulus);
- exp_tgt = tmp_type50_msg->meb1.exponent;
- exp_tgt_len = sizeof(tmp_type50_msg->meb1.exponent);
- inp_tgt = tmp_type50_msg->meb1.message;
- inp_tgt_len = sizeof(tmp_type50_msg->meb1.message);
- } else {
- tmp_type50_msg->meb2.header.msg_len = TYPE50_MEB2_LEN;
- tmp_type50_msg->meb2.keyblock_type = TYPE50_MEB2_FMT;
- mod_tgt = tmp_type50_msg->meb2.modulus;
- mod_tgt_len = sizeof(tmp_type50_msg->meb2.modulus);
- exp_tgt = tmp_type50_msg->meb2.exponent;
- exp_tgt_len = sizeof(tmp_type50_msg->meb2.exponent);
- inp_tgt = tmp_type50_msg->meb2.message;
- inp_tgt_len = sizeof(tmp_type50_msg->meb2.message);
- }
-
- mod_tgt += (mod_tgt_len - mod_len);
- if (copy_from_user(mod_tgt, icaMex_p->n_modulus, mod_len))
- return SEN_RELEASED;
- if (is_empty(mod_tgt, mod_len))
- return SEN_USER_ERROR;
- exp_tgt += (exp_tgt_len - mod_len);
- if (copy_from_user(exp_tgt, icaMex_p->b_key, mod_len))
- return SEN_RELEASED;
- if (is_empty(exp_tgt, mod_len))
- return SEN_USER_ERROR;
- inp_tgt += (inp_tgt_len - mod_len);
- if (copy_from_user(inp_tgt, icaMex_p->inputdata, mod_len))
- return SEN_RELEASED;
- if (is_empty(inp_tgt, mod_len))
- return SEN_USER_ERROR;
-
- *z90cMsg_l_p = msg_size - CALLER_HEADER;
-
- return 0;
-}
-
-static int
-ICACRT_msg_to_type50CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p,
- int *z90cMsg_l_p, union type50_msg *z90cMsg_p)
-{
- int mod_len, short_len, long_len, tmp_size, p_tgt_len, q_tgt_len,
- dp_tgt_len, dq_tgt_len, u_tgt_len, inp_tgt_len, long_offset;
- unsigned char *p_tgt, *q_tgt, *dp_tgt, *dq_tgt, *u_tgt, *inp_tgt,
- temp[8];
- union type50_msg *tmp_type50_msg;
-
- mod_len = icaMsg_p->inputdatalength;
- short_len = mod_len / 2;
- long_len = mod_len / 2 + 8;
- long_offset = 0;
-
- if (long_len > 128) {
- memset(temp, 0x00, sizeof(temp));
- if (copy_from_user(temp, icaMsg_p->np_prime, long_len-128))
- return SEN_RELEASED;
- if (!is_empty(temp, 8))
- return SEN_NOT_AVAIL;
- if (copy_from_user(temp, icaMsg_p->bp_key, long_len-128))
- return SEN_RELEASED;
- if (!is_empty(temp, 8))
- return SEN_NOT_AVAIL;
- if (copy_from_user(temp, icaMsg_p->u_mult_inv, long_len-128))
- return SEN_RELEASED;
- if (!is_empty(temp, 8))
- return SEN_NOT_AVAIL;
- long_offset = long_len - 128;
- long_len = 128;
- }
-
- tmp_size = ((long_len <= 64) ? TYPE50_CRB1_LEN : TYPE50_CRB2_LEN) +
- CALLER_HEADER;
-
- memset(z90cMsg_p, 0, tmp_size);
-
- tmp_type50_msg = (union type50_msg *)
- ((unsigned char *) z90cMsg_p + CALLER_HEADER);
-
- tmp_type50_msg->crb1.header.msg_type_code = TYPE50_TYPE_CODE;
- if (long_len <= 64) {
- tmp_type50_msg->crb1.header.msg_len = TYPE50_CRB1_LEN;
- tmp_type50_msg->crb1.keyblock_type = TYPE50_CRB1_FMT;
- p_tgt = tmp_type50_msg->crb1.p;
- p_tgt_len = sizeof(tmp_type50_msg->crb1.p);
- q_tgt = tmp_type50_msg->crb1.q;
- q_tgt_len = sizeof(tmp_type50_msg->crb1.q);
- dp_tgt = tmp_type50_msg->crb1.dp;
- dp_tgt_len = sizeof(tmp_type50_msg->crb1.dp);
- dq_tgt = tmp_type50_msg->crb1.dq;
- dq_tgt_len = sizeof(tmp_type50_msg->crb1.dq);
- u_tgt = tmp_type50_msg->crb1.u;
- u_tgt_len = sizeof(tmp_type50_msg->crb1.u);
- inp_tgt = tmp_type50_msg->crb1.message;
- inp_tgt_len = sizeof(tmp_type50_msg->crb1.message);
- } else {
- tmp_type50_msg->crb2.header.msg_len = TYPE50_CRB2_LEN;
- tmp_type50_msg->crb2.keyblock_type = TYPE50_CRB2_FMT;
- p_tgt = tmp_type50_msg->crb2.p;
- p_tgt_len = sizeof(tmp_type50_msg->crb2.p);
- q_tgt = tmp_type50_msg->crb2.q;
- q_tgt_len = sizeof(tmp_type50_msg->crb2.q);
- dp_tgt = tmp_type50_msg->crb2.dp;
- dp_tgt_len = sizeof(tmp_type50_msg->crb2.dp);
- dq_tgt = tmp_type50_msg->crb2.dq;
- dq_tgt_len = sizeof(tmp_type50_msg->crb2.dq);
- u_tgt = tmp_type50_msg->crb2.u;
- u_tgt_len = sizeof(tmp_type50_msg->crb2.u);
- inp_tgt = tmp_type50_msg->crb2.message;
- inp_tgt_len = sizeof(tmp_type50_msg->crb2.message);
- }
-
- p_tgt += (p_tgt_len - long_len);
- if (copy_from_user(p_tgt, icaMsg_p->np_prime + long_offset, long_len))
- return SEN_RELEASED;
- if (is_empty(p_tgt, long_len))
- return SEN_USER_ERROR;
- q_tgt += (q_tgt_len - short_len);
- if (copy_from_user(q_tgt, icaMsg_p->nq_prime, short_len))
- return SEN_RELEASED;
- if (is_empty(q_tgt, short_len))
- return SEN_USER_ERROR;
- dp_tgt += (dp_tgt_len - long_len);
- if (copy_from_user(dp_tgt, icaMsg_p->bp_key + long_offset, long_len))
- return SEN_RELEASED;
- if (is_empty(dp_tgt, long_len))
- return SEN_USER_ERROR;
- dq_tgt += (dq_tgt_len - short_len);
- if (copy_from_user(dq_tgt, icaMsg_p->bq_key, short_len))
- return SEN_RELEASED;
- if (is_empty(dq_tgt, short_len))
- return SEN_USER_ERROR;
- u_tgt += (u_tgt_len - long_len);
- if (copy_from_user(u_tgt, icaMsg_p->u_mult_inv + long_offset, long_len))
- return SEN_RELEASED;
- if (is_empty(u_tgt, long_len))
- return SEN_USER_ERROR;
- inp_tgt += (inp_tgt_len - mod_len);
- if (copy_from_user(inp_tgt, icaMsg_p->inputdata, mod_len))
- return SEN_RELEASED;
- if (is_empty(inp_tgt, mod_len))
- return SEN_USER_ERROR;
-
- *z90cMsg_l_p = tmp_size - CALLER_HEADER;
-
- return 0;
-}
-
-int
-convert_request(unsigned char *buffer, int func, unsigned short function,
- int cdx, int dev_type, int *msg_l_p, unsigned char *msg_p)
-{
- if (dev_type == PCICA) {
- if (func == ICARSACRT)
- return ICACRT_msg_to_type4CRT_msg(
- (struct ica_rsa_modexpo_crt *) buffer,
- msg_l_p, (union type4_msg *) msg_p);
- else
- return ICAMEX_msg_to_type4MEX_msg(
- (struct ica_rsa_modexpo *) buffer,
- msg_l_p, (union type4_msg *) msg_p);
- }
- if (dev_type == PCICC) {
- if (func == ICARSACRT)
- return ICACRT_msg_to_type6CRT_msg(
- (struct ica_rsa_modexpo_crt *) buffer,
- cdx, msg_l_p, (struct type6_msg *)msg_p);
- if (function == PCI_FUNC_KEY_ENCRYPT)
- return ICAMEX_msg_to_type6MEX_en_msg(
- (struct ica_rsa_modexpo *) buffer,
- cdx, msg_l_p, (struct type6_msg *) msg_p);
- else
- return ICAMEX_msg_to_type6MEX_de_msg(
- (struct ica_rsa_modexpo *) buffer,
- cdx, msg_l_p, (struct type6_msg *) msg_p);
- }
- if ((dev_type == PCIXCC_MCL2) ||
- (dev_type == PCIXCC_MCL3) ||
- (dev_type == CEX2C)) {
- if (func == ICARSACRT)
- return ICACRT_msg_to_type6CRT_msgX(
- (struct ica_rsa_modexpo_crt *) buffer,
- cdx, msg_l_p, (struct type6_msg *) msg_p,
- dev_type);
- else
- return ICAMEX_msg_to_type6MEX_msgX(
- (struct ica_rsa_modexpo *) buffer,
- cdx, msg_l_p, (struct type6_msg *) msg_p,
- dev_type);
- }
- if (dev_type == CEX2A) {
- if (func == ICARSACRT)
- return ICACRT_msg_to_type50CRT_msg(
- (struct ica_rsa_modexpo_crt *) buffer,
- msg_l_p, (union type50_msg *) msg_p);
- else
- return ICAMEX_msg_to_type50MEX_msg(
- (struct ica_rsa_modexpo *) buffer,
- msg_l_p, (union type50_msg *) msg_p);
- }
-
- return 0;
-}
-
-int ext_bitlens_msg_count = 0;
-static inline void
-unset_ext_bitlens(void)
-{
- if (!ext_bitlens_msg_count) {
- PRINTK("Unable to use coprocessors for extended bitlengths. "
- "Using PCICAs/CEX2As (if present) for extended "
- "bitlengths. This is not an error.\n");
- ext_bitlens_msg_count++;
- }
- ext_bitlens = 0;
-}
-
-int
-convert_response(unsigned char *response, unsigned char *buffer,
- int *respbufflen_p, unsigned char *resp_buff)
-{
- struct ica_rsa_modexpo *icaMsg_p = (struct ica_rsa_modexpo *) buffer;
- struct error_hdr *errh_p = (struct error_hdr *) response;
- struct type80_hdr *t80h_p = (struct type80_hdr *) response;
- struct type84_hdr *t84h_p = (struct type84_hdr *) response;
- struct type86_fmt2_msg *t86m_p = (struct type86_fmt2_msg *) response;
- int reply_code, service_rc, service_rs, src_l;
- unsigned char *src_p, *tgt_p;
- struct CPRB *cprb_p;
- struct CPRBX *cprbx_p;
-
- src_p = 0;
- reply_code = 0;
- service_rc = 0;
- service_rs = 0;
- src_l = 0;
- switch (errh_p->type) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- reply_code = errh_p->reply_code;
- src_p = (unsigned char *)errh_p;
- PRINTK("Hardware error: Type %02X Message Header: "
- "%02x%02x%02x%02x%02x%02x%02x%02x\n",
- errh_p->type,
- src_p[0], src_p[1], src_p[2], src_p[3],
- src_p[4], src_p[5], src_p[6], src_p[7]);
- break;
- case TYPE80_RSP_CODE:
- src_l = icaMsg_p->outputdatalength;
- src_p = response + (int)t80h_p->len - src_l;
- break;
- case TYPE84_RSP_CODE:
- src_l = icaMsg_p->outputdatalength;
- src_p = response + (int)t84h_p->len - src_l;
- break;
- case TYPE86_RSP_CODE:
- reply_code = t86m_p->header.reply_code;
- if (reply_code != 0)
- break;
- cprb_p = (struct CPRB *)
- (response + sizeof(struct type86_fmt2_msg));
- cprbx_p = (struct CPRBX *) cprb_p;
- if (cprb_p->cprb_ver_id != 0x02) {
- le2toI(cprb_p->ccp_rtcode, &service_rc);
- if (service_rc != 0) {
- le2toI(cprb_p->ccp_rscode, &service_rs);
- if ((service_rc == 8) && (service_rs == 66))
- PDEBUG("Bad block format on PCICC\n");
- else if ((service_rc == 8) && (service_rs == 65))
- PDEBUG("Probably an even modulus on "
- "PCICC\n");
- else if ((service_rc == 8) && (service_rs == 770)) {
- PDEBUG("Invalid key length on PCICC\n");
- unset_ext_bitlens();
- return REC_USE_PCICA;
- }
- else if ((service_rc == 8) && (service_rs == 783)) {
- PDEBUG("Extended bitlengths not enabled"
- "on PCICC\n");
- unset_ext_bitlens();
- return REC_USE_PCICA;
- }
- else
- PRINTK("service rc/rs (PCICC): %d/%d\n",
- service_rc, service_rs);
- return REC_OPERAND_INV;
- }
- src_p = (unsigned char *)cprb_p + sizeof(struct CPRB);
- src_p += 4;
- le2toI(src_p, &src_l);
- src_l -= 2;
- src_p += 2;
- } else {
- service_rc = (int)cprbx_p->ccp_rtcode;
- if (service_rc != 0) {
- service_rs = (int) cprbx_p->ccp_rscode;
- if ((service_rc == 8) && (service_rs == 66))
- PDEBUG("Bad block format on PCIXCC\n");
- else if ((service_rc == 8) && (service_rs == 65))
- PDEBUG("Probably an even modulus on "
- "PCIXCC\n");
- else if ((service_rc == 8) && (service_rs == 770)) {
- PDEBUG("Invalid key length on PCIXCC\n");
- unset_ext_bitlens();
- return REC_USE_PCICA;
- }
- else if ((service_rc == 8) && (service_rs == 783)) {
- PDEBUG("Extended bitlengths not enabled"
- "on PCIXCC\n");
- unset_ext_bitlens();
- return REC_USE_PCICA;
- }
- else
- PRINTK("service rc/rs (PCIXCC): %d/%d\n",
- service_rc, service_rs);
- return REC_OPERAND_INV;
- }
- src_p = (unsigned char *)
- cprbx_p + sizeof(struct CPRBX);
- src_p += 4;
- src_l = (int)(*((short *) src_p));
- src_l -= 2;
- src_p += 2;
- }
- break;
- default:
- src_p = (unsigned char *)errh_p;
- PRINTK("Unrecognized Message Header: "
- "%02x%02x%02x%02x%02x%02x%02x%02x\n",
- src_p[0], src_p[1], src_p[2], src_p[3],
- src_p[4], src_p[5], src_p[6], src_p[7]);
- return REC_BAD_MESSAGE;
- }
-
- if (reply_code)
- switch (reply_code) {
- case REP82_ERROR_MACHINE_FAILURE:
- if (errh_p->type == TYPE82_RSP_CODE)
- PRINTKW("Machine check failure\n");
- else
- PRINTKW("Module failure\n");
- return REC_HARDWAR_ERR;
- case REP82_ERROR_OPERAND_INVALID:
- return REC_OPERAND_INV;
- case REP88_ERROR_MESSAGE_MALFORMD:
- PRINTKW("Message malformed\n");
- return REC_OPERAND_INV;
- case REP82_ERROR_OPERAND_SIZE:
- return REC_OPERAND_SIZE;
- case REP82_ERROR_EVEN_MOD_IN_OPND:
- return REC_EVEN_MOD;
- case REP82_ERROR_MESSAGE_TYPE:
- return WRONG_DEVICE_TYPE;
- case REP82_ERROR_TRANSPORT_FAIL:
- PRINTKW("Transport failed (APFS = %02X%02X%02X%02X)\n",
- t86m_p->apfs[0], t86m_p->apfs[1],
- t86m_p->apfs[2], t86m_p->apfs[3]);
- return REC_HARDWAR_ERR;
- default:
- PRINTKW("reply code = %d\n", reply_code);
- return REC_HARDWAR_ERR;
- }
-
- if (service_rc != 0)
- return REC_OPERAND_INV;
-
- if ((src_l > icaMsg_p->outputdatalength) ||
- (src_l > RESPBUFFSIZE) ||
- (src_l <= 0))
- return REC_OPERAND_SIZE;
-
- PDEBUG("Length returned = %d\n", src_l);
- tgt_p = resp_buff + icaMsg_p->outputdatalength - src_l;
- memcpy(tgt_p, src_p, src_l);
- if ((errh_p->type == TYPE86_RSP_CODE) && (resp_buff < tgt_p)) {
- memset(resp_buff, 0, icaMsg_p->outputdatalength - src_l);
- if (pad_msg(resp_buff, icaMsg_p->outputdatalength, src_l))
- return REC_INVALID_PAD;
- }
- *respbufflen_p = icaMsg_p->outputdatalength;
- if (*respbufflen_p == 0)
- PRINTK("Zero *respbufflen_p\n");
-
- return 0;
-}
-
diff --git a/drivers/s390/crypto/z90main.c b/drivers/s390/crypto/z90main.c
deleted file mode 100644
index 982acc7303ea..000000000000
--- a/drivers/s390/crypto/z90main.c
+++ /dev/null
@@ -1,3380 +0,0 @@
-/*
- * linux/drivers/s390/crypto/z90main.c
- *
- * z90crypt 1.3.3
- *
- * Copyright (C) 2001, 2005 IBM Corporation
- * Author(s): Robert Burroughs (burrough@us.ibm.com)
- * Eric Rossman (edrossma@us.ibm.com)
- *
- * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- *
- * 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 <asm/uaccess.h> // copy_(from|to)_user
-#include <linux/compat.h>
-#include <linux/compiler.h>
-#include <linux/delay.h> // mdelay
-#include <linux/init.h>
-#include <linux/interrupt.h> // for tasklets
-#include <linux/miscdevice.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/proc_fs.h>
-#include <linux/syscalls.h>
-#include "z90crypt.h"
-#include "z90common.h"
-
-/**
- * Defaults that may be modified.
- */
-
-/**
- * You can specify a different minor at compile time.
- */
-#ifndef Z90CRYPT_MINOR
-#define Z90CRYPT_MINOR MISC_DYNAMIC_MINOR
-#endif
-
-/**
- * You can specify a different domain at compile time or on the insmod
- * command line.
- */
-#ifndef DOMAIN_INDEX
-#define DOMAIN_INDEX -1
-#endif
-
-/**
- * This is the name under which the device is registered in /proc/modules.
- */
-#define REG_NAME "z90crypt"
-
-/**
- * Cleanup should run every CLEANUPTIME seconds and should clean up requests
- * older than CLEANUPTIME seconds in the past.
- */
-#ifndef CLEANUPTIME
-#define CLEANUPTIME 15
-#endif
-
-/**
- * Config should run every CONFIGTIME seconds
- */
-#ifndef CONFIGTIME
-#define CONFIGTIME 30
-#endif
-
-/**
- * The first execution of the config task should take place
- * immediately after initialization
- */
-#ifndef INITIAL_CONFIGTIME
-#define INITIAL_CONFIGTIME 1
-#endif
-
-/**
- * Reader should run every READERTIME milliseconds
- * With the 100Hz patch for s390, z90crypt can lock the system solid while
- * under heavy load. We'll try to avoid that.
- */
-#ifndef READERTIME
-#if HZ > 1000
-#define READERTIME 2
-#else
-#define READERTIME 10
-#endif
-#endif
-
-/**
- * turn long device array index into device pointer
- */
-#define LONG2DEVPTR(ndx) (z90crypt.device_p[(ndx)])
-
-/**
- * turn short device array index into long device array index
- */
-#define SHRT2LONG(ndx) (z90crypt.overall_device_x.device_index[(ndx)])
-
-/**
- * turn short device array index into device pointer
- */
-#define SHRT2DEVPTR(ndx) LONG2DEVPTR(SHRT2LONG(ndx))
-
-/**
- * Status for a work-element
- */
-#define STAT_DEFAULT 0x00 // request has not been processed
-
-#define STAT_ROUTED 0x80 // bit 7: requests get routed to specific device
- // else, device is determined each write
-#define STAT_FAILED 0x40 // bit 6: this bit is set if the request failed
- // before being sent to the hardware.
-#define STAT_WRITTEN 0x30 // bits 5-4: work to be done, not sent to device
-// 0x20 // UNUSED state
-#define STAT_READPEND 0x10 // bits 5-4: work done, we're returning data now
-#define STAT_NOWORK 0x00 // bits off: no work on any queue
-#define STAT_RDWRMASK 0x30 // mask for bits 5-4
-
-/**
- * Macros to check the status RDWRMASK
- */
-#define CHK_RDWRMASK(statbyte) ((statbyte) & STAT_RDWRMASK)
-#define SET_RDWRMASK(statbyte, newval) \
- {(statbyte) &= ~STAT_RDWRMASK; (statbyte) |= newval;}
-
-/**
- * Audit Trail. Progress of a Work element
- * audit[0]: Unless noted otherwise, these bits are all set by the process
- */
-#define FP_COPYFROM 0x80 // Caller's buffer has been copied to work element
-#define FP_BUFFREQ 0x40 // Low Level buffer requested
-#define FP_BUFFGOT 0x20 // Low Level buffer obtained
-#define FP_SENT 0x10 // Work element sent to a crypto device
- // (may be set by process or by reader task)
-#define FP_PENDING 0x08 // Work element placed on pending queue
- // (may be set by process or by reader task)
-#define FP_REQUEST 0x04 // Work element placed on request queue
-#define FP_ASLEEP 0x02 // Work element about to sleep
-#define FP_AWAKE 0x01 // Work element has been awakened
-
-/**
- * audit[1]: These bits are set by the reader task and/or the cleanup task
- */
-#define FP_NOTPENDING 0x80 // Work element removed from pending queue
-#define FP_AWAKENING 0x40 // Caller about to be awakened
-#define FP_TIMEDOUT 0x20 // Caller timed out
-#define FP_RESPSIZESET 0x10 // Response size copied to work element
-#define FP_RESPADDRCOPIED 0x08 // Response address copied to work element
-#define FP_RESPBUFFCOPIED 0x04 // Response buffer copied to work element
-#define FP_REMREQUEST 0x02 // Work element removed from request queue
-#define FP_SIGNALED 0x01 // Work element was awakened by a signal
-
-/**
- * audit[2]: unused
- */
-
-/**
- * state of the file handle in private_data.status
- */
-#define STAT_OPEN 0
-#define STAT_CLOSED 1
-
-/**
- * PID() expands to the process ID of the current process
- */
-#define PID() (current->pid)
-
-/**
- * Selected Constants. The number of APs and the number of devices
- */
-#ifndef Z90CRYPT_NUM_APS
-#define Z90CRYPT_NUM_APS 64
-#endif
-#ifndef Z90CRYPT_NUM_DEVS
-#define Z90CRYPT_NUM_DEVS Z90CRYPT_NUM_APS
-#endif
-
-/**
- * Buffer size for receiving responses. The maximum Response Size
- * is actually the maximum request size, since in an error condition
- * the request itself may be returned unchanged.
- */
-#define MAX_RESPONSE_SIZE 0x0000077C
-
-/**
- * A count and status-byte mask
- */
-struct status {
- int st_count; // # of enabled devices
- int disabled_count; // # of disabled devices
- int user_disabled_count; // # of devices disabled via proc fs
- unsigned char st_mask[Z90CRYPT_NUM_APS]; // current status mask
-};
-
-/**
- * The array of device indexes is a mechanism for fast indexing into
- * a long (and sparse) array. For instance, if APs 3, 9 and 47 are
- * installed, z90CDeviceIndex[0] is 3, z90CDeviceIndex[1] is 9, and
- * z90CDeviceIndex[2] is 47.
- */
-struct device_x {
- int device_index[Z90CRYPT_NUM_DEVS];
-};
-
-/**
- * All devices are arranged in a single array: 64 APs
- */
-struct device {
- int dev_type; // PCICA, PCICC, PCIXCC_MCL2,
- // PCIXCC_MCL3, CEX2C, CEX2A
- enum devstat dev_stat; // current device status
- int dev_self_x; // Index in array
- int disabled; // Set when device is in error
- int user_disabled; // Set when device is disabled by user
- int dev_q_depth; // q depth
- unsigned char * dev_resp_p; // Response buffer address
- int dev_resp_l; // Response Buffer length
- int dev_caller_count; // Number of callers
- int dev_total_req_cnt; // # requests for device since load
- struct list_head dev_caller_list; // List of callers
-};
-
-/**
- * There's a struct status and a struct device_x for each device type.
- */
-struct hdware_block {
- struct status hdware_mask;
- struct status type_mask[Z90CRYPT_NUM_TYPES];
- struct device_x type_x_addr[Z90CRYPT_NUM_TYPES];
- unsigned char device_type_array[Z90CRYPT_NUM_APS];
-};
-
-/**
- * z90crypt is the topmost data structure in the hierarchy.
- */
-struct z90crypt {
- int max_count; // Nr of possible crypto devices
- struct status mask;
- int q_depth_array[Z90CRYPT_NUM_DEVS];
- int dev_type_array[Z90CRYPT_NUM_DEVS];
- struct device_x overall_device_x; // array device indexes
- struct device * device_p[Z90CRYPT_NUM_DEVS];
- int terminating;
- int domain_established;// TRUE: domain has been found
- int cdx; // Crypto Domain Index
- int len; // Length of this data structure
- struct hdware_block *hdware_info;
-};
-
-/**
- * An array of these structures is pointed to from dev_caller
- * The length of the array depends on the device type. For APs,
- * there are 8.
- *
- * The caller buffer is allocated to the user at OPEN. At WRITE,
- * it contains the request; at READ, the response. The function
- * send_to_crypto_device converts the request to device-dependent
- * form and use the caller's OPEN-allocated buffer for the response.
- *
- * For the contents of caller_dev_dep_req and caller_dev_dep_req_p
- * because that points to it, see the discussion in z90hardware.c.
- * Search for "extended request message block".
- */
-struct caller {
- int caller_buf_l; // length of original request
- unsigned char * caller_buf_p; // Original request on WRITE
- int caller_dev_dep_req_l; // len device dependent request
- unsigned char * caller_dev_dep_req_p; // Device dependent form
- unsigned char caller_id[8]; // caller-supplied message id
- struct list_head caller_liste;
- unsigned char caller_dev_dep_req[MAX_RESPONSE_SIZE];
-};
-
-/**
- * Function prototypes from z90hardware.c
- */
-enum hdstat query_online(int deviceNr, int cdx, int resetNr, int *q_depth,
- int *dev_type);
-enum devstat reset_device(int deviceNr, int cdx, int resetNr);
-enum devstat send_to_AP(int dev_nr, int cdx, int msg_len, unsigned char *msg_ext);
-enum devstat receive_from_AP(int dev_nr, int cdx, int resplen,
- unsigned char *resp, unsigned char *psmid);
-int convert_request(unsigned char *buffer, int func, unsigned short function,
- int cdx, int dev_type, int *msg_l_p, unsigned char *msg_p);
-int convert_response(unsigned char *response, unsigned char *buffer,
- int *respbufflen_p, unsigned char *resp_buff);
-
-/**
- * Low level function prototypes
- */
-static int create_z90crypt(int *cdx_p);
-static int refresh_z90crypt(int *cdx_p);
-static int find_crypto_devices(struct status *deviceMask);
-static int create_crypto_device(int index);
-static int destroy_crypto_device(int index);
-static void destroy_z90crypt(void);
-static int refresh_index_array(struct status *status_str,
- struct device_x *index_array);
-static int probe_device_type(struct device *devPtr);
-static int probe_PCIXCC_type(struct device *devPtr);
-
-/**
- * proc fs definitions
- */
-static struct proc_dir_entry *z90crypt_entry;
-
-/**
- * data structures
- */
-
-/**
- * work_element.opener points back to this structure
- */
-struct priv_data {
- pid_t opener_pid;
- unsigned char status; // 0: open 1: closed
-};
-
-/**
- * A work element is allocated for each request
- */
-struct work_element {
- struct priv_data *priv_data;
- pid_t pid;
- int devindex; // index of device processing this w_e
- // (If request did not specify device,
- // -1 until placed onto a queue)
- int devtype;
- struct list_head liste; // used for requestq and pendingq
- char buffer[128]; // local copy of user request
- int buff_size; // size of the buffer for the request
- char resp_buff[RESPBUFFSIZE];
- int resp_buff_size;
- char __user * resp_addr; // address of response in user space
- unsigned int funccode; // function code of request
- wait_queue_head_t waitq;
- unsigned long requestsent; // time at which the request was sent
- atomic_t alarmrung; // wake-up signal
- unsigned char caller_id[8]; // pid + counter, for this w_e
- unsigned char status[1]; // bits to mark status of the request
- unsigned char audit[3]; // record of work element's progress
- unsigned char * requestptr; // address of request buffer
- int retcode; // return code of request
-};
-
-/**
- * High level function prototypes
- */
-static int z90crypt_open(struct inode *, struct file *);
-static int z90crypt_release(struct inode *, struct file *);
-static ssize_t z90crypt_read(struct file *, char __user *, size_t, loff_t *);
-static ssize_t z90crypt_write(struct file *, const char __user *,
- size_t, loff_t *);
-static long z90crypt_unlocked_ioctl(struct file *, unsigned int, unsigned long);
-static long z90crypt_compat_ioctl(struct file *, unsigned int, unsigned long);
-
-static void z90crypt_reader_task(unsigned long);
-static void z90crypt_schedule_reader_task(unsigned long);
-static void z90crypt_config_task(unsigned long);
-static void z90crypt_cleanup_task(unsigned long);
-
-static int z90crypt_status(char *, char **, off_t, int, int *, void *);
-static int z90crypt_status_write(struct file *, const char __user *,
- unsigned long, void *);
-
-/**
- * Storage allocated at initialization and used throughout the life of
- * this insmod
- */
-static int domain = DOMAIN_INDEX;
-static struct z90crypt z90crypt;
-static int quiesce_z90crypt;
-static spinlock_t queuespinlock;
-static struct list_head request_list;
-static int requestq_count;
-static struct list_head pending_list;
-static int pendingq_count;
-
-static struct tasklet_struct reader_tasklet;
-static struct timer_list reader_timer;
-static struct timer_list config_timer;
-static struct timer_list cleanup_timer;
-static atomic_t total_open;
-static atomic_t z90crypt_step;
-
-static struct file_operations z90crypt_fops = {
- .owner = THIS_MODULE,
- .read = z90crypt_read,
- .write = z90crypt_write,
- .unlocked_ioctl = z90crypt_unlocked_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = z90crypt_compat_ioctl,
-#endif
- .open = z90crypt_open,
- .release = z90crypt_release
-};
-
-static struct miscdevice z90crypt_misc_device = {
- .minor = Z90CRYPT_MINOR,
- .name = DEV_NAME,
- .fops = &z90crypt_fops,
- .devfs_name = DEV_NAME
-};
-
-/**
- * Documentation values.
- */
-MODULE_AUTHOR("zSeries Linux Crypto Team: Robert H. Burroughs, Eric D. Rossman"
- "and Jochen Roehrig");
-MODULE_DESCRIPTION("zSeries Linux Cryptographic Coprocessor device driver, "
- "Copyright 2001, 2005 IBM Corporation");
-MODULE_LICENSE("GPL");
-module_param(domain, int, 0);
-MODULE_PARM_DESC(domain, "domain index for device");
-
-#ifdef CONFIG_COMPAT
-/**
- * ioctl32 conversion routines
- */
-struct ica_rsa_modexpo_32 { // For 32-bit callers
- compat_uptr_t inputdata;
- unsigned int inputdatalength;
- compat_uptr_t outputdata;
- unsigned int outputdatalength;
- compat_uptr_t b_key;
- compat_uptr_t n_modulus;
-};
-
-static long
-trans_modexpo32(struct file *filp, unsigned int cmd, unsigned long arg)
-{
- struct ica_rsa_modexpo_32 __user *mex32u = compat_ptr(arg);
- struct ica_rsa_modexpo_32 mex32k;
- struct ica_rsa_modexpo __user *mex64;
- long ret = 0;
- unsigned int i;
-
- if (!access_ok(VERIFY_WRITE, mex32u, sizeof(struct ica_rsa_modexpo_32)))
- return -EFAULT;
- mex64 = compat_alloc_user_space(sizeof(struct ica_rsa_modexpo));
- if (!access_ok(VERIFY_WRITE, mex64, sizeof(struct ica_rsa_modexpo)))
- return -EFAULT;
- if (copy_from_user(&mex32k, mex32u, sizeof(struct ica_rsa_modexpo_32)))
- return -EFAULT;
- if (__put_user(compat_ptr(mex32k.inputdata), &mex64->inputdata) ||
- __put_user(mex32k.inputdatalength, &mex64->inputdatalength) ||
- __put_user(compat_ptr(mex32k.outputdata), &mex64->outputdata) ||
- __put_user(mex32k.outputdatalength, &mex64->outputdatalength) ||
- __put_user(compat_ptr(mex32k.b_key), &mex64->b_key) ||
- __put_user(compat_ptr(mex32k.n_modulus), &mex64->n_modulus))
- return -EFAULT;
- ret = z90crypt_unlocked_ioctl(filp, cmd, (unsigned long)mex64);
- if (!ret)
- if (__get_user(i, &mex64->outputdatalength) ||
- __put_user(i, &mex32u->outputdatalength))
- ret = -EFAULT;
- return ret;
-}
-
-struct ica_rsa_modexpo_crt_32 { // For 32-bit callers
- compat_uptr_t inputdata;
- unsigned int inputdatalength;
- compat_uptr_t outputdata;
- unsigned int outputdatalength;
- compat_uptr_t bp_key;
- compat_uptr_t bq_key;
- compat_uptr_t np_prime;
- compat_uptr_t nq_prime;
- compat_uptr_t u_mult_inv;
-};
-
-static long
-trans_modexpo_crt32(struct file *filp, unsigned int cmd, unsigned long arg)
-{
- struct ica_rsa_modexpo_crt_32 __user *crt32u = compat_ptr(arg);
- struct ica_rsa_modexpo_crt_32 crt32k;
- struct ica_rsa_modexpo_crt __user *crt64;
- long ret = 0;
- unsigned int i;
-
- if (!access_ok(VERIFY_WRITE, crt32u,
- sizeof(struct ica_rsa_modexpo_crt_32)))
- return -EFAULT;
- crt64 = compat_alloc_user_space(sizeof(struct ica_rsa_modexpo_crt));
- if (!access_ok(VERIFY_WRITE, crt64, sizeof(struct ica_rsa_modexpo_crt)))
- return -EFAULT;
- if (copy_from_user(&crt32k, crt32u,
- sizeof(struct ica_rsa_modexpo_crt_32)))
- return -EFAULT;
- if (__put_user(compat_ptr(crt32k.inputdata), &crt64->inputdata) ||
- __put_user(crt32k.inputdatalength, &crt64->inputdatalength) ||
- __put_user(compat_ptr(crt32k.outputdata), &crt64->outputdata) ||
- __put_user(crt32k.outputdatalength, &crt64->outputdatalength) ||
- __put_user(compat_ptr(crt32k.bp_key), &crt64->bp_key) ||
- __put_user(compat_ptr(crt32k.bq_key), &crt64->bq_key) ||
- __put_user(compat_ptr(crt32k.np_prime), &crt64->np_prime) ||
- __put_user(compat_ptr(crt32k.nq_prime), &crt64->nq_prime) ||
- __put_user(compat_ptr(crt32k.u_mult_inv), &crt64->u_mult_inv))
- return -EFAULT;
- ret = z90crypt_unlocked_ioctl(filp, cmd, (unsigned long)crt64);
- if (!ret)
- if (__get_user(i, &crt64->outputdatalength) ||
- __put_user(i, &crt32u->outputdatalength))
- ret = -EFAULT;
- return ret;
-}
-
-static long
-z90crypt_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
- switch (cmd) {
- case ICAZ90STATUS:
- case Z90QUIESCE:
- case Z90STAT_TOTALCOUNT:
- case Z90STAT_PCICACOUNT:
- case Z90STAT_PCICCCOUNT:
- case Z90STAT_PCIXCCCOUNT:
- case Z90STAT_PCIXCCMCL2COUNT:
- case Z90STAT_PCIXCCMCL3COUNT:
- case Z90STAT_CEX2CCOUNT:
- case Z90STAT_REQUESTQ_COUNT:
- case Z90STAT_PENDINGQ_COUNT:
- case Z90STAT_TOTALOPEN_COUNT:
- case Z90STAT_DOMAIN_INDEX:
- case Z90STAT_STATUS_MASK:
- case Z90STAT_QDEPTH_MASK:
- case Z90STAT_PERDEV_REQCNT:
- return z90crypt_unlocked_ioctl(filp, cmd, arg);
- case ICARSAMODEXPO:
- return trans_modexpo32(filp, cmd, arg);
- case ICARSACRT:
- return trans_modexpo_crt32(filp, cmd, arg);
- default:
- return -ENOIOCTLCMD;
- }
-}
-#endif
-
-/**
- * The module initialization code.
- */
-static int __init
-z90crypt_init_module(void)
-{
- int result, nresult;
- struct proc_dir_entry *entry;
-
- PDEBUG("PID %d\n", PID());
-
- if ((domain < -1) || (domain > 15)) {
- PRINTKW("Invalid param: domain = %d. Not loading.\n", domain);
- return -EINVAL;
- }
-
- /* Register as misc device with given minor (or get a dynamic one). */
- result = misc_register(&z90crypt_misc_device);
- if (result < 0) {
- PRINTKW(KERN_ERR "misc_register (minor %d) failed with %d\n",
- z90crypt_misc_device.minor, result);
- return result;
- }
-
- PDEBUG("Registered " DEV_NAME " with result %d\n", result);
-
- result = create_z90crypt(&domain);
- if (result != 0) {
- PRINTKW("create_z90crypt (domain index %d) failed with %d.\n",
- domain, result);
- result = -ENOMEM;
- goto init_module_cleanup;
- }
-
- if (result == 0) {
- PRINTKN("Version %d.%d.%d loaded, built on %s %s\n",
- z90crypt_VERSION, z90crypt_RELEASE, z90crypt_VARIANT,
- __DATE__, __TIME__);
- PDEBUG("create_z90crypt (domain index %d) successful.\n",
- domain);
- } else
- PRINTK("No devices at startup\n");
-
- /* Initialize globals. */
- spin_lock_init(&queuespinlock);
-
- INIT_LIST_HEAD(&pending_list);
- pendingq_count = 0;
-
- INIT_LIST_HEAD(&request_list);
- requestq_count = 0;
-
- quiesce_z90crypt = 0;
-
- atomic_set(&total_open, 0);
- atomic_set(&z90crypt_step, 0);
-
- /* Set up the cleanup task. */
- init_timer(&cleanup_timer);
- cleanup_timer.function = z90crypt_cleanup_task;
- cleanup_timer.data = 0;
- cleanup_timer.expires = jiffies + (CLEANUPTIME * HZ);
- add_timer(&cleanup_timer);
-
- /* Set up the proc file system */
- entry = create_proc_entry("driver/z90crypt", 0644, 0);
- if (entry) {
- entry->nlink = 1;
- entry->data = 0;
- entry->read_proc = z90crypt_status;
- entry->write_proc = z90crypt_status_write;
- }
- else
- PRINTK("Couldn't create z90crypt proc entry\n");
- z90crypt_entry = entry;
-
- /* Set up the configuration task. */
- init_timer(&config_timer);
- config_timer.function = z90crypt_config_task;
- config_timer.data = 0;
- config_timer.expires = jiffies + (INITIAL_CONFIGTIME * HZ);
- add_timer(&config_timer);
-
- /* Set up the reader task */
- tasklet_init(&reader_tasklet, z90crypt_reader_task, 0);
- init_timer(&reader_timer);
- reader_timer.function = z90crypt_schedule_reader_task;
- reader_timer.data = 0;
- reader_timer.expires = jiffies + (READERTIME * HZ / 1000);
- add_timer(&reader_timer);
-
- return 0; // success
-
-init_module_cleanup:
- if ((nresult = misc_deregister(&z90crypt_misc_device)))
- PRINTK("misc_deregister failed with %d.\n", nresult);
- else
- PDEBUG("misc_deregister successful.\n");
-
- return result; // failure
-}
-
-/**
- * The module termination code
- */
-static void __exit
-z90crypt_cleanup_module(void)
-{
- int nresult;
-
- PDEBUG("PID %d\n", PID());
-
- remove_proc_entry("driver/z90crypt", 0);
-
- if ((nresult = misc_deregister(&z90crypt_misc_device)))
- PRINTK("misc_deregister failed with %d.\n", nresult);
- else
- PDEBUG("misc_deregister successful.\n");
-
- /* Remove the tasks */
- tasklet_kill(&reader_tasklet);
- del_timer(&reader_timer);
- del_timer(&config_timer);
- del_timer(&cleanup_timer);
-
- destroy_z90crypt();
-
- PRINTKN("Unloaded.\n");
-}
-
-/**
- * Functions running under a process id
- *
- * The I/O functions:
- * z90crypt_open
- * z90crypt_release
- * z90crypt_read
- * z90crypt_write
- * z90crypt_unlocked_ioctl
- * z90crypt_status
- * z90crypt_status_write
- * disable_card
- * enable_card
- *
- * Helper functions:
- * z90crypt_rsa
- * z90crypt_prepare
- * z90crypt_send
- * z90crypt_process_results
- *
- */
-static int
-z90crypt_open(struct inode *inode, struct file *filp)
-{
- struct priv_data *private_data_p;
-
- if (quiesce_z90crypt)
- return -EQUIESCE;
-
- private_data_p = kzalloc(sizeof(struct priv_data), GFP_KERNEL);
- if (!private_data_p) {
- PRINTK("Memory allocate failed\n");
- return -ENOMEM;
- }
-
- private_data_p->status = STAT_OPEN;
- private_data_p->opener_pid = PID();
- filp->private_data = private_data_p;
- atomic_inc(&total_open);
-
- return 0;
-}
-
-static int
-z90crypt_release(struct inode *inode, struct file *filp)
-{
- struct priv_data *private_data_p = filp->private_data;
-
- PDEBUG("PID %d (filp %p)\n", PID(), filp);
-
- private_data_p->status = STAT_CLOSED;
- memset(private_data_p, 0, sizeof(struct priv_data));
- kfree(private_data_p);
- atomic_dec(&total_open);
-
- return 0;
-}
-
-/*
- * there are two read functions, of which compile options will choose one
- * without USE_GET_RANDOM_BYTES
- * => read() always returns -EPERM;
- * otherwise
- * => read() uses get_random_bytes() kernel function
- */
-#ifndef USE_GET_RANDOM_BYTES
-/**
- * z90crypt_read will not be supported beyond z90crypt 1.3.1
- */
-static ssize_t
-z90crypt_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
-{
- PDEBUG("filp %p (PID %d)\n", filp, PID());
- return -EPERM;
-}
-#else // we want to use get_random_bytes
-/**
- * read() just returns a string of random bytes. Since we have no way
- * to generate these cryptographically, we just execute get_random_bytes
- * for the length specified.
- */
-#include <linux/random.h>
-static ssize_t
-z90crypt_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
-{
- unsigned char *temp_buff;
-
- PDEBUG("filp %p (PID %d)\n", filp, PID());
-
- if (quiesce_z90crypt)
- return -EQUIESCE;
- if (count < 0) {
- PRINTK("Requested random byte count negative: %ld\n", count);
- return -EINVAL;
- }
- if (count > RESPBUFFSIZE) {
- PDEBUG("count[%d] > RESPBUFFSIZE", count);
- return -EINVAL;
- }
- if (count == 0)
- return 0;
- temp_buff = kmalloc(RESPBUFFSIZE, GFP_KERNEL);
- if (!temp_buff) {
- PRINTK("Memory allocate failed\n");
- return -ENOMEM;
- }
- get_random_bytes(temp_buff, count);
-
- if (copy_to_user(buf, temp_buff, count) != 0) {
- kfree(temp_buff);
- return -EFAULT;
- }
- kfree(temp_buff);
- return count;
-}
-#endif
-
-/**
- * Write is is not allowed
- */
-static ssize_t
-z90crypt_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
-{
- PDEBUG("filp %p (PID %d)\n", filp, PID());
- return -EPERM;
-}
-
-/**
- * New status functions
- */
-static inline int
-get_status_totalcount(void)
-{
- return z90crypt.hdware_info->hdware_mask.st_count;
-}
-
-static inline int
-get_status_PCICAcount(void)
-{
- return z90crypt.hdware_info->type_mask[PCICA].st_count;
-}
-
-static inline int
-get_status_PCICCcount(void)
-{
- return z90crypt.hdware_info->type_mask[PCICC].st_count;
-}
-
-static inline int
-get_status_PCIXCCcount(void)
-{
- return z90crypt.hdware_info->type_mask[PCIXCC_MCL2].st_count +
- z90crypt.hdware_info->type_mask[PCIXCC_MCL3].st_count;
-}
-
-static inline int
-get_status_PCIXCCMCL2count(void)
-{
- return z90crypt.hdware_info->type_mask[PCIXCC_MCL2].st_count;
-}
-
-static inline int
-get_status_PCIXCCMCL3count(void)
-{
- return z90crypt.hdware_info->type_mask[PCIXCC_MCL3].st_count;
-}
-
-static inline int
-get_status_CEX2Ccount(void)
-{
- return z90crypt.hdware_info->type_mask[CEX2C].st_count;
-}
-
-static inline int
-get_status_CEX2Acount(void)
-{
- return z90crypt.hdware_info->type_mask[CEX2A].st_count;
-}
-
-static inline int
-get_status_requestq_count(void)
-{
- return requestq_count;
-}
-
-static inline int
-get_status_pendingq_count(void)
-{
- return pendingq_count;
-}
-
-static inline int
-get_status_totalopen_count(void)
-{
- return atomic_read(&total_open);
-}
-
-static inline int
-get_status_domain_index(void)
-{
- return z90crypt.cdx;
-}
-
-static inline unsigned char *
-get_status_status_mask(unsigned char status[Z90CRYPT_NUM_APS])
-{
- int i, ix;
-
- memcpy(status, z90crypt.hdware_info->device_type_array,
- Z90CRYPT_NUM_APS);
-
- for (i = 0; i < get_status_totalcount(); i++) {
- ix = SHRT2LONG(i);
- if (LONG2DEVPTR(ix)->user_disabled)
- status[ix] = 0x0d;
- }
-
- return status;
-}
-
-static inline unsigned char *
-get_status_qdepth_mask(unsigned char qdepth[Z90CRYPT_NUM_APS])
-{
- int i, ix;
-
- memset(qdepth, 0, Z90CRYPT_NUM_APS);
-
- for (i = 0; i < get_status_totalcount(); i++) {
- ix = SHRT2LONG(i);
- qdepth[ix] = LONG2DEVPTR(ix)->dev_caller_count;
- }
-
- return qdepth;
-}
-
-static inline unsigned int *
-get_status_perdevice_reqcnt(unsigned int reqcnt[Z90CRYPT_NUM_APS])
-{
- int i, ix;
-
- memset(reqcnt, 0, Z90CRYPT_NUM_APS * sizeof(int));
-
- for (i = 0; i < get_status_totalcount(); i++) {
- ix = SHRT2LONG(i);
- reqcnt[ix] = LONG2DEVPTR(ix)->dev_total_req_cnt;
- }
-
- return reqcnt;
-}
-
-static inline void
-init_work_element(struct work_element *we_p,
- struct priv_data *priv_data, pid_t pid)
-{
- int step;
-
- we_p->requestptr = (unsigned char *)we_p + sizeof(struct work_element);
- /* Come up with a unique id for this caller. */
- step = atomic_inc_return(&z90crypt_step);
- memcpy(we_p->caller_id+0, (void *) &pid, sizeof(pid));
- memcpy(we_p->caller_id+4, (void *) &step, sizeof(step));
- we_p->pid = pid;
- we_p->priv_data = priv_data;
- we_p->status[0] = STAT_DEFAULT;
- we_p->audit[0] = 0x00;
- we_p->audit[1] = 0x00;
- we_p->audit[2] = 0x00;
- we_p->resp_buff_size = 0;
- we_p->retcode = 0;
- we_p->devindex = -1;
- we_p->devtype = -1;
- atomic_set(&we_p->alarmrung, 0);
- init_waitqueue_head(&we_p->waitq);
- INIT_LIST_HEAD(&(we_p->liste));
-}
-
-static inline int
-allocate_work_element(struct work_element **we_pp,
- struct priv_data *priv_data_p, pid_t pid)
-{
- struct work_element *we_p;
-
- we_p = (struct work_element *) get_zeroed_page(GFP_KERNEL);
- if (!we_p)
- return -ENOMEM;
- init_work_element(we_p, priv_data_p, pid);
- *we_pp = we_p;
- return 0;
-}
-
-static inline void
-remove_device(struct device *device_p)
-{
- if (!device_p || (device_p->disabled != 0))
- return;
- device_p->disabled = 1;
- z90crypt.hdware_info->type_mask[device_p->dev_type].disabled_count++;
- z90crypt.hdware_info->hdware_mask.disabled_count++;
-}
-
-/**
- * Bitlength limits for each card
- *
- * There are new MCLs which allow more bitlengths. See the table for details.
- * The MCL must be applied and the newer bitlengths enabled for these to work.
- *
- * Card Type Old limit New limit
- * PCICA ??-2048 same (the lower limit is less than 128 bit...)
- * PCICC 512-1024 512-2048
- * PCIXCC_MCL2 512-2048 ----- (applying any GA LIC will make an MCL3 card)
- * PCIXCC_MCL3 ----- 128-2048
- * CEX2C 512-2048 128-2048
- * CEX2A ??-2048 same (the lower limit is less than 128 bit...)
- *
- * ext_bitlens (extended bitlengths) is a global, since you should not apply an
- * MCL to just one card in a machine. We assume, at first, that all cards have
- * these capabilities.
- */
-int ext_bitlens = 1; // This is global
-#define PCIXCC_MIN_MOD_SIZE 16 // 128 bits
-#define OLD_PCIXCC_MIN_MOD_SIZE 64 // 512 bits
-#define PCICC_MIN_MOD_SIZE 64 // 512 bits
-#define OLD_PCICC_MAX_MOD_SIZE 128 // 1024 bits
-#define MAX_MOD_SIZE 256 // 2048 bits
-
-static inline int
-select_device_type(int *dev_type_p, int bytelength)
-{
- static int count = 0;
- int PCICA_avail, PCIXCC_MCL3_avail, CEX2C_avail, CEX2A_avail,
- index_to_use;
- struct status *stat;
- if ((*dev_type_p != PCICC) && (*dev_type_p != PCICA) &&
- (*dev_type_p != PCIXCC_MCL2) && (*dev_type_p != PCIXCC_MCL3) &&
- (*dev_type_p != CEX2C) && (*dev_type_p != CEX2A) &&
- (*dev_type_p != ANYDEV))
- return -1;
- if (*dev_type_p != ANYDEV) {
- stat = &z90crypt.hdware_info->type_mask[*dev_type_p];
- if (stat->st_count >
- (stat->disabled_count + stat->user_disabled_count))
- return 0;
- return -1;
- }
-
- /**
- * Assumption: PCICA, PCIXCC_MCL3, CEX2C, and CEX2A are all similar in
- * speed.
- *
- * PCICA and CEX2A do NOT co-exist, so it would be either one or the
- * other present.
- */
- stat = &z90crypt.hdware_info->type_mask[PCICA];
- PCICA_avail = stat->st_count -
- (stat->disabled_count + stat->user_disabled_count);
- stat = &z90crypt.hdware_info->type_mask[PCIXCC_MCL3];
- PCIXCC_MCL3_avail = stat->st_count -
- (stat->disabled_count + stat->user_disabled_count);
- stat = &z90crypt.hdware_info->type_mask[CEX2C];
- CEX2C_avail = stat->st_count -
- (stat->disabled_count + stat->user_disabled_count);
- stat = &z90crypt.hdware_info->type_mask[CEX2A];
- CEX2A_avail = stat->st_count -
- (stat->disabled_count + stat->user_disabled_count);
- if (PCICA_avail || PCIXCC_MCL3_avail || CEX2C_avail || CEX2A_avail) {
- /**
- * bitlength is a factor, PCICA or CEX2A are the most capable,
- * even with the new MCL for PCIXCC.
- */
- if ((bytelength < PCIXCC_MIN_MOD_SIZE) ||
- (!ext_bitlens && (bytelength < OLD_PCIXCC_MIN_MOD_SIZE))) {
- if (PCICA_avail) {
- *dev_type_p = PCICA;
- return 0;
- }
- if (CEX2A_avail) {
- *dev_type_p = CEX2A;
- return 0;
- }
- return -1;
- }
-
- index_to_use = count % (PCICA_avail + PCIXCC_MCL3_avail +
- CEX2C_avail + CEX2A_avail);
- if (index_to_use < PCICA_avail)
- *dev_type_p = PCICA;
- else if (index_to_use < (PCICA_avail + PCIXCC_MCL3_avail))
- *dev_type_p = PCIXCC_MCL3;
- else if (index_to_use < (PCICA_avail + PCIXCC_MCL3_avail +
- CEX2C_avail))
- *dev_type_p = CEX2C;
- else
- *dev_type_p = CEX2A;
- count++;
- return 0;
- }
-
- /* Less than OLD_PCIXCC_MIN_MOD_SIZE cannot go to a PCIXCC_MCL2 */
- if (bytelength < OLD_PCIXCC_MIN_MOD_SIZE)
- return -1;
- stat = &z90crypt.hdware_info->type_mask[PCIXCC_MCL2];
- if (stat->st_count >
- (stat->disabled_count + stat->user_disabled_count)) {
- *dev_type_p = PCIXCC_MCL2;
- return 0;
- }
-
- /**
- * Less than PCICC_MIN_MOD_SIZE or more than OLD_PCICC_MAX_MOD_SIZE
- * (if we don't have the MCL applied and the newer bitlengths enabled)
- * cannot go to a PCICC
- */
- if ((bytelength < PCICC_MIN_MOD_SIZE) ||
- (!ext_bitlens && (bytelength > OLD_PCICC_MAX_MOD_SIZE))) {
- return -1;
- }
- stat = &z90crypt.hdware_info->type_mask[PCICC];
- if (stat->st_count >
- (stat->disabled_count + stat->user_disabled_count)) {
- *dev_type_p = PCICC;
- return 0;
- }
-
- return -1;
-}
-
-/**
- * Try the selected number, then the selected type (can be ANYDEV)
- */
-static inline int
-select_device(int *dev_type_p, int *device_nr_p, int bytelength)
-{
- int i, indx, devTp, low_count, low_indx;
- struct device_x *index_p;
- struct device *dev_ptr;
-
- PDEBUG("device type = %d, index = %d\n", *dev_type_p, *device_nr_p);
- if ((*device_nr_p >= 0) && (*device_nr_p < Z90CRYPT_NUM_DEVS)) {
- PDEBUG("trying index = %d\n", *device_nr_p);
- dev_ptr = z90crypt.device_p[*device_nr_p];
-
- if (dev_ptr &&
- (dev_ptr->dev_stat != DEV_GONE) &&
- (dev_ptr->disabled == 0) &&
- (dev_ptr->user_disabled == 0)) {
- PDEBUG("selected by number, index = %d\n",
- *device_nr_p);
- *dev_type_p = dev_ptr->dev_type;
- return *device_nr_p;
- }
- }
- *device_nr_p = -1;
- PDEBUG("trying type = %d\n", *dev_type_p);
- devTp = *dev_type_p;
- if (select_device_type(&devTp, bytelength) == -1) {
- PDEBUG("failed to select by type\n");
- return -1;
- }
- PDEBUG("selected type = %d\n", devTp);
- index_p = &z90crypt.hdware_info->type_x_addr[devTp];
- low_count = 0x0000FFFF;
- low_indx = -1;
- for (i = 0; i < z90crypt.hdware_info->type_mask[devTp].st_count; i++) {
- indx = index_p->device_index[i];
- dev_ptr = z90crypt.device_p[indx];
- if (dev_ptr &&
- (dev_ptr->dev_stat != DEV_GONE) &&
- (dev_ptr->disabled == 0) &&
- (dev_ptr->user_disabled == 0) &&
- (devTp == dev_ptr->dev_type) &&
- (low_count > dev_ptr->dev_caller_count)) {
- low_count = dev_ptr->dev_caller_count;
- low_indx = indx;
- }
- }
- *device_nr_p = low_indx;
- return low_indx;
-}
-
-static inline int
-send_to_crypto_device(struct work_element *we_p)
-{
- struct caller *caller_p;
- struct device *device_p;
- int dev_nr;
- int bytelen = ((struct ica_rsa_modexpo *)we_p->buffer)->inputdatalength;
-
- if (!we_p->requestptr)
- return SEN_FATAL_ERROR;
- caller_p = (struct caller *)we_p->requestptr;
- dev_nr = we_p->devindex;
- if (select_device(&we_p->devtype, &dev_nr, bytelen) == -1) {
- if (z90crypt.hdware_info->hdware_mask.st_count != 0)
- return SEN_RETRY;
- else
- return SEN_NOT_AVAIL;
- }
- we_p->devindex = dev_nr;
- device_p = z90crypt.device_p[dev_nr];
- if (!device_p)
- return SEN_NOT_AVAIL;
- if (device_p->dev_type != we_p->devtype)
- return SEN_RETRY;
- if (device_p->dev_caller_count >= device_p->dev_q_depth)
- return SEN_QUEUE_FULL;
- PDEBUG("device number prior to send: %d\n", dev_nr);
- switch (send_to_AP(dev_nr, z90crypt.cdx,
- caller_p->caller_dev_dep_req_l,
- caller_p->caller_dev_dep_req_p)) {
- case DEV_SEN_EXCEPTION:
- PRINTKC("Exception during send to device %d\n", dev_nr);
- z90crypt.terminating = 1;
- return SEN_FATAL_ERROR;
- case DEV_GONE:
- PRINTK("Device %d not available\n", dev_nr);
- remove_device(device_p);
- return SEN_NOT_AVAIL;
- case DEV_EMPTY:
- return SEN_NOT_AVAIL;
- case DEV_NO_WORK:
- return SEN_FATAL_ERROR;
- case DEV_BAD_MESSAGE:
- return SEN_USER_ERROR;
- case DEV_QUEUE_FULL:
- return SEN_QUEUE_FULL;
- default:
- case DEV_ONLINE:
- break;
- }
- list_add_tail(&(caller_p->caller_liste), &(device_p->dev_caller_list));
- device_p->dev_caller_count++;
- return 0;
-}
-
-/**
- * Send puts the user's work on one of two queues:
- * the pending queue if the send was successful
- * the request queue if the send failed because device full or busy
- */
-static inline int
-z90crypt_send(struct work_element *we_p, const char *buf)
-{
- int rv;
-
- PDEBUG("PID %d\n", PID());
-
- if (CHK_RDWRMASK(we_p->status[0]) != STAT_NOWORK) {
- PDEBUG("PID %d tried to send more work but has outstanding "
- "work.\n", PID());
- return -EWORKPEND;
- }
- we_p->devindex = -1; // Reset device number
- spin_lock_irq(&queuespinlock);
- rv = send_to_crypto_device(we_p);
- switch (rv) {
- case 0:
- we_p->requestsent = jiffies;
- we_p->audit[0] |= FP_SENT;
- list_add_tail(&we_p->liste, &pending_list);
- ++pendingq_count;
- we_p->audit[0] |= FP_PENDING;
- break;
- case SEN_BUSY:
- case SEN_QUEUE_FULL:
- rv = 0;
- we_p->devindex = -1; // any device will do
- we_p->requestsent = jiffies;
- list_add_tail(&we_p->liste, &request_list);
- ++requestq_count;
- we_p->audit[0] |= FP_REQUEST;
- break;
- case SEN_RETRY:
- rv = -ERESTARTSYS;
- break;
- case SEN_NOT_AVAIL:
- PRINTK("*** No devices available.\n");
- rv = we_p->retcode = -ENODEV;
- we_p->status[0] |= STAT_FAILED;
- break;
- case REC_OPERAND_INV:
- case REC_OPERAND_SIZE:
- case REC_EVEN_MOD:
- case REC_INVALID_PAD:
- rv = we_p->retcode = -EINVAL;
- we_p->status[0] |= STAT_FAILED;
- break;
- default:
- we_p->retcode = rv;
- we_p->status[0] |= STAT_FAILED;
- break;
- }
- if (rv != -ERESTARTSYS)
- SET_RDWRMASK(we_p->status[0], STAT_WRITTEN);
- spin_unlock_irq(&queuespinlock);
- if (rv == 0)
- tasklet_schedule(&reader_tasklet);
- return rv;
-}
-
-/**
- * process_results copies the user's work from kernel space.
- */
-static inline int
-z90crypt_process_results(struct work_element *we_p, char __user *buf)
-{
- int rv;
-
- PDEBUG("we_p %p (PID %d)\n", we_p, PID());
-
- LONG2DEVPTR(we_p->devindex)->dev_total_req_cnt++;
- SET_RDWRMASK(we_p->status[0], STAT_READPEND);
-
- rv = 0;
- if (!we_p->buffer) {
- PRINTK("we_p %p PID %d in STAT_READPEND: buffer NULL.\n",
- we_p, PID());
- rv = -ENOBUFF;
- }
-
- if (!rv)
- if ((rv = copy_to_user(buf, we_p->buffer, we_p->buff_size))) {
- PDEBUG("copy_to_user failed: rv = %d\n", rv);
- rv = -EFAULT;
- }
-
- if (!rv)
- rv = we_p->retcode;
- if (!rv)
- if (we_p->resp_buff_size
- && copy_to_user(we_p->resp_addr, we_p->resp_buff,
- we_p->resp_buff_size))
- rv = -EFAULT;
-
- SET_RDWRMASK(we_p->status[0], STAT_NOWORK);
- return rv;
-}
-
-static unsigned char NULL_psmid[8] =
-{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-/**
- * Used in device configuration functions
- */
-#define MAX_RESET 90
-
-/**
- * This is used only for PCICC support
- */
-static inline int
-is_PKCS11_padded(unsigned char *buffer, int length)
-{
- int i;
- if ((buffer[0] != 0x00) || (buffer[1] != 0x01))
- return 0;
- for (i = 2; i < length; i++)
- if (buffer[i] != 0xFF)
- break;
- if ((i < 10) || (i == length))
- return 0;
- if (buffer[i] != 0x00)
- return 0;
- return 1;
-}
-
-/**
- * This is used only for PCICC support
- */
-static inline int
-is_PKCS12_padded(unsigned char *buffer, int length)
-{
- int i;
- if ((buffer[0] != 0x00) || (buffer[1] != 0x02))
- return 0;
- for (i = 2; i < length; i++)
- if (buffer[i] == 0x00)
- break;
- if ((i < 10) || (i == length))
- return 0;
- if (buffer[i] != 0x00)
- return 0;
- return 1;
-}
-
-/**
- * builds struct caller and converts message from generic format to
- * device-dependent format
- * func is ICARSAMODEXPO or ICARSACRT
- * function is PCI_FUNC_KEY_ENCRYPT or PCI_FUNC_KEY_DECRYPT
- */
-static inline int
-build_caller(struct work_element *we_p, short function)
-{
- int rv;
- struct caller *caller_p = (struct caller *)we_p->requestptr;
-
- if ((we_p->devtype != PCICC) && (we_p->devtype != PCICA) &&
- (we_p->devtype != PCIXCC_MCL2) && (we_p->devtype != PCIXCC_MCL3) &&
- (we_p->devtype != CEX2C) && (we_p->devtype != CEX2A))
- return SEN_NOT_AVAIL;
-
- memcpy(caller_p->caller_id, we_p->caller_id,
- sizeof(caller_p->caller_id));
- caller_p->caller_dev_dep_req_p = caller_p->caller_dev_dep_req;
- caller_p->caller_dev_dep_req_l = MAX_RESPONSE_SIZE;
- caller_p->caller_buf_p = we_p->buffer;
- INIT_LIST_HEAD(&(caller_p->caller_liste));
-
- rv = convert_request(we_p->buffer, we_p->funccode, function,
- z90crypt.cdx, we_p->devtype,
- &caller_p->caller_dev_dep_req_l,
- caller_p->caller_dev_dep_req_p);
- if (rv) {
- if (rv == SEN_NOT_AVAIL)
- PDEBUG("request can't be processed on hdwr avail\n");
- else
- PRINTK("Error from convert_request: %d\n", rv);
- }
- else
- memcpy(&(caller_p->caller_dev_dep_req_p[4]), we_p->caller_id,8);
- return rv;
-}
-
-static inline void
-unbuild_caller(struct device *device_p, struct caller *caller_p)
-{
- if (!caller_p)
- return;
- if (caller_p->caller_liste.next && caller_p->caller_liste.prev)
- if (!list_empty(&caller_p->caller_liste)) {
- list_del_init(&caller_p->caller_liste);
- device_p->dev_caller_count--;
- }
- memset(caller_p->caller_id, 0, sizeof(caller_p->caller_id));
-}
-
-static inline int
-get_crypto_request_buffer(struct work_element *we_p)
-{
- struct ica_rsa_modexpo *mex_p;
- struct ica_rsa_modexpo_crt *crt_p;
- unsigned char *temp_buffer;
- short function;
- int rv;
-
- mex_p = (struct ica_rsa_modexpo *) we_p->buffer;
- crt_p = (struct ica_rsa_modexpo_crt *) we_p->buffer;
-
- PDEBUG("device type input = %d\n", we_p->devtype);
-
- if (z90crypt.terminating)
- return REC_NO_RESPONSE;
- if (memcmp(we_p->caller_id, NULL_psmid, 8) == 0) {
- PRINTK("psmid zeroes\n");
- return SEN_FATAL_ERROR;
- }
- if (!we_p->buffer) {
- PRINTK("buffer pointer NULL\n");
- return SEN_USER_ERROR;
- }
- if (!we_p->requestptr) {
- PRINTK("caller pointer NULL\n");
- return SEN_USER_ERROR;
- }
-
- if ((we_p->devtype != PCICA) && (we_p->devtype != PCICC) &&
- (we_p->devtype != PCIXCC_MCL2) && (we_p->devtype != PCIXCC_MCL3) &&
- (we_p->devtype != CEX2C) && (we_p->devtype != CEX2A) &&
- (we_p->devtype != ANYDEV)) {
- PRINTK("invalid device type\n");
- return SEN_USER_ERROR;
- }
-
- if ((mex_p->inputdatalength < 1) ||
- (mex_p->inputdatalength > MAX_MOD_SIZE)) {
- PRINTK("inputdatalength[%d] is not valid\n",
- mex_p->inputdatalength);
- return SEN_USER_ERROR;
- }
-
- if (mex_p->outputdatalength < mex_p->inputdatalength) {
- PRINTK("outputdatalength[%d] < inputdatalength[%d]\n",
- mex_p->outputdatalength, mex_p->inputdatalength);
- return SEN_USER_ERROR;
- }
-
- if (!mex_p->inputdata || !mex_p->outputdata) {
- PRINTK("inputdata[%p] or outputdata[%p] is NULL\n",
- mex_p->outputdata, mex_p->inputdata);
- return SEN_USER_ERROR;
- }
-
- /**
- * As long as outputdatalength is big enough, we can set the
- * outputdatalength equal to the inputdatalength, since that is the
- * number of bytes we will copy in any case
- */
- mex_p->outputdatalength = mex_p->inputdatalength;
-
- rv = 0;
- switch (we_p->funccode) {
- case ICARSAMODEXPO:
- if (!mex_p->b_key || !mex_p->n_modulus)
- rv = SEN_USER_ERROR;
- break;
- case ICARSACRT:
- if (!IS_EVEN(crt_p->inputdatalength)) {
- PRINTK("inputdatalength[%d] is odd, CRT form\n",
- crt_p->inputdatalength);
- rv = SEN_USER_ERROR;
- break;
- }
- if (!crt_p->bp_key ||
- !crt_p->bq_key ||
- !crt_p->np_prime ||
- !crt_p->nq_prime ||
- !crt_p->u_mult_inv) {
- PRINTK("CRT form, bad data: %p/%p/%p/%p/%p\n",
- crt_p->bp_key, crt_p->bq_key,
- crt_p->np_prime, crt_p->nq_prime,
- crt_p->u_mult_inv);
- rv = SEN_USER_ERROR;
- }
- break;
- default:
- PRINTK("bad func = %d\n", we_p->funccode);
- rv = SEN_USER_ERROR;
- break;
- }
- if (rv != 0)
- return rv;
-
- if (select_device_type(&we_p->devtype, mex_p->inputdatalength) < 0)
- return SEN_NOT_AVAIL;
-
- temp_buffer = (unsigned char *)we_p + sizeof(struct work_element) +
- sizeof(struct caller);
- if (copy_from_user(temp_buffer, mex_p->inputdata,
- mex_p->inputdatalength) != 0)
- return SEN_RELEASED;
-
- function = PCI_FUNC_KEY_ENCRYPT;
- switch (we_p->devtype) {
- /* PCICA and CEX2A do everything with a simple RSA mod-expo operation */
- case PCICA:
- case CEX2A:
- function = PCI_FUNC_KEY_ENCRYPT;
- break;
- /**
- * PCIXCC_MCL2 does all Mod-Expo form with a simple RSA mod-expo
- * operation, and all CRT forms with a PKCS-1.2 format decrypt.
- * PCIXCC_MCL3 and CEX2C do all Mod-Expo and CRT forms with a simple RSA
- * mod-expo operation
- */
- case PCIXCC_MCL2:
- if (we_p->funccode == ICARSAMODEXPO)
- function = PCI_FUNC_KEY_ENCRYPT;
- else
- function = PCI_FUNC_KEY_DECRYPT;
- break;
- case PCIXCC_MCL3:
- case CEX2C:
- if (we_p->funccode == ICARSAMODEXPO)
- function = PCI_FUNC_KEY_ENCRYPT;
- else
- function = PCI_FUNC_KEY_DECRYPT;
- break;
- /**
- * PCICC does everything as a PKCS-1.2 format request
- */
- case PCICC:
- /* PCICC cannot handle input that is is PKCS#1.1 padded */
- if (is_PKCS11_padded(temp_buffer, mex_p->inputdatalength)) {
- return SEN_NOT_AVAIL;
- }
- if (we_p->funccode == ICARSAMODEXPO) {
- if (is_PKCS12_padded(temp_buffer,
- mex_p->inputdatalength))
- function = PCI_FUNC_KEY_ENCRYPT;
- else
- function = PCI_FUNC_KEY_DECRYPT;
- } else
- /* all CRT forms are decrypts */
- function = PCI_FUNC_KEY_DECRYPT;
- break;
- }
- PDEBUG("function: %04x\n", function);
- rv = build_caller(we_p, function);
- PDEBUG("rv from build_caller = %d\n", rv);
- return rv;
-}
-
-static inline int
-z90crypt_prepare(struct work_element *we_p, unsigned int funccode,
- const char __user *buffer)
-{
- int rv;
-
- we_p->devindex = -1;
- if (funccode == ICARSAMODEXPO)
- we_p->buff_size = sizeof(struct ica_rsa_modexpo);
- else
- we_p->buff_size = sizeof(struct ica_rsa_modexpo_crt);
-
- if (copy_from_user(we_p->buffer, buffer, we_p->buff_size))
- return -EFAULT;
-
- we_p->audit[0] |= FP_COPYFROM;
- SET_RDWRMASK(we_p->status[0], STAT_WRITTEN);
- we_p->funccode = funccode;
- we_p->devtype = -1;
- we_p->audit[0] |= FP_BUFFREQ;
- rv = get_crypto_request_buffer(we_p);
- switch (rv) {
- case 0:
- we_p->audit[0] |= FP_BUFFGOT;
- break;
- case SEN_USER_ERROR:
- rv = -EINVAL;
- break;
- case SEN_QUEUE_FULL:
- rv = 0;
- break;
- case SEN_RELEASED:
- rv = -EFAULT;
- break;
- case REC_NO_RESPONSE:
- rv = -ENODEV;
- break;
- case SEN_NOT_AVAIL:
- case EGETBUFF:
- rv = -EGETBUFF;
- break;
- default:
- PRINTK("rv = %d\n", rv);
- rv = -EGETBUFF;
- break;
- }
- if (CHK_RDWRMASK(we_p->status[0]) == STAT_WRITTEN)
- SET_RDWRMASK(we_p->status[0], STAT_DEFAULT);
- return rv;
-}
-
-static inline void
-purge_work_element(struct work_element *we_p)
-{
- struct list_head *lptr;
-
- spin_lock_irq(&queuespinlock);
- list_for_each(lptr, &request_list) {
- if (lptr == &we_p->liste) {
- list_del_init(lptr);
- requestq_count--;
- break;
- }
- }
- list_for_each(lptr, &pending_list) {
- if (lptr == &we_p->liste) {
- list_del_init(lptr);
- pendingq_count--;
- break;
- }
- }
- spin_unlock_irq(&queuespinlock);
-}
-
-/**
- * Build the request and send it.
- */
-static inline int
-z90crypt_rsa(struct priv_data *private_data_p, pid_t pid,
- unsigned int cmd, unsigned long arg)
-{
- struct work_element *we_p;
- int rv;
-
- if ((rv = allocate_work_element(&we_p, private_data_p, pid))) {
- PDEBUG("PID %d: allocate_work_element returned ENOMEM\n", pid);
- return rv;
- }
- if ((rv = z90crypt_prepare(we_p, cmd, (const char __user *)arg)))
- PDEBUG("PID %d: rv = %d from z90crypt_prepare\n", pid, rv);
- if (!rv)
- if ((rv = z90crypt_send(we_p, (const char *)arg)))
- PDEBUG("PID %d: rv %d from z90crypt_send.\n", pid, rv);
- if (!rv) {
- we_p->audit[0] |= FP_ASLEEP;
- wait_event(we_p->waitq, atomic_read(&we_p->alarmrung));
- we_p->audit[0] |= FP_AWAKE;
- rv = we_p->retcode;
- }
- if (!rv)
- rv = z90crypt_process_results(we_p, (char __user *)arg);
-
- if ((we_p->status[0] & STAT_FAILED)) {
- switch (rv) {
- /**
- * EINVAL *after* receive is almost always a padding error or
- * length error issued by a coprocessor (not an accelerator).
- * We convert this return value to -EGETBUFF which should
- * trigger a fallback to software.
- */
- case -EINVAL:
- if ((we_p->devtype != PCICA) &&
- (we_p->devtype != CEX2A))
- rv = -EGETBUFF;
- break;
- case -ETIMEOUT:
- if (z90crypt.mask.st_count > 0)
- rv = -ERESTARTSYS; // retry with another
- else
- rv = -ENODEV; // no cards left
- /* fall through to clean up request queue */
- case -ERESTARTSYS:
- case -ERELEASED:
- switch (CHK_RDWRMASK(we_p->status[0])) {
- case STAT_WRITTEN:
- purge_work_element(we_p);
- break;
- case STAT_READPEND:
- case STAT_NOWORK:
- default:
- break;
- }
- break;
- default:
- we_p->status[0] ^= STAT_FAILED;
- break;
- }
- }
- free_page((long)we_p);
- return rv;
-}
-
-/**
- * This function is a little long, but it's really just one large switch
- * statement.
- */
-static long
-z90crypt_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
- struct priv_data *private_data_p = filp->private_data;
- unsigned char *status;
- unsigned char *qdepth;
- unsigned int *reqcnt;
- struct ica_z90_status *pstat;
- int ret, i, loopLim, tempstat;
- static int deprecated_msg_count1 = 0;
- static int deprecated_msg_count2 = 0;
-
- PDEBUG("filp %p (PID %d), cmd 0x%08X\n", filp, PID(), cmd);
- PDEBUG("cmd 0x%08X: dir %s, size 0x%04X, type 0x%02X, nr 0x%02X\n",
- cmd,
- !_IOC_DIR(cmd) ? "NO"
- : ((_IOC_DIR(cmd) == (_IOC_READ|_IOC_WRITE)) ? "RW"
- : ((_IOC_DIR(cmd) == _IOC_READ) ? "RD"
- : "WR")),
- _IOC_SIZE(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd));
-
- if (_IOC_TYPE(cmd) != Z90_IOCTL_MAGIC) {
- PRINTK("cmd 0x%08X contains bad magic\n", cmd);
- return -ENOTTY;
- }
-
- ret = 0;
- switch (cmd) {
- case ICARSAMODEXPO:
- case ICARSACRT:
- if (quiesce_z90crypt) {
- ret = -EQUIESCE;
- break;
- }
- ret = -ENODEV; // Default if no devices
- loopLim = z90crypt.hdware_info->hdware_mask.st_count -
- (z90crypt.hdware_info->hdware_mask.disabled_count +
- z90crypt.hdware_info->hdware_mask.user_disabled_count);
- for (i = 0; i < loopLim; i++) {
- ret = z90crypt_rsa(private_data_p, PID(), cmd, arg);
- if (ret != -ERESTARTSYS)
- break;
- }
- if (ret == -ERESTARTSYS)
- ret = -ENODEV;
- break;
-
- case Z90STAT_TOTALCOUNT:
- tempstat = get_status_totalcount();
- if (copy_to_user((int __user *)arg, &tempstat,sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90STAT_PCICACOUNT:
- tempstat = get_status_PCICAcount();
- if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90STAT_PCICCCOUNT:
- tempstat = get_status_PCICCcount();
- if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90STAT_PCIXCCMCL2COUNT:
- tempstat = get_status_PCIXCCMCL2count();
- if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90STAT_PCIXCCMCL3COUNT:
- tempstat = get_status_PCIXCCMCL3count();
- if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90STAT_CEX2CCOUNT:
- tempstat = get_status_CEX2Ccount();
- if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90STAT_CEX2ACOUNT:
- tempstat = get_status_CEX2Acount();
- if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90STAT_REQUESTQ_COUNT:
- tempstat = get_status_requestq_count();
- if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90STAT_PENDINGQ_COUNT:
- tempstat = get_status_pendingq_count();
- if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90STAT_TOTALOPEN_COUNT:
- tempstat = get_status_totalopen_count();
- if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90STAT_DOMAIN_INDEX:
- tempstat = get_status_domain_index();
- if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90STAT_STATUS_MASK:
- status = kmalloc(Z90CRYPT_NUM_APS, GFP_KERNEL);
- if (!status) {
- PRINTK("kmalloc for status failed!\n");
- ret = -ENOMEM;
- break;
- }
- get_status_status_mask(status);
- if (copy_to_user((char __user *) arg, status, Z90CRYPT_NUM_APS)
- != 0)
- ret = -EFAULT;
- kfree(status);
- break;
-
- case Z90STAT_QDEPTH_MASK:
- qdepth = kmalloc(Z90CRYPT_NUM_APS, GFP_KERNEL);
- if (!qdepth) {
- PRINTK("kmalloc for qdepth failed!\n");
- ret = -ENOMEM;
- break;
- }
- get_status_qdepth_mask(qdepth);
- if (copy_to_user((char __user *) arg, qdepth, Z90CRYPT_NUM_APS) != 0)
- ret = -EFAULT;
- kfree(qdepth);
- break;
-
- case Z90STAT_PERDEV_REQCNT:
- reqcnt = kmalloc(sizeof(int) * Z90CRYPT_NUM_APS, GFP_KERNEL);
- if (!reqcnt) {
- PRINTK("kmalloc for reqcnt failed!\n");
- ret = -ENOMEM;
- break;
- }
- get_status_perdevice_reqcnt(reqcnt);
- if (copy_to_user((char __user *) arg, reqcnt,
- Z90CRYPT_NUM_APS * sizeof(int)) != 0)
- ret = -EFAULT;
- kfree(reqcnt);
- break;
-
- /* THIS IS DEPRECATED. USE THE NEW STATUS CALLS */
- case ICAZ90STATUS:
- if (deprecated_msg_count1 < 20) {
- PRINTK("deprecated call to ioctl (ICAZ90STATUS)!\n");
- deprecated_msg_count1++;
- if (deprecated_msg_count1 == 20)
- PRINTK("No longer issuing messages related to "
- "deprecated call to ICAZ90STATUS.\n");
- }
-
- pstat = kmalloc(sizeof(struct ica_z90_status), GFP_KERNEL);
- if (!pstat) {
- PRINTK("kmalloc for pstat failed!\n");
- ret = -ENOMEM;
- break;
- }
-
- pstat->totalcount = get_status_totalcount();
- pstat->leedslitecount = get_status_PCICAcount();
- pstat->leeds2count = get_status_PCICCcount();
- pstat->requestqWaitCount = get_status_requestq_count();
- pstat->pendingqWaitCount = get_status_pendingq_count();
- pstat->totalOpenCount = get_status_totalopen_count();
- pstat->cryptoDomain = get_status_domain_index();
- get_status_status_mask(pstat->status);
- get_status_qdepth_mask(pstat->qdepth);
-
- if (copy_to_user((struct ica_z90_status __user *) arg, pstat,
- sizeof(struct ica_z90_status)) != 0)
- ret = -EFAULT;
- kfree(pstat);
- break;
-
- /* THIS IS DEPRECATED. USE THE NEW STATUS CALLS */
- case Z90STAT_PCIXCCCOUNT:
- if (deprecated_msg_count2 < 20) {
- PRINTK("deprecated ioctl (Z90STAT_PCIXCCCOUNT)!\n");
- deprecated_msg_count2++;
- if (deprecated_msg_count2 == 20)
- PRINTK("No longer issuing messages about depre"
- "cated ioctl Z90STAT_PCIXCCCOUNT.\n");
- }
-
- tempstat = get_status_PCIXCCcount();
- if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0)
- ret = -EFAULT;
- break;
-
- case Z90QUIESCE:
- if (current->euid != 0) {
- PRINTK("QUIESCE fails: euid %d\n",
- current->euid);
- ret = -EACCES;
- } else {
- PRINTK("QUIESCE device from PID %d\n", PID());
- quiesce_z90crypt = 1;
- }
- break;
-
- default:
- /* user passed an invalid IOCTL number */
- PDEBUG("cmd 0x%08X contains invalid ioctl code\n", cmd);
- ret = -ENOTTY;
- break;
- }
-
- return ret;
-}
-
-static inline int
-sprintcl(unsigned char *outaddr, unsigned char *addr, unsigned int len)
-{
- int hl, i;
-
- hl = 0;
- for (i = 0; i < len; i++)
- hl += sprintf(outaddr+hl, "%01x", (unsigned int) addr[i]);
- hl += sprintf(outaddr+hl, " ");
-
- return hl;
-}
-
-static inline int
-sprintrw(unsigned char *outaddr, unsigned char *addr, unsigned int len)
-{
- int hl, inl, c, cx;
-
- hl = sprintf(outaddr, " ");
- inl = 0;
- for (c = 0; c < (len / 16); c++) {
- hl += sprintcl(outaddr+hl, addr+inl, 16);
- inl += 16;
- }
-
- cx = len%16;
- if (cx) {
- hl += sprintcl(outaddr+hl, addr+inl, cx);
- inl += cx;
- }
-
- hl += sprintf(outaddr+hl, "\n");
-
- return hl;
-}
-
-static inline int
-sprinthx(unsigned char *title, unsigned char *outaddr,
- unsigned char *addr, unsigned int len)
-{
- int hl, inl, r, rx;
-
- hl = sprintf(outaddr, "\n%s\n", title);
- inl = 0;
- for (r = 0; r < (len / 64); r++) {
- hl += sprintrw(outaddr+hl, addr+inl, 64);
- inl += 64;
- }
- rx = len % 64;
- if (rx) {
- hl += sprintrw(outaddr+hl, addr+inl, rx);
- inl += rx;
- }
-
- hl += sprintf(outaddr+hl, "\n");
-
- return hl;
-}
-
-static inline int
-sprinthx4(unsigned char *title, unsigned char *outaddr,
- unsigned int *array, unsigned int len)
-{
- int hl, r;
-
- hl = sprintf(outaddr, "\n%s\n", title);
-
- for (r = 0; r < len; r++) {
- if ((r % 8) == 0)
- hl += sprintf(outaddr+hl, " ");
- hl += sprintf(outaddr+hl, "%08X ", array[r]);
- if ((r % 8) == 7)
- hl += sprintf(outaddr+hl, "\n");
- }
-
- hl += sprintf(outaddr+hl, "\n");
-
- return hl;
-}
-
-static int
-z90crypt_status(char *resp_buff, char **start, off_t offset,
- int count, int *eof, void *data)
-{
- unsigned char *workarea;
- int len;
-
- /* resp_buff is a page. Use the right half for a work area */
- workarea = resp_buff+2000;
- len = 0;
- len += sprintf(resp_buff+len, "\nz90crypt version: %d.%d.%d\n",
- z90crypt_VERSION, z90crypt_RELEASE, z90crypt_VARIANT);
- len += sprintf(resp_buff+len, "Cryptographic domain: %d\n",
- get_status_domain_index());
- len += sprintf(resp_buff+len, "Total device count: %d\n",
- get_status_totalcount());
- len += sprintf(resp_buff+len, "PCICA count: %d\n",
- get_status_PCICAcount());
- len += sprintf(resp_buff+len, "PCICC count: %d\n",
- get_status_PCICCcount());
- len += sprintf(resp_buff+len, "PCIXCC MCL2 count: %d\n",
- get_status_PCIXCCMCL2count());
- len += sprintf(resp_buff+len, "PCIXCC MCL3 count: %d\n",
- get_status_PCIXCCMCL3count());
- len += sprintf(resp_buff+len, "CEX2C count: %d\n",
- get_status_CEX2Ccount());
- len += sprintf(resp_buff+len, "CEX2A count: %d\n",
- get_status_CEX2Acount());
- len += sprintf(resp_buff+len, "requestq count: %d\n",
- get_status_requestq_count());
- len += sprintf(resp_buff+len, "pendingq count: %d\n",
- get_status_pendingq_count());
- len += sprintf(resp_buff+len, "Total open handles: %d\n\n",
- get_status_totalopen_count());
- len += sprinthx(
- "Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) "
- "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A",
- resp_buff+len,
- get_status_status_mask(workarea),
- Z90CRYPT_NUM_APS);
- len += sprinthx("Waiting work element counts",
- resp_buff+len,
- get_status_qdepth_mask(workarea),
- Z90CRYPT_NUM_APS);
- len += sprinthx4(
- "Per-device successfully completed request counts",
- resp_buff+len,
- get_status_perdevice_reqcnt((unsigned int *)workarea),
- Z90CRYPT_NUM_APS);
- *eof = 1;
- memset(workarea, 0, Z90CRYPT_NUM_APS * sizeof(unsigned int));
- return len;
-}
-
-static inline void
-disable_card(int card_index)
-{
- struct device *devp;
-
- devp = LONG2DEVPTR(card_index);
- if (!devp || devp->user_disabled)
- return;
- devp->user_disabled = 1;
- z90crypt.hdware_info->hdware_mask.user_disabled_count++;
- if (devp->dev_type == -1)
- return;
- z90crypt.hdware_info->type_mask[devp->dev_type].user_disabled_count++;
-}
-
-static inline void
-enable_card(int card_index)
-{
- struct device *devp;
-
- devp = LONG2DEVPTR(card_index);
- if (!devp || !devp->user_disabled)
- return;
- devp->user_disabled = 0;
- z90crypt.hdware_info->hdware_mask.user_disabled_count--;
- if (devp->dev_type == -1)
- return;
- z90crypt.hdware_info->type_mask[devp->dev_type].user_disabled_count--;
-}
-
-static int
-z90crypt_status_write(struct file *file, const char __user *buffer,
- unsigned long count, void *data)
-{
- int j, eol;
- unsigned char *lbuf, *ptr;
- unsigned int local_count;
-
-#define LBUFSIZE 1200
- lbuf = kmalloc(LBUFSIZE, GFP_KERNEL);
- if (!lbuf) {
- PRINTK("kmalloc failed!\n");
- return 0;
- }
-
- if (count <= 0)
- return 0;
-
- local_count = UMIN((unsigned int)count, LBUFSIZE-1);
-
- if (copy_from_user(lbuf, buffer, local_count) != 0) {
- kfree(lbuf);
- return -EFAULT;
- }
-
- lbuf[local_count] = '\0';
-
- ptr = strstr(lbuf, "Online devices");
- if (ptr == 0) {
- PRINTK("Unable to parse data (missing \"Online devices\")\n");
- kfree(lbuf);
- return count;
- }
-
- ptr = strstr(ptr, "\n");
- if (ptr == 0) {
- PRINTK("Unable to parse data (missing newline after \"Online devices\")\n");
- kfree(lbuf);
- return count;
- }
- ptr++;
-
- if (strstr(ptr, "Waiting work element counts") == NULL) {
- PRINTK("Unable to parse data (missing \"Waiting work element counts\")\n");
- kfree(lbuf);
- return count;
- }
-
- j = 0;
- eol = 0;
- while ((j < 64) && (*ptr != '\0')) {
- switch (*ptr) {
- case '\t':
- case ' ':
- break;
- case '\n':
- default:
- eol = 1;
- break;
- case '0': // no device
- case '1': // PCICA
- case '2': // PCICC
- case '3': // PCIXCC_MCL2
- case '4': // PCIXCC_MCL3
- case '5': // CEX2C
- case '6': // CEX2A
- j++;
- break;
- case 'd':
- case 'D':
- disable_card(j);
- j++;
- break;
- case 'e':
- case 'E':
- enable_card(j);
- j++;
- break;
- }
- if (eol)
- break;
- ptr++;
- }
-
- kfree(lbuf);
- return count;
-}
-
-/**
- * Functions that run under a timer, with no process id
- *
- * The task functions:
- * z90crypt_reader_task
- * helper_send_work
- * helper_handle_work_element
- * helper_receive_rc
- * z90crypt_config_task
- * z90crypt_cleanup_task
- *
- * Helper functions:
- * z90crypt_schedule_reader_timer
- * z90crypt_schedule_reader_task
- * z90crypt_schedule_config_task
- * z90crypt_schedule_cleanup_task
- */
-static inline int
-receive_from_crypto_device(int index, unsigned char *psmid, int *buff_len_p,
- unsigned char *buff, unsigned char __user **dest_p_p)
-{
- int dv, rv;
- struct device *dev_ptr;
- struct caller *caller_p;
- struct ica_rsa_modexpo *icaMsg_p;
- struct list_head *ptr, *tptr;
-
- memcpy(psmid, NULL_psmid, sizeof(NULL_psmid));
-
- if (z90crypt.terminating)
- return REC_FATAL_ERROR;
-
- caller_p = 0;
- dev_ptr = z90crypt.device_p[index];
- rv = 0;
- do {
- if (!dev_ptr || dev_ptr->disabled) {
- rv = REC_NO_WORK; // a disabled device can't return work
- break;
- }
- if (dev_ptr->dev_self_x != index) {
- PRINTKC("Corrupt dev ptr\n");
- z90crypt.terminating = 1;
- rv = REC_FATAL_ERROR;
- break;
- }
- if (!dev_ptr->dev_resp_l || !dev_ptr->dev_resp_p) {
- dv = DEV_REC_EXCEPTION;
- PRINTK("dev_resp_l = %d, dev_resp_p = %p\n",
- dev_ptr->dev_resp_l, dev_ptr->dev_resp_p);
- } else {
- PDEBUG("Dequeue called for device %d\n", index);
- dv = receive_from_AP(index, z90crypt.cdx,
- dev_ptr->dev_resp_l,
- dev_ptr->dev_resp_p, psmid);
- }
- switch (dv) {
- case DEV_REC_EXCEPTION:
- rv = REC_FATAL_ERROR;
- z90crypt.terminating = 1;
- PRINTKC("Exception in receive from device %d\n",
- index);
- break;
- case DEV_ONLINE:
- rv = 0;
- break;
- case DEV_EMPTY:
- rv = REC_EMPTY;
- break;
- case DEV_NO_WORK:
- rv = REC_NO_WORK;
- break;
- case DEV_BAD_MESSAGE:
- case DEV_GONE:
- case REC_HARDWAR_ERR:
- default:
- rv = REC_NO_RESPONSE;
- break;
- }
- if (rv)
- break;
- if (dev_ptr->dev_caller_count <= 0) {
- rv = REC_USER_GONE;
- break;
- }
-
- list_for_each_safe(ptr, tptr, &dev_ptr->dev_caller_list) {
- caller_p = list_entry(ptr, struct caller, caller_liste);
- if (!memcmp(caller_p->caller_id, psmid,
- sizeof(caller_p->caller_id))) {
- if (!list_empty(&caller_p->caller_liste)) {
- list_del_init(ptr);
- dev_ptr->dev_caller_count--;
- break;
- }
- }
- caller_p = 0;
- }
- if (!caller_p) {
- PRINTKW("Unable to locate PSMID %02X%02X%02X%02X%02X"
- "%02X%02X%02X in device list\n",
- psmid[0], psmid[1], psmid[2], psmid[3],
- psmid[4], psmid[5], psmid[6], psmid[7]);
- rv = REC_USER_GONE;
- break;
- }
-
- PDEBUG("caller_p after successful receive: %p\n", caller_p);
- rv = convert_response(dev_ptr->dev_resp_p,
- caller_p->caller_buf_p, buff_len_p, buff);
- switch (rv) {
- case REC_USE_PCICA:
- break;
- case REC_OPERAND_INV:
- case REC_OPERAND_SIZE:
- case REC_EVEN_MOD:
- case REC_INVALID_PAD:
- PDEBUG("device %d: 'user error' %d\n", index, rv);
- break;
- case WRONG_DEVICE_TYPE:
- case REC_HARDWAR_ERR:
- case REC_BAD_MESSAGE:
- PRINTKW("device %d: hardware error %d\n", index, rv);
- rv = REC_NO_RESPONSE;
- break;
- default:
- PDEBUG("device %d: rv = %d\n", index, rv);
- break;
- }
- } while (0);
-
- switch (rv) {
- case 0:
- PDEBUG("Successful receive from device %d\n", index);
- icaMsg_p = (struct ica_rsa_modexpo *)caller_p->caller_buf_p;
- *dest_p_p = icaMsg_p->outputdata;
- if (*buff_len_p == 0)
- PRINTK("Zero *buff_len_p\n");
- break;
- case REC_NO_RESPONSE:
- PRINTKW("Removing device %d from availability\n", index);
- remove_device(dev_ptr);
- break;
- }
-
- if (caller_p)
- unbuild_caller(dev_ptr, caller_p);
-
- return rv;
-}
-
-static inline void
-helper_send_work(int index)
-{
- struct work_element *rq_p;
- int rv;
-
- if (list_empty(&request_list))
- return;
- requestq_count--;
- rq_p = list_entry(request_list.next, struct work_element, liste);
- list_del_init(&rq_p->liste);
- rq_p->audit[1] |= FP_REMREQUEST;
- if (rq_p->devtype == SHRT2DEVPTR(index)->dev_type) {
- rq_p->devindex = SHRT2LONG(index);
- rv = send_to_crypto_device(rq_p);
- if (rv == 0) {
- rq_p->requestsent = jiffies;
- rq_p->audit[0] |= FP_SENT;
- list_add_tail(&rq_p->liste, &pending_list);
- ++pendingq_count;
- rq_p->audit[0] |= FP_PENDING;
- } else {
- switch (rv) {
- case REC_OPERAND_INV:
- case REC_OPERAND_SIZE:
- case REC_EVEN_MOD:
- case REC_INVALID_PAD:
- rq_p->retcode = -EINVAL;
- break;
- case SEN_NOT_AVAIL:
- case SEN_RETRY:
- case REC_NO_RESPONSE:
- default:
- if (z90crypt.mask.st_count > 1)
- rq_p->retcode =
- -ERESTARTSYS;
- else
- rq_p->retcode = -ENODEV;
- break;
- }
- rq_p->status[0] |= STAT_FAILED;
- rq_p->audit[1] |= FP_AWAKENING;
- atomic_set(&rq_p->alarmrung, 1);
- wake_up(&rq_p->waitq);
- }
- } else {
- if (z90crypt.mask.st_count > 1)
- rq_p->retcode = -ERESTARTSYS;
- else
- rq_p->retcode = -ENODEV;
- rq_p->status[0] |= STAT_FAILED;
- rq_p->audit[1] |= FP_AWAKENING;
- atomic_set(&rq_p->alarmrung, 1);
- wake_up(&rq_p->waitq);
- }
-}
-
-static inline void
-helper_handle_work_element(int index, unsigned char psmid[8], int rc,
- int buff_len, unsigned char *buff,
- unsigned char __user *resp_addr)
-{
- struct work_element *pq_p;
- struct list_head *lptr, *tptr;
-
- pq_p = 0;
- list_for_each_safe(lptr, tptr, &pending_list) {
- pq_p = list_entry(lptr, struct work_element, liste);
- if (!memcmp(pq_p->caller_id, psmid, sizeof(pq_p->caller_id))) {
- list_del_init(lptr);
- pendingq_count--;
- pq_p->audit[1] |= FP_NOTPENDING;
- break;
- }
- pq_p = 0;
- }
-
- if (!pq_p) {
- PRINTK("device %d has work but no caller exists on pending Q\n",
- SHRT2LONG(index));
- return;
- }
-
- switch (rc) {
- case 0:
- pq_p->resp_buff_size = buff_len;
- pq_p->audit[1] |= FP_RESPSIZESET;
- if (buff_len) {
- pq_p->resp_addr = resp_addr;
- pq_p->audit[1] |= FP_RESPADDRCOPIED;
- memcpy(pq_p->resp_buff, buff, buff_len);
- pq_p->audit[1] |= FP_RESPBUFFCOPIED;
- }
- break;
- case REC_OPERAND_INV:
- case REC_OPERAND_SIZE:
- case REC_EVEN_MOD:
- case REC_INVALID_PAD:
- PDEBUG("-EINVAL after application error %d\n", rc);
- pq_p->retcode = -EINVAL;
- pq_p->status[0] |= STAT_FAILED;
- break;
- case REC_USE_PCICA:
- pq_p->retcode = -ERESTARTSYS;
- pq_p->status[0] |= STAT_FAILED;
- break;
- case REC_NO_RESPONSE:
- default:
- if (z90crypt.mask.st_count > 1)
- pq_p->retcode = -ERESTARTSYS;
- else
- pq_p->retcode = -ENODEV;
- pq_p->status[0] |= STAT_FAILED;
- break;
- }
- if ((pq_p->status[0] != STAT_FAILED) || (pq_p->retcode != -ERELEASED)) {
- pq_p->audit[1] |= FP_AWAKENING;
- atomic_set(&pq_p->alarmrung, 1);
- wake_up(&pq_p->waitq);
- }
-}
-
-/**
- * return TRUE if the work element should be removed from the queue
- */
-static inline int
-helper_receive_rc(int index, int *rc_p)
-{
- switch (*rc_p) {
- case 0:
- case REC_OPERAND_INV:
- case REC_OPERAND_SIZE:
- case REC_EVEN_MOD:
- case REC_INVALID_PAD:
- case REC_USE_PCICA:
- break;
-
- case REC_BUSY:
- case REC_NO_WORK:
- case REC_EMPTY:
- case REC_RETRY_DEV:
- case REC_FATAL_ERROR:
- return 0;
-
- case REC_NO_RESPONSE:
- break;
-
- default:
- PRINTK("rc %d, device %d converted to REC_NO_RESPONSE\n",
- *rc_p, SHRT2LONG(index));
- *rc_p = REC_NO_RESPONSE;
- break;
- }
- return 1;
-}
-
-static inline void
-z90crypt_schedule_reader_timer(void)
-{
- if (timer_pending(&reader_timer))
- return;
- if (mod_timer(&reader_timer, jiffies+(READERTIME*HZ/1000)) != 0)
- PRINTK("Timer pending while modifying reader timer\n");
-}
-
-static void
-z90crypt_reader_task(unsigned long ptr)
-{
- int workavail, index, rc, buff_len;
- unsigned char psmid[8];
- unsigned char __user *resp_addr;
- static unsigned char buff[1024];
-
- /**
- * we use workavail = 2 to ensure 2 passes with nothing dequeued before
- * exiting the loop. If (pendingq_count+requestq_count) == 0 after the
- * loop, there is no work remaining on the queues.
- */
- resp_addr = 0;
- workavail = 2;
- buff_len = 0;
- while (workavail) {
- workavail--;
- rc = 0;
- spin_lock_irq(&queuespinlock);
- memset(buff, 0x00, sizeof(buff));
-
- /* Dequeue once from each device in round robin. */
- for (index = 0; index < z90crypt.mask.st_count; index++) {
- PDEBUG("About to receive.\n");
- rc = receive_from_crypto_device(SHRT2LONG(index),
- psmid,
- &buff_len,
- buff,
- &resp_addr);
- PDEBUG("Dequeued: rc = %d.\n", rc);
-
- if (helper_receive_rc(index, &rc)) {
- if (rc != REC_NO_RESPONSE) {
- helper_send_work(index);
- workavail = 2;
- }
-
- helper_handle_work_element(index, psmid, rc,
- buff_len, buff,
- resp_addr);
- }
-
- if (rc == REC_FATAL_ERROR)
- PRINTKW("REC_FATAL_ERROR from device %d!\n",
- SHRT2LONG(index));
- }
- spin_unlock_irq(&queuespinlock);
- }
-
- if (pendingq_count + requestq_count)
- z90crypt_schedule_reader_timer();
-}
-
-static inline void
-z90crypt_schedule_config_task(unsigned int expiration)
-{
- if (timer_pending(&config_timer))
- return;
- if (mod_timer(&config_timer, jiffies+(expiration*HZ)) != 0)
- PRINTK("Timer pending while modifying config timer\n");
-}
-
-static void
-z90crypt_config_task(unsigned long ptr)
-{
- int rc;
-
- PDEBUG("jiffies %ld\n", jiffies);
-
- if ((rc = refresh_z90crypt(&z90crypt.cdx)))
- PRINTK("Error %d detected in refresh_z90crypt.\n", rc);
- /* If return was fatal, don't bother reconfiguring */
- if ((rc != TSQ_FATAL_ERROR) && (rc != RSQ_FATAL_ERROR))
- z90crypt_schedule_config_task(CONFIGTIME);
-}
-
-static inline void
-z90crypt_schedule_cleanup_task(void)
-{
- if (timer_pending(&cleanup_timer))
- return;
- if (mod_timer(&cleanup_timer, jiffies+(CLEANUPTIME*HZ)) != 0)
- PRINTK("Timer pending while modifying cleanup timer\n");
-}
-
-static inline void
-helper_drain_queues(void)
-{
- struct work_element *pq_p;
- struct list_head *lptr, *tptr;
-
- list_for_each_safe(lptr, tptr, &pending_list) {
- pq_p = list_entry(lptr, struct work_element, liste);
- pq_p->retcode = -ENODEV;
- pq_p->status[0] |= STAT_FAILED;
- unbuild_caller(LONG2DEVPTR(pq_p->devindex),
- (struct caller *)pq_p->requestptr);
- list_del_init(lptr);
- pendingq_count--;
- pq_p->audit[1] |= FP_NOTPENDING;
- pq_p->audit[1] |= FP_AWAKENING;
- atomic_set(&pq_p->alarmrung, 1);
- wake_up(&pq_p->waitq);
- }
-
- list_for_each_safe(lptr, tptr, &request_list) {
- pq_p = list_entry(lptr, struct work_element, liste);
- pq_p->retcode = -ENODEV;
- pq_p->status[0] |= STAT_FAILED;
- list_del_init(lptr);
- requestq_count--;
- pq_p->audit[1] |= FP_REMREQUEST;
- pq_p->audit[1] |= FP_AWAKENING;
- atomic_set(&pq_p->alarmrung, 1);
- wake_up(&pq_p->waitq);
- }
-}
-
-static inline void
-helper_timeout_requests(void)
-{
- struct work_element *pq_p;
- struct list_head *lptr, *tptr;
- long timelimit;
-
- timelimit = jiffies - (CLEANUPTIME * HZ);
- /* The list is in strict chronological order */
- list_for_each_safe(lptr, tptr, &pending_list) {
- pq_p = list_entry(lptr, struct work_element, liste);
- if (pq_p->requestsent >= timelimit)
- break;
- PRINTKW("Purging(PQ) PSMID %02X%02X%02X%02X%02X%02X%02X%02X\n",
- ((struct caller *)pq_p->requestptr)->caller_id[0],
- ((struct caller *)pq_p->requestptr)->caller_id[1],
- ((struct caller *)pq_p->requestptr)->caller_id[2],
- ((struct caller *)pq_p->requestptr)->caller_id[3],
- ((struct caller *)pq_p->requestptr)->caller_id[4],
- ((struct caller *)pq_p->requestptr)->caller_id[5],
- ((struct caller *)pq_p->requestptr)->caller_id[6],
- ((struct caller *)pq_p->requestptr)->caller_id[7]);
- pq_p->retcode = -ETIMEOUT;
- pq_p->status[0] |= STAT_FAILED;
- /* get this off any caller queue it may be on */
- unbuild_caller(LONG2DEVPTR(pq_p->devindex),
- (struct caller *) pq_p->requestptr);
- list_del_init(lptr);
- pendingq_count--;
- pq_p->audit[1] |= FP_TIMEDOUT;
- pq_p->audit[1] |= FP_NOTPENDING;
- pq_p->audit[1] |= FP_AWAKENING;
- atomic_set(&pq_p->alarmrung, 1);
- wake_up(&pq_p->waitq);
- }
-
- /**
- * If pending count is zero, items left on the request queue may
- * never be processed.
- */
- if (pendingq_count <= 0) {
- list_for_each_safe(lptr, tptr, &request_list) {
- pq_p = list_entry(lptr, struct work_element, liste);
- if (pq_p->requestsent >= timelimit)
- break;
- PRINTKW("Purging(RQ) PSMID %02X%02X%02X%02X%02X%02X%02X%02X\n",
- ((struct caller *)pq_p->requestptr)->caller_id[0],
- ((struct caller *)pq_p->requestptr)->caller_id[1],
- ((struct caller *)pq_p->requestptr)->caller_id[2],
- ((struct caller *)pq_p->requestptr)->caller_id[3],
- ((struct caller *)pq_p->requestptr)->caller_id[4],
- ((struct caller *)pq_p->requestptr)->caller_id[5],
- ((struct caller *)pq_p->requestptr)->caller_id[6],
- ((struct caller *)pq_p->requestptr)->caller_id[7]);
- pq_p->retcode = -ETIMEOUT;
- pq_p->status[0] |= STAT_FAILED;
- list_del_init(lptr);
- requestq_count--;
- pq_p->audit[1] |= FP_TIMEDOUT;
- pq_p->audit[1] |= FP_REMREQUEST;
- pq_p->audit[1] |= FP_AWAKENING;
- atomic_set(&pq_p->alarmrung, 1);
- wake_up(&pq_p->waitq);
- }
- }
-}
-
-static void
-z90crypt_cleanup_task(unsigned long ptr)
-{
- PDEBUG("jiffies %ld\n", jiffies);
- spin_lock_irq(&queuespinlock);
- if (z90crypt.mask.st_count <= 0) // no devices!
- helper_drain_queues();
- else
- helper_timeout_requests();
- spin_unlock_irq(&queuespinlock);
- z90crypt_schedule_cleanup_task();
-}
-
-static void
-z90crypt_schedule_reader_task(unsigned long ptr)
-{
- tasklet_schedule(&reader_tasklet);
-}
-
-/**
- * Lowlevel Functions:
- *
- * create_z90crypt: creates and initializes basic data structures
- * refresh_z90crypt: re-initializes basic data structures
- * find_crypto_devices: returns a count and mask of hardware status
- * create_crypto_device: builds the descriptor for a device
- * destroy_crypto_device: unallocates the descriptor for a device
- * destroy_z90crypt: drains all work, unallocates structs
- */
-
-/**
- * build the z90crypt root structure using the given domain index
- */
-static int
-create_z90crypt(int *cdx_p)
-{
- struct hdware_block *hdware_blk_p;
-
- memset(&z90crypt, 0x00, sizeof(struct z90crypt));
- z90crypt.domain_established = 0;
- z90crypt.len = sizeof(struct z90crypt);
- z90crypt.max_count = Z90CRYPT_NUM_DEVS;
- z90crypt.cdx = *cdx_p;
-
- hdware_blk_p = kzalloc(sizeof(struct hdware_block), GFP_ATOMIC);
- if (!hdware_blk_p) {
- PDEBUG("kmalloc for hardware block failed\n");
- return ENOMEM;
- }
- z90crypt.hdware_info = hdware_blk_p;
-
- return 0;
-}
-
-static inline int
-helper_scan_devices(int cdx_array[16], int *cdx_p, int *correct_cdx_found)
-{
- enum hdstat hd_stat;
- int q_depth, dev_type;
- int indx, chkdom, numdomains;
-
- q_depth = dev_type = numdomains = 0;
- for (chkdom = 0; chkdom <= 15; cdx_array[chkdom++] = -1);
- for (indx = 0; indx < z90crypt.max_count; indx++) {
- hd_stat = HD_NOT_THERE;
- numdomains = 0;
- for (chkdom = 0; chkdom <= 15; chkdom++) {
- hd_stat = query_online(indx, chkdom, MAX_RESET,
- &q_depth, &dev_type);
- if (hd_stat == HD_TSQ_EXCEPTION) {
- z90crypt.terminating = 1;
- PRINTKC("exception taken!\n");
- break;
- }
- if (hd_stat == HD_ONLINE) {
- cdx_array[numdomains++] = chkdom;
- if (*cdx_p == chkdom) {
- *correct_cdx_found = 1;
- break;
- }
- }
- }
- if ((*correct_cdx_found == 1) || (numdomains != 0))
- break;
- if (z90crypt.terminating)
- break;
- }
- return numdomains;
-}
-
-static inline int
-probe_crypto_domain(int *cdx_p)
-{
- int cdx_array[16];
- char cdx_array_text[53], temp[5];
- int correct_cdx_found, numdomains;
-
- correct_cdx_found = 0;
- numdomains = helper_scan_devices(cdx_array, cdx_p, &correct_cdx_found);
-
- if (z90crypt.terminating)
- return TSQ_FATAL_ERROR;
-
- if (correct_cdx_found)
- return 0;
-
- if (numdomains == 0) {
- PRINTKW("Unable to find crypto domain: No devices found\n");
- return Z90C_NO_DEVICES;
- }
-
- if (numdomains == 1) {
- if (*cdx_p == -1) {
- *cdx_p = cdx_array[0];
- return 0;
- }
- PRINTKW("incorrect domain: specified = %d, found = %d\n",
- *cdx_p, cdx_array[0]);
- return Z90C_INCORRECT_DOMAIN;
- }
-
- numdomains--;
- sprintf(cdx_array_text, "%d", cdx_array[numdomains]);
- while (numdomains) {
- numdomains--;
- sprintf(temp, ", %d", cdx_array[numdomains]);
- strcat(cdx_array_text, temp);
- }
-
- PRINTKW("ambiguous domain detected: specified = %d, found array = %s\n",
- *cdx_p, cdx_array_text);
- return Z90C_AMBIGUOUS_DOMAIN;
-}
-
-static int
-refresh_z90crypt(int *cdx_p)
-{
- int i, j, indx, rv;
- static struct status local_mask;
- struct device *devPtr;
- unsigned char oldStat, newStat;
- int return_unchanged;
-
- if (z90crypt.len != sizeof(z90crypt))
- return ENOTINIT;
- if (z90crypt.terminating)
- return TSQ_FATAL_ERROR;
- rv = 0;
- if (!z90crypt.hdware_info->hdware_mask.st_count &&
- !z90crypt.domain_established) {
- rv = probe_crypto_domain(cdx_p);
- if (z90crypt.terminating)
- return TSQ_FATAL_ERROR;
- if (rv == Z90C_NO_DEVICES)
- return 0; // try later
- if (rv)
- return rv;
- z90crypt.cdx = *cdx_p;
- z90crypt.domain_established = 1;
- }
- rv = find_crypto_devices(&local_mask);
- if (rv) {
- PRINTK("find crypto devices returned %d\n", rv);
- return rv;
- }
- if (!memcmp(&local_mask, &z90crypt.hdware_info->hdware_mask,
- sizeof(struct status))) {
- return_unchanged = 1;
- for (i = 0; i < Z90CRYPT_NUM_TYPES; i++) {
- /**
- * Check for disabled cards. If any device is marked
- * disabled, destroy it.
- */
- for (j = 0;
- j < z90crypt.hdware_info->type_mask[i].st_count;
- j++) {
- indx = z90crypt.hdware_info->type_x_addr[i].
- device_index[j];
- devPtr = z90crypt.device_p[indx];
- if (devPtr && devPtr->disabled) {
- local_mask.st_mask[indx] = HD_NOT_THERE;
- return_unchanged = 0;
- }
- }
- }
- if (return_unchanged == 1)
- return 0;
- }
-
- spin_lock_irq(&queuespinlock);
- for (i = 0; i < z90crypt.max_count; i++) {
- oldStat = z90crypt.hdware_info->hdware_mask.st_mask[i];
- newStat = local_mask.st_mask[i];
- if ((oldStat == HD_ONLINE) && (newStat != HD_ONLINE))
- destroy_crypto_device(i);
- else if ((oldStat != HD_ONLINE) && (newStat == HD_ONLINE)) {
- rv = create_crypto_device(i);
- if (rv >= REC_FATAL_ERROR)
- return rv;
- if (rv != 0) {
- local_mask.st_mask[i] = HD_NOT_THERE;
- local_mask.st_count--;
- }
- }
- }
- memcpy(z90crypt.hdware_info->hdware_mask.st_mask, local_mask.st_mask,
- sizeof(local_mask.st_mask));
- z90crypt.hdware_info->hdware_mask.st_count = local_mask.st_count;
- z90crypt.hdware_info->hdware_mask.disabled_count =
- local_mask.disabled_count;
- refresh_index_array(&z90crypt.mask, &z90crypt.overall_device_x);
- for (i = 0; i < Z90CRYPT_NUM_TYPES; i++)
- refresh_index_array(&(z90crypt.hdware_info->type_mask[i]),
- &(z90crypt.hdware_info->type_x_addr[i]));
- spin_unlock_irq(&queuespinlock);
-
- return rv;
-}
-
-static int
-find_crypto_devices(struct status *deviceMask)
-{
- int i, q_depth, dev_type;
- enum hdstat hd_stat;
-
- deviceMask->st_count = 0;
- deviceMask->disabled_count = 0;
- deviceMask->user_disabled_count = 0;
-
- for (i = 0; i < z90crypt.max_count; i++) {
- hd_stat = query_online(i, z90crypt.cdx, MAX_RESET, &q_depth,
- &dev_type);
- if (hd_stat == HD_TSQ_EXCEPTION) {
- z90crypt.terminating = 1;
- PRINTKC("Exception during probe for crypto devices\n");
- return TSQ_FATAL_ERROR;
- }
- deviceMask->st_mask[i] = hd_stat;
- if (hd_stat == HD_ONLINE) {
- PDEBUG("Got an online crypto!: %d\n", i);
- PDEBUG("Got a queue depth of %d\n", q_depth);
- PDEBUG("Got a device type of %d\n", dev_type);
- if (q_depth <= 0)
- return TSQ_FATAL_ERROR;
- deviceMask->st_count++;
- z90crypt.q_depth_array[i] = q_depth;
- z90crypt.dev_type_array[i] = dev_type;
- }
- }
-
- return 0;
-}
-
-static int
-refresh_index_array(struct status *status_str, struct device_x *index_array)
-{
- int i, count;
- enum devstat stat;
-
- i = -1;
- count = 0;
- do {
- stat = status_str->st_mask[++i];
- if (stat == DEV_ONLINE)
- index_array->device_index[count++] = i;
- } while ((i < Z90CRYPT_NUM_DEVS) && (count < status_str->st_count));
-
- return count;
-}
-
-static int
-create_crypto_device(int index)
-{
- int rv, devstat, total_size;
- struct device *dev_ptr;
- struct status *type_str_p;
- int deviceType;
-
- dev_ptr = z90crypt.device_p[index];
- if (!dev_ptr) {
- total_size = sizeof(struct device) +
- z90crypt.q_depth_array[index] * sizeof(int);
-
- dev_ptr = kzalloc(total_size, GFP_ATOMIC);
- if (!dev_ptr) {
- PRINTK("kmalloc device %d failed\n", index);
- return ENOMEM;
- }
- dev_ptr->dev_resp_p = kmalloc(MAX_RESPONSE_SIZE, GFP_ATOMIC);
- if (!dev_ptr->dev_resp_p) {
- kfree(dev_ptr);
- PRINTK("kmalloc device %d rec buffer failed\n", index);
- return ENOMEM;
- }
- dev_ptr->dev_resp_l = MAX_RESPONSE_SIZE;
- INIT_LIST_HEAD(&(dev_ptr->dev_caller_list));
- }
-
- devstat = reset_device(index, z90crypt.cdx, MAX_RESET);
- if (devstat == DEV_RSQ_EXCEPTION) {
- PRINTK("exception during reset device %d\n", index);
- kfree(dev_ptr->dev_resp_p);
- kfree(dev_ptr);
- return RSQ_FATAL_ERROR;
- }
- if (devstat == DEV_ONLINE) {
- dev_ptr->dev_self_x = index;
- dev_ptr->dev_type = z90crypt.dev_type_array[index];
- if (dev_ptr->dev_type == NILDEV) {
- rv = probe_device_type(dev_ptr);
- if (rv) {
- PRINTK("rv = %d from probe_device_type %d\n",
- rv, index);
- kfree(dev_ptr->dev_resp_p);
- kfree(dev_ptr);
- return rv;
- }
- }
- if (dev_ptr->dev_type == PCIXCC_UNK) {
- rv = probe_PCIXCC_type(dev_ptr);
- if (rv) {
- PRINTK("rv = %d from probe_PCIXCC_type %d\n",
- rv, index);
- kfree(dev_ptr->dev_resp_p);
- kfree(dev_ptr);
- return rv;
- }
- }
- deviceType = dev_ptr->dev_type;
- z90crypt.dev_type_array[index] = deviceType;
- if (deviceType == PCICA)
- z90crypt.hdware_info->device_type_array[index] = 1;
- else if (deviceType == PCICC)
- z90crypt.hdware_info->device_type_array[index] = 2;
- else if (deviceType == PCIXCC_MCL2)
- z90crypt.hdware_info->device_type_array[index] = 3;
- else if (deviceType == PCIXCC_MCL3)
- z90crypt.hdware_info->device_type_array[index] = 4;
- else if (deviceType == CEX2C)
- z90crypt.hdware_info->device_type_array[index] = 5;
- else if (deviceType == CEX2A)
- z90crypt.hdware_info->device_type_array[index] = 6;
- else // No idea how this would happen.
- z90crypt.hdware_info->device_type_array[index] = -1;
- }
-
- /**
- * 'q_depth' returned by the hardware is one less than
- * the actual depth
- */
- dev_ptr->dev_q_depth = z90crypt.q_depth_array[index];
- dev_ptr->dev_type = z90crypt.dev_type_array[index];
- dev_ptr->dev_stat = devstat;
- dev_ptr->disabled = 0;
- z90crypt.device_p[index] = dev_ptr;
-
- if (devstat == DEV_ONLINE) {
- if (z90crypt.mask.st_mask[index] != DEV_ONLINE) {
- z90crypt.mask.st_mask[index] = DEV_ONLINE;
- z90crypt.mask.st_count++;
- }
- deviceType = dev_ptr->dev_type;
- type_str_p = &z90crypt.hdware_info->type_mask[deviceType];
- if (type_str_p->st_mask[index] != DEV_ONLINE) {
- type_str_p->st_mask[index] = DEV_ONLINE;
- type_str_p->st_count++;
- }
- }
-
- return 0;
-}
-
-static int
-destroy_crypto_device(int index)
-{
- struct device *dev_ptr;
- int t, disabledFlag;
-
- dev_ptr = z90crypt.device_p[index];
-
- /* remember device type; get rid of device struct */
- if (dev_ptr) {
- disabledFlag = dev_ptr->disabled;
- t = dev_ptr->dev_type;
- kfree(dev_ptr->dev_resp_p);
- kfree(dev_ptr);
- } else {
- disabledFlag = 0;
- t = -1;
- }
- z90crypt.device_p[index] = 0;
-
- /* if the type is valid, remove the device from the type_mask */
- if ((t != -1) && z90crypt.hdware_info->type_mask[t].st_mask[index]) {
- z90crypt.hdware_info->type_mask[t].st_mask[index] = 0x00;
- z90crypt.hdware_info->type_mask[t].st_count--;
- if (disabledFlag == 1)
- z90crypt.hdware_info->type_mask[t].disabled_count--;
- }
- if (z90crypt.mask.st_mask[index] != DEV_GONE) {
- z90crypt.mask.st_mask[index] = DEV_GONE;
- z90crypt.mask.st_count--;
- }
- z90crypt.hdware_info->device_type_array[index] = 0;
-
- return 0;
-}
-
-static void
-destroy_z90crypt(void)
-{
- int i;
-
- for (i = 0; i < z90crypt.max_count; i++)
- if (z90crypt.device_p[i])
- destroy_crypto_device(i);
- kfree(z90crypt.hdware_info);
- memset((void *)&z90crypt, 0, sizeof(z90crypt));
-}
-
-static unsigned char static_testmsg[384] = {
-0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x06,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x58,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x43,
-0x41,0x2d,0x41,0x50,0x50,0x4c,0x20,0x20,0x20,0x01,0x01,0x01,0x00,0x00,0x00,0x00,
-0x50,0x4b,0x00,0x00,0x00,0x00,0x01,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x05,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x70,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x32,
-0x01,0x00,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0xb8,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x49,0x43,0x53,0x46,
-0x20,0x20,0x20,0x20,0x50,0x4b,0x0a,0x00,0x50,0x4b,0x43,0x53,0x2d,0x31,0x2e,0x32,
-0x37,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,
-0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,
-0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66,
-0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x5d,0x00,0x5b,0x00,0x77,0x88,0x1e,0x00,0x00,
-0x57,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x4f,0x00,0x00,0x00,0x03,0x02,0x00,0x00,
-0x40,0x01,0x00,0x01,0xce,0x02,0x68,0x2d,0x5f,0xa9,0xde,0x0c,0xf6,0xd2,0x7b,0x58,
-0x4b,0xf9,0x28,0x68,0x3d,0xb4,0xf4,0xef,0x78,0xd5,0xbe,0x66,0x63,0x42,0xef,0xf8,
-0xfd,0xa4,0xf8,0xb0,0x8e,0x29,0xc2,0xc9,0x2e,0xd8,0x45,0xb8,0x53,0x8c,0x6f,0x4e,
-0x72,0x8f,0x6c,0x04,0x9c,0x88,0xfc,0x1e,0xc5,0x83,0x55,0x57,0xf7,0xdd,0xfd,0x4f,
-0x11,0x36,0x95,0x5d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
-};
-
-static int
-probe_device_type(struct device *devPtr)
-{
- int rv, dv, i, index, length;
- unsigned char psmid[8];
- static unsigned char loc_testmsg[sizeof(static_testmsg)];
-
- index = devPtr->dev_self_x;
- rv = 0;
- do {
- memcpy(loc_testmsg, static_testmsg, sizeof(static_testmsg));
- length = sizeof(static_testmsg) - 24;
- /* the -24 allows for the header */
- dv = send_to_AP(index, z90crypt.cdx, length, loc_testmsg);
- if (dv) {
- PDEBUG("dv returned by send during probe: %d\n", dv);
- if (dv == DEV_SEN_EXCEPTION) {
- rv = SEN_FATAL_ERROR;
- PRINTKC("exception in send to AP %d\n", index);
- break;
- }
- PDEBUG("return value from send_to_AP: %d\n", rv);
- switch (dv) {
- case DEV_GONE:
- PDEBUG("dev %d not available\n", index);
- rv = SEN_NOT_AVAIL;
- break;
- case DEV_ONLINE:
- rv = 0;
- break;
- case DEV_EMPTY:
- rv = SEN_NOT_AVAIL;
- break;
- case DEV_NO_WORK:
- rv = SEN_FATAL_ERROR;
- break;
- case DEV_BAD_MESSAGE:
- rv = SEN_USER_ERROR;
- break;
- case DEV_QUEUE_FULL:
- rv = SEN_QUEUE_FULL;
- break;
- default:
- PRINTK("unknown dv=%d for dev %d\n", dv, index);
- rv = SEN_NOT_AVAIL;
- break;
- }
- }
-
- if (rv)
- break;
-
- for (i = 0; i < 6; i++) {
- mdelay(300);
- dv = receive_from_AP(index, z90crypt.cdx,
- devPtr->dev_resp_l,
- devPtr->dev_resp_p, psmid);
- PDEBUG("dv returned by DQ = %d\n", dv);
- if (dv == DEV_REC_EXCEPTION) {
- rv = REC_FATAL_ERROR;
- PRINTKC("exception in dequeue %d\n",
- index);
- break;
- }
- switch (dv) {
- case DEV_ONLINE:
- rv = 0;
- break;
- case DEV_EMPTY:
- rv = REC_EMPTY;
- break;
- case DEV_NO_WORK:
- rv = REC_NO_WORK;
- break;
- case DEV_BAD_MESSAGE:
- case DEV_GONE:
- default:
- rv = REC_NO_RESPONSE;
- break;
- }
- if ((rv != 0) && (rv != REC_NO_WORK))
- break;
- if (rv == 0)
- break;
- }
- if (rv)
- break;
- rv = (devPtr->dev_resp_p[0] == 0x00) &&
- (devPtr->dev_resp_p[1] == 0x86);
- if (rv)
- devPtr->dev_type = PCICC;
- else
- devPtr->dev_type = PCICA;
- rv = 0;
- } while (0);
- /* In a general error case, the card is not marked online */
- return rv;
-}
-
-static unsigned char MCL3_testmsg[] = {
-0x00,0x00,0x00,0x00,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,
-0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x43,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x00,0x00,0x00,0x01,0xC4,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x02,0x00,0x00,0x00,0x54,0x32,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE8,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x0A,0x4D,0x52,0x50,0x20,0x20,0x20,0x20,0x20,
-0x00,0x42,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,
-0x0E,0x0F,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,
-0xEE,0xFF,0xFF,0xEE,0xDD,0xCC,0xBB,0xAA,0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22,
-0x11,0x00,0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,0xFE,0xDC,0xBA,0x98,0x76,0x54,
-0x32,0x10,0x00,0x9A,0x00,0x98,0x00,0x00,0x1E,0x00,0x00,0x94,0x00,0x00,0x00,0x00,
-0x04,0x00,0x00,0x8C,0x00,0x00,0x00,0x40,0x02,0x00,0x00,0x40,0xBA,0xE8,0x23,0x3C,
-0x75,0xF3,0x91,0x61,0xD6,0x73,0x39,0xCF,0x7B,0x6D,0x8E,0x61,0x97,0x63,0x9E,0xD9,
-0x60,0x55,0xD6,0xC7,0xEF,0xF8,0x1E,0x63,0x95,0x17,0xCC,0x28,0x45,0x60,0x11,0xC5,
-0xC4,0x4E,0x66,0xC6,0xE6,0xC3,0xDE,0x8A,0x19,0x30,0xCF,0x0E,0xD7,0xAA,0xDB,0x01,
-0xD8,0x00,0xBB,0x8F,0x39,0x9F,0x64,0x28,0xF5,0x7A,0x77,0x49,0xCC,0x6B,0xA3,0x91,
-0x97,0x70,0xE7,0x60,0x1E,0x39,0xE1,0xE5,0x33,0xE1,0x15,0x63,0x69,0x08,0x80,0x4C,
-0x67,0xC4,0x41,0x8F,0x48,0xDF,0x26,0x98,0xF1,0xD5,0x8D,0x88,0xD9,0x6A,0xA4,0x96,
-0xC5,0x84,0xD9,0x30,0x49,0x67,0x7D,0x19,0xB1,0xB3,0x45,0x4D,0xB2,0x53,0x9A,0x47,
-0x3C,0x7C,0x55,0xBF,0xCC,0x85,0x00,0x36,0xF1,0x3D,0x93,0x53
-};
-
-static int
-probe_PCIXCC_type(struct device *devPtr)
-{
- int rv, dv, i, index, length;
- unsigned char psmid[8];
- static unsigned char loc_testmsg[548];
- struct CPRBX *cprbx_p;
-
- index = devPtr->dev_self_x;
- rv = 0;
- do {
- memcpy(loc_testmsg, MCL3_testmsg, sizeof(MCL3_testmsg));
- length = sizeof(MCL3_testmsg) - 0x0C;
- dv = send_to_AP(index, z90crypt.cdx, length, loc_testmsg);
- if (dv) {
- PDEBUG("dv returned = %d\n", dv);
- if (dv == DEV_SEN_EXCEPTION) {
- rv = SEN_FATAL_ERROR;
- PRINTKC("exception in send to AP %d\n", index);
- break;
- }
- PDEBUG("return value from send_to_AP: %d\n", rv);
- switch (dv) {
- case DEV_GONE:
- PDEBUG("dev %d not available\n", index);
- rv = SEN_NOT_AVAIL;
- break;
- case DEV_ONLINE:
- rv = 0;
- break;
- case DEV_EMPTY:
- rv = SEN_NOT_AVAIL;
- break;
- case DEV_NO_WORK:
- rv = SEN_FATAL_ERROR;
- break;
- case DEV_BAD_MESSAGE:
- rv = SEN_USER_ERROR;
- break;
- case DEV_QUEUE_FULL:
- rv = SEN_QUEUE_FULL;
- break;
- default:
- PRINTK("unknown dv=%d for dev %d\n", dv, index);
- rv = SEN_NOT_AVAIL;
- break;
- }
- }
-
- if (rv)
- break;
-
- for (i = 0; i < 6; i++) {
- mdelay(300);
- dv = receive_from_AP(index, z90crypt.cdx,
- devPtr->dev_resp_l,
- devPtr->dev_resp_p, psmid);
- PDEBUG("dv returned by DQ = %d\n", dv);
- if (dv == DEV_REC_EXCEPTION) {
- rv = REC_FATAL_ERROR;
- PRINTKC("exception in dequeue %d\n",
- index);
- break;
- }
- switch (dv) {
- case DEV_ONLINE:
- rv = 0;
- break;
- case DEV_EMPTY:
- rv = REC_EMPTY;
- break;
- case DEV_NO_WORK:
- rv = REC_NO_WORK;
- break;
- case DEV_BAD_MESSAGE:
- case DEV_GONE:
- default:
- rv = REC_NO_RESPONSE;
- break;
- }
- if ((rv != 0) && (rv != REC_NO_WORK))
- break;
- if (rv == 0)
- break;
- }
- if (rv)
- break;
- cprbx_p = (struct CPRBX *) (devPtr->dev_resp_p + 48);
- if ((cprbx_p->ccp_rtcode == 8) && (cprbx_p->ccp_rscode == 33)) {
- devPtr->dev_type = PCIXCC_MCL2;
- PDEBUG("device %d is MCL2\n", index);
- } else {
- devPtr->dev_type = PCIXCC_MCL3;
- PDEBUG("device %d is MCL3\n", index);
- }
- } while (0);
- /* In a general error case, the card is not marked online */
- return rv;
-}
-
-module_init(z90crypt_init_module);
-module_exit(z90crypt_cleanup_module);
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
new file mode 100644
index 000000000000..1edc10a7a6f2
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -0,0 +1,1091 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_api.c
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ * Cornelia Huck <cornelia.huck@de.ibm.com>
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *
+ * 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/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/compat.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+
+#include "zcrypt_api.h"
+
+/**
+ * Module description.
+ */
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("Cryptographic Coprocessor interface, "
+ "Copyright 2001, 2006 IBM Corporation");
+MODULE_LICENSE("GPL");
+
+static DEFINE_SPINLOCK(zcrypt_device_lock);
+static LIST_HEAD(zcrypt_device_list);
+static int zcrypt_device_count = 0;
+static atomic_t zcrypt_open_count = ATOMIC_INIT(0);
+
+/**
+ * Device attributes common for all crypto devices.
+ */
+static ssize_t zcrypt_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct zcrypt_device *zdev = to_ap_dev(dev)->private;
+ return snprintf(buf, PAGE_SIZE, "%s\n", zdev->type_string);
+}
+
+static DEVICE_ATTR(type, 0444, zcrypt_type_show, NULL);
+
+static ssize_t zcrypt_online_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct zcrypt_device *zdev = to_ap_dev(dev)->private;
+ return snprintf(buf, PAGE_SIZE, "%d\n", zdev->online);
+}
+
+static ssize_t zcrypt_online_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct zcrypt_device *zdev = to_ap_dev(dev)->private;
+ int online;
+
+ if (sscanf(buf, "%d\n", &online) != 1 || online < 0 || online > 1)
+ return -EINVAL;
+ zdev->online = online;
+ if (!online)
+ ap_flush_queue(zdev->ap_dev);
+ return count;
+}
+
+static DEVICE_ATTR(online, 0644, zcrypt_online_show, zcrypt_online_store);
+
+static struct attribute * zcrypt_device_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_online.attr,
+ NULL,
+};
+
+static struct attribute_group zcrypt_device_attr_group = {
+ .attrs = zcrypt_device_attrs,
+};
+
+/**
+ * Move the device towards the head of the device list.
+ * Need to be called while holding the zcrypt device list lock.
+ * Note: cards with speed_rating of 0 are kept at the end of the list.
+ */
+static void __zcrypt_increase_preference(struct zcrypt_device *zdev)
+{
+ struct zcrypt_device *tmp;
+ struct list_head *l;
+
+ if (zdev->speed_rating == 0)
+ return;
+ for (l = zdev->list.prev; l != &zcrypt_device_list; l = l->prev) {
+ tmp = list_entry(l, struct zcrypt_device, list);
+ if ((tmp->request_count + 1) * tmp->speed_rating <=
+ (zdev->request_count + 1) * zdev->speed_rating &&
+ tmp->speed_rating != 0)
+ break;
+ }
+ if (l == zdev->list.prev)
+ return;
+ /* Move zdev behind l */
+ list_del(&zdev->list);
+ list_add(&zdev->list, l);
+}
+
+/**
+ * Move the device towards the tail of the device list.
+ * Need to be called while holding the zcrypt device list lock.
+ * Note: cards with speed_rating of 0 are kept at the end of the list.
+ */
+static void __zcrypt_decrease_preference(struct zcrypt_device *zdev)
+{
+ struct zcrypt_device *tmp;
+ struct list_head *l;
+
+ if (zdev->speed_rating == 0)
+ return;
+ for (l = zdev->list.next; l != &zcrypt_device_list; l = l->next) {
+ tmp = list_entry(l, struct zcrypt_device, list);
+ if ((tmp->request_count + 1) * tmp->speed_rating >
+ (zdev->request_count + 1) * zdev->speed_rating ||
+ tmp->speed_rating == 0)
+ break;
+ }
+ if (l == zdev->list.next)
+ return;
+ /* Move zdev before l */
+ list_del(&zdev->list);
+ list_add_tail(&zdev->list, l);
+}
+
+static void zcrypt_device_release(struct kref *kref)
+{
+ struct zcrypt_device *zdev =
+ container_of(kref, struct zcrypt_device, refcount);
+ zcrypt_device_free(zdev);
+}
+
+void zcrypt_device_get(struct zcrypt_device *zdev)
+{
+ kref_get(&zdev->refcount);
+}
+EXPORT_SYMBOL(zcrypt_device_get);
+
+int zcrypt_device_put(struct zcrypt_device *zdev)
+{
+ return kref_put(&zdev->refcount, zcrypt_device_release);
+}
+EXPORT_SYMBOL(zcrypt_device_put);
+
+struct zcrypt_device *zcrypt_device_alloc(size_t max_response_size)
+{
+ struct zcrypt_device *zdev;
+
+ zdev = kzalloc(sizeof(struct zcrypt_device), GFP_KERNEL);
+ if (!zdev)
+ return NULL;
+ zdev->reply.message = kmalloc(max_response_size, GFP_KERNEL);
+ if (!zdev->reply.message)
+ goto out_free;
+ zdev->reply.length = max_response_size;
+ spin_lock_init(&zdev->lock);
+ INIT_LIST_HEAD(&zdev->list);
+ return zdev;
+
+out_free:
+ kfree(zdev);
+ return NULL;
+}
+EXPORT_SYMBOL(zcrypt_device_alloc);
+
+void zcrypt_device_free(struct zcrypt_device *zdev)
+{
+ kfree(zdev->reply.message);
+ kfree(zdev);
+}
+EXPORT_SYMBOL(zcrypt_device_free);
+
+/**
+ * Register a crypto device.
+ */
+int zcrypt_device_register(struct zcrypt_device *zdev)
+{
+ int rc;
+
+ rc = sysfs_create_group(&zdev->ap_dev->device.kobj,
+ &zcrypt_device_attr_group);
+ if (rc)
+ goto out;
+ get_device(&zdev->ap_dev->device);
+ kref_init(&zdev->refcount);
+ spin_lock_bh(&zcrypt_device_lock);
+ zdev->online = 1; /* New devices are online by default. */
+ list_add_tail(&zdev->list, &zcrypt_device_list);
+ __zcrypt_increase_preference(zdev);
+ zcrypt_device_count++;
+ spin_unlock_bh(&zcrypt_device_lock);
+out:
+ return rc;
+}
+EXPORT_SYMBOL(zcrypt_device_register);
+
+/**
+ * Unregister a crypto device.
+ */
+void zcrypt_device_unregister(struct zcrypt_device *zdev)
+{
+ spin_lock_bh(&zcrypt_device_lock);
+ zcrypt_device_count--;
+ list_del_init(&zdev->list);
+ spin_unlock_bh(&zcrypt_device_lock);
+ sysfs_remove_group(&zdev->ap_dev->device.kobj,
+ &zcrypt_device_attr_group);
+ put_device(&zdev->ap_dev->device);
+ zcrypt_device_put(zdev);
+}
+EXPORT_SYMBOL(zcrypt_device_unregister);
+
+/**
+ * zcrypt_read is not be supported beyond zcrypt 1.3.1
+ */
+static ssize_t zcrypt_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ return -EPERM;
+}
+
+/**
+ * Write is is not allowed
+ */
+static ssize_t zcrypt_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ return -EPERM;
+}
+
+/**
+ * Device open/close functions to count number of users.
+ */
+static int zcrypt_open(struct inode *inode, struct file *filp)
+{
+ atomic_inc(&zcrypt_open_count);
+ return 0;
+}
+
+static int zcrypt_release(struct inode *inode, struct file *filp)
+{
+ atomic_dec(&zcrypt_open_count);
+ return 0;
+}
+
+/**
+ * zcrypt ioctls.
+ */
+static long zcrypt_rsa_modexpo(struct ica_rsa_modexpo *mex)
+{
+ struct zcrypt_device *zdev;
+ int rc;
+
+ if (mex->outputdatalength < mex->inputdatalength)
+ return -EINVAL;
+ /**
+ * As long as outputdatalength is big enough, we can set the
+ * outputdatalength equal to the inputdatalength, since that is the
+ * number of bytes we will copy in any case
+ */
+ mex->outputdatalength = mex->inputdatalength;
+
+ spin_lock_bh(&zcrypt_device_lock);
+ list_for_each_entry(zdev, &zcrypt_device_list, list) {
+ if (!zdev->online ||
+ !zdev->ops->rsa_modexpo ||
+ zdev->min_mod_size > mex->inputdatalength ||
+ zdev->max_mod_size < mex->inputdatalength)
+ continue;
+ zcrypt_device_get(zdev);
+ get_device(&zdev->ap_dev->device);
+ zdev->request_count++;
+ __zcrypt_decrease_preference(zdev);
+ spin_unlock_bh(&zcrypt_device_lock);
+ if (try_module_get(zdev->ap_dev->drv->driver.owner)) {
+ rc = zdev->ops->rsa_modexpo(zdev, mex);
+ module_put(zdev->ap_dev->drv->driver.owner);
+ }
+ else
+ rc = -EAGAIN;
+ spin_lock_bh(&zcrypt_device_lock);
+ zdev->request_count--;
+ __zcrypt_increase_preference(zdev);
+ put_device(&zdev->ap_dev->device);
+ zcrypt_device_put(zdev);
+ spin_unlock_bh(&zcrypt_device_lock);
+ return rc;
+ }
+ spin_unlock_bh(&zcrypt_device_lock);
+ return -ENODEV;
+}
+
+static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt)
+{
+ struct zcrypt_device *zdev;
+ unsigned long long z1, z2, z3;
+ int rc, copied;
+
+ if (crt->outputdatalength < crt->inputdatalength ||
+ (crt->inputdatalength & 1))
+ return -EINVAL;
+ /**
+ * As long as outputdatalength is big enough, we can set the
+ * outputdatalength equal to the inputdatalength, since that is the
+ * number of bytes we will copy in any case
+ */
+ crt->outputdatalength = crt->inputdatalength;
+
+ copied = 0;
+ restart:
+ spin_lock_bh(&zcrypt_device_lock);
+ list_for_each_entry(zdev, &zcrypt_device_list, list) {
+ if (!zdev->online ||
+ !zdev->ops->rsa_modexpo_crt ||
+ zdev->min_mod_size > crt->inputdatalength ||
+ zdev->max_mod_size < crt->inputdatalength)
+ continue;
+ if (zdev->short_crt && crt->inputdatalength > 240) {
+ /**
+ * Check inputdata for leading zeros for cards
+ * that can't handle np_prime, bp_key, or
+ * u_mult_inv > 128 bytes.
+ */
+ if (copied == 0) {
+ int len;
+ spin_unlock_bh(&zcrypt_device_lock);
+ /* len is max 256 / 2 - 120 = 8 */
+ len = crt->inputdatalength / 2 - 120;
+ z1 = z2 = z3 = 0;
+ if (copy_from_user(&z1, crt->np_prime, len) ||
+ copy_from_user(&z2, crt->bp_key, len) ||
+ copy_from_user(&z3, crt->u_mult_inv, len))
+ return -EFAULT;
+ copied = 1;
+ /**
+ * We have to restart device lookup -
+ * the device list may have changed by now.
+ */
+ goto restart;
+ }
+ if (z1 != 0ULL || z2 != 0ULL || z3 != 0ULL)
+ /* The device can't handle this request. */
+ continue;
+ }
+ zcrypt_device_get(zdev);
+ get_device(&zdev->ap_dev->device);
+ zdev->request_count++;
+ __zcrypt_decrease_preference(zdev);
+ spin_unlock_bh(&zcrypt_device_lock);
+ if (try_module_get(zdev->ap_dev->drv->driver.owner)) {
+ rc = zdev->ops->rsa_modexpo_crt(zdev, crt);
+ module_put(zdev->ap_dev->drv->driver.owner);
+ }
+ else
+ rc = -EAGAIN;
+ spin_lock_bh(&zcrypt_device_lock);
+ zdev->request_count--;
+ __zcrypt_increase_preference(zdev);
+ put_device(&zdev->ap_dev->device);
+ zcrypt_device_put(zdev);
+ spin_unlock_bh(&zcrypt_device_lock);
+ return rc;
+ }
+ spin_unlock_bh(&zcrypt_device_lock);
+ return -ENODEV;
+}
+
+static long zcrypt_send_cprb(struct ica_xcRB *xcRB)
+{
+ struct zcrypt_device *zdev;
+ int rc;
+
+ spin_lock_bh(&zcrypt_device_lock);
+ list_for_each_entry(zdev, &zcrypt_device_list, list) {
+ if (!zdev->online || !zdev->ops->send_cprb ||
+ (xcRB->user_defined != AUTOSELECT &&
+ AP_QID_DEVICE(zdev->ap_dev->qid) != xcRB->user_defined)
+ )
+ continue;
+ zcrypt_device_get(zdev);
+ get_device(&zdev->ap_dev->device);
+ zdev->request_count++;
+ __zcrypt_decrease_preference(zdev);
+ spin_unlock_bh(&zcrypt_device_lock);
+ if (try_module_get(zdev->ap_dev->drv->driver.owner)) {
+ rc = zdev->ops->send_cprb(zdev, xcRB);
+ module_put(zdev->ap_dev->drv->driver.owner);
+ }
+ else
+ rc = -EAGAIN;
+ spin_lock_bh(&zcrypt_device_lock);
+ zdev->request_count--;
+ __zcrypt_increase_preference(zdev);
+ put_device(&zdev->ap_dev->device);
+ zcrypt_device_put(zdev);
+ spin_unlock_bh(&zcrypt_device_lock);
+ return rc;
+ }
+ spin_unlock_bh(&zcrypt_device_lock);
+ return -ENODEV;
+}
+
+static void zcrypt_status_mask(char status[AP_DEVICES])
+{
+ struct zcrypt_device *zdev;
+
+ memset(status, 0, sizeof(char) * AP_DEVICES);
+ spin_lock_bh(&zcrypt_device_lock);
+ list_for_each_entry(zdev, &zcrypt_device_list, list)
+ status[AP_QID_DEVICE(zdev->ap_dev->qid)] =
+ zdev->online ? zdev->user_space_type : 0x0d;
+ spin_unlock_bh(&zcrypt_device_lock);
+}
+
+static void zcrypt_qdepth_mask(char qdepth[AP_DEVICES])
+{
+ struct zcrypt_device *zdev;
+
+ memset(qdepth, 0, sizeof(char) * AP_DEVICES);
+ spin_lock_bh(&zcrypt_device_lock);
+ list_for_each_entry(zdev, &zcrypt_device_list, list) {
+ spin_lock(&zdev->ap_dev->lock);
+ qdepth[AP_QID_DEVICE(zdev->ap_dev->qid)] =
+ zdev->ap_dev->pendingq_count +
+ zdev->ap_dev->requestq_count;
+ spin_unlock(&zdev->ap_dev->lock);
+ }
+ spin_unlock_bh(&zcrypt_device_lock);
+}
+
+static void zcrypt_perdev_reqcnt(int reqcnt[AP_DEVICES])
+{
+ struct zcrypt_device *zdev;
+
+ memset(reqcnt, 0, sizeof(int) * AP_DEVICES);
+ spin_lock_bh(&zcrypt_device_lock);
+ list_for_each_entry(zdev, &zcrypt_device_list, list) {
+ spin_lock(&zdev->ap_dev->lock);
+ reqcnt[AP_QID_DEVICE(zdev->ap_dev->qid)] =
+ zdev->ap_dev->total_request_count;
+ spin_unlock(&zdev->ap_dev->lock);
+ }
+ spin_unlock_bh(&zcrypt_device_lock);
+}
+
+static int zcrypt_pendingq_count(void)
+{
+ struct zcrypt_device *zdev;
+ int pendingq_count = 0;
+
+ spin_lock_bh(&zcrypt_device_lock);
+ list_for_each_entry(zdev, &zcrypt_device_list, list) {
+ spin_lock(&zdev->ap_dev->lock);
+ pendingq_count += zdev->ap_dev->pendingq_count;
+ spin_unlock(&zdev->ap_dev->lock);
+ }
+ spin_unlock_bh(&zcrypt_device_lock);
+ return pendingq_count;
+}
+
+static int zcrypt_requestq_count(void)
+{
+ struct zcrypt_device *zdev;
+ int requestq_count = 0;
+
+ spin_lock_bh(&zcrypt_device_lock);
+ list_for_each_entry(zdev, &zcrypt_device_list, list) {
+ spin_lock(&zdev->ap_dev->lock);
+ requestq_count += zdev->ap_dev->requestq_count;
+ spin_unlock(&zdev->ap_dev->lock);
+ }
+ spin_unlock_bh(&zcrypt_device_lock);
+ return requestq_count;
+}
+
+static int zcrypt_count_type(int type)
+{
+ struct zcrypt_device *zdev;
+ int device_count = 0;
+
+ spin_lock_bh(&zcrypt_device_lock);
+ list_for_each_entry(zdev, &zcrypt_device_list, list)
+ if (zdev->user_space_type == type)
+ device_count++;
+ spin_unlock_bh(&zcrypt_device_lock);
+ return device_count;
+}
+
+/**
+ * Old, deprecated combi status call.
+ */
+static long zcrypt_ica_status(struct file *filp, unsigned long arg)
+{
+ struct ica_z90_status *pstat;
+ int ret;
+
+ pstat = kzalloc(sizeof(*pstat), GFP_KERNEL);
+ if (!pstat)
+ return -ENOMEM;
+ pstat->totalcount = zcrypt_device_count;
+ pstat->leedslitecount = zcrypt_count_type(ZCRYPT_PCICA);
+ pstat->leeds2count = zcrypt_count_type(ZCRYPT_PCICC);
+ pstat->requestqWaitCount = zcrypt_requestq_count();
+ pstat->pendingqWaitCount = zcrypt_pendingq_count();
+ pstat->totalOpenCount = atomic_read(&zcrypt_open_count);
+ pstat->cryptoDomain = ap_domain_index;
+ zcrypt_status_mask(pstat->status);
+ zcrypt_qdepth_mask(pstat->qdepth);
+ ret = 0;
+ if (copy_to_user((void __user *) arg, pstat, sizeof(*pstat)))
+ ret = -EFAULT;
+ kfree(pstat);
+ return ret;
+}
+
+static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc;
+
+ switch (cmd) {
+ case ICARSAMODEXPO: {
+ struct ica_rsa_modexpo __user *umex = (void __user *) arg;
+ struct ica_rsa_modexpo mex;
+ if (copy_from_user(&mex, umex, sizeof(mex)))
+ return -EFAULT;
+ do {
+ rc = zcrypt_rsa_modexpo(&mex);
+ } while (rc == -EAGAIN);
+ if (rc)
+ return rc;
+ return put_user(mex.outputdatalength, &umex->outputdatalength);
+ }
+ case ICARSACRT: {
+ struct ica_rsa_modexpo_crt __user *ucrt = (void __user *) arg;
+ struct ica_rsa_modexpo_crt crt;
+ if (copy_from_user(&crt, ucrt, sizeof(crt)))
+ return -EFAULT;
+ do {
+ rc = zcrypt_rsa_crt(&crt);
+ } while (rc == -EAGAIN);
+ if (rc)
+ return rc;
+ return put_user(crt.outputdatalength, &ucrt->outputdatalength);
+ }
+ case ZSECSENDCPRB: {
+ struct ica_xcRB __user *uxcRB = (void __user *) arg;
+ struct ica_xcRB xcRB;
+ if (copy_from_user(&xcRB, uxcRB, sizeof(xcRB)))
+ return -EFAULT;
+ do {
+ rc = zcrypt_send_cprb(&xcRB);
+ } while (rc == -EAGAIN);
+ if (copy_to_user(uxcRB, &xcRB, sizeof(xcRB)))
+ return -EFAULT;
+ return rc;
+ }
+ case Z90STAT_STATUS_MASK: {
+ char status[AP_DEVICES];
+ zcrypt_status_mask(status);
+ if (copy_to_user((char __user *) arg, status,
+ sizeof(char) * AP_DEVICES))
+ return -EFAULT;
+ return 0;
+ }
+ case Z90STAT_QDEPTH_MASK: {
+ char qdepth[AP_DEVICES];
+ zcrypt_qdepth_mask(qdepth);
+ if (copy_to_user((char __user *) arg, qdepth,
+ sizeof(char) * AP_DEVICES))
+ return -EFAULT;
+ return 0;
+ }
+ case Z90STAT_PERDEV_REQCNT: {
+ int reqcnt[AP_DEVICES];
+ zcrypt_perdev_reqcnt(reqcnt);
+ if (copy_to_user((int __user *) arg, reqcnt,
+ sizeof(int) * AP_DEVICES))
+ return -EFAULT;
+ return 0;
+ }
+ case Z90STAT_REQUESTQ_COUNT:
+ return put_user(zcrypt_requestq_count(), (int __user *) arg);
+ case Z90STAT_PENDINGQ_COUNT:
+ return put_user(zcrypt_pendingq_count(), (int __user *) arg);
+ case Z90STAT_TOTALOPEN_COUNT:
+ return put_user(atomic_read(&zcrypt_open_count),
+ (int __user *) arg);
+ case Z90STAT_DOMAIN_INDEX:
+ return put_user(ap_domain_index, (int __user *) arg);
+ /**
+ * Deprecated ioctls. Don't add another device count ioctl,
+ * you can count them yourself in the user space with the
+ * output of the Z90STAT_STATUS_MASK ioctl.
+ */
+ case ICAZ90STATUS:
+ return zcrypt_ica_status(filp, arg);
+ case Z90STAT_TOTALCOUNT:
+ return put_user(zcrypt_device_count, (int __user *) arg);
+ case Z90STAT_PCICACOUNT:
+ return put_user(zcrypt_count_type(ZCRYPT_PCICA),
+ (int __user *) arg);
+ case Z90STAT_PCICCCOUNT:
+ return put_user(zcrypt_count_type(ZCRYPT_PCICC),
+ (int __user *) arg);
+ case Z90STAT_PCIXCCMCL2COUNT:
+ return put_user(zcrypt_count_type(ZCRYPT_PCIXCC_MCL2),
+ (int __user *) arg);
+ case Z90STAT_PCIXCCMCL3COUNT:
+ return put_user(zcrypt_count_type(ZCRYPT_PCIXCC_MCL3),
+ (int __user *) arg);
+ case Z90STAT_PCIXCCCOUNT:
+ return put_user(zcrypt_count_type(ZCRYPT_PCIXCC_MCL2) +
+ zcrypt_count_type(ZCRYPT_PCIXCC_MCL3),
+ (int __user *) arg);
+ case Z90STAT_CEX2CCOUNT:
+ return put_user(zcrypt_count_type(ZCRYPT_CEX2C),
+ (int __user *) arg);
+ case Z90STAT_CEX2ACOUNT:
+ return put_user(zcrypt_count_type(ZCRYPT_CEX2A),
+ (int __user *) arg);
+ default:
+ /* unknown ioctl number */
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+/**
+ * ioctl32 conversion routines
+ */
+struct compat_ica_rsa_modexpo {
+ compat_uptr_t inputdata;
+ unsigned int inputdatalength;
+ compat_uptr_t outputdata;
+ unsigned int outputdatalength;
+ compat_uptr_t b_key;
+ compat_uptr_t n_modulus;
+};
+
+static long trans_modexpo32(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct compat_ica_rsa_modexpo __user *umex32 = compat_ptr(arg);
+ struct compat_ica_rsa_modexpo mex32;
+ struct ica_rsa_modexpo mex64;
+ long rc;
+
+ if (copy_from_user(&mex32, umex32, sizeof(mex32)))
+ return -EFAULT;
+ mex64.inputdata = compat_ptr(mex32.inputdata);
+ mex64.inputdatalength = mex32.inputdatalength;
+ mex64.outputdata = compat_ptr(mex32.outputdata);
+ mex64.outputdatalength = mex32.outputdatalength;
+ mex64.b_key = compat_ptr(mex32.b_key);
+ mex64.n_modulus = compat_ptr(mex32.n_modulus);
+ do {
+ rc = zcrypt_rsa_modexpo(&mex64);
+ } while (rc == -EAGAIN);
+ if (!rc)
+ rc = put_user(mex64.outputdatalength,
+ &umex32->outputdatalength);
+ return rc;
+}
+
+struct compat_ica_rsa_modexpo_crt {
+ compat_uptr_t inputdata;
+ unsigned int inputdatalength;
+ compat_uptr_t outputdata;
+ unsigned int outputdatalength;
+ compat_uptr_t bp_key;
+ compat_uptr_t bq_key;
+ compat_uptr_t np_prime;
+ compat_uptr_t nq_prime;
+ compat_uptr_t u_mult_inv;
+};
+
+static long trans_modexpo_crt32(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct compat_ica_rsa_modexpo_crt __user *ucrt32 = compat_ptr(arg);
+ struct compat_ica_rsa_modexpo_crt crt32;
+ struct ica_rsa_modexpo_crt crt64;
+ long rc;
+
+ if (copy_from_user(&crt32, ucrt32, sizeof(crt32)))
+ return -EFAULT;
+ crt64.inputdata = compat_ptr(crt32.inputdata);
+ crt64.inputdatalength = crt32.inputdatalength;
+ crt64.outputdata= compat_ptr(crt32.outputdata);
+ crt64.outputdatalength = crt32.outputdatalength;
+ crt64.bp_key = compat_ptr(crt32.bp_key);
+ crt64.bq_key = compat_ptr(crt32.bq_key);
+ crt64.np_prime = compat_ptr(crt32.np_prime);
+ crt64.nq_prime = compat_ptr(crt32.nq_prime);
+ crt64.u_mult_inv = compat_ptr(crt32.u_mult_inv);
+ do {
+ rc = zcrypt_rsa_crt(&crt64);
+ } while (rc == -EAGAIN);
+ if (!rc)
+ rc = put_user(crt64.outputdatalength,
+ &ucrt32->outputdatalength);
+ return rc;
+}
+
+struct compat_ica_xcRB {
+ unsigned short agent_ID;
+ unsigned int user_defined;
+ unsigned short request_ID;
+ unsigned int request_control_blk_length;
+ unsigned char padding1[16 - sizeof (compat_uptr_t)];
+ compat_uptr_t request_control_blk_addr;
+ unsigned int request_data_length;
+ char padding2[16 - sizeof (compat_uptr_t)];
+ compat_uptr_t request_data_address;
+ unsigned int reply_control_blk_length;
+ char padding3[16 - sizeof (compat_uptr_t)];
+ compat_uptr_t reply_control_blk_addr;
+ unsigned int reply_data_length;
+ char padding4[16 - sizeof (compat_uptr_t)];
+ compat_uptr_t reply_data_addr;
+ unsigned short priority_window;
+ unsigned int status;
+} __attribute__((packed));
+
+static long trans_xcRB32(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct compat_ica_xcRB __user *uxcRB32 = compat_ptr(arg);
+ struct compat_ica_xcRB xcRB32;
+ struct ica_xcRB xcRB64;
+ long rc;
+
+ if (copy_from_user(&xcRB32, uxcRB32, sizeof(xcRB32)))
+ return -EFAULT;
+ xcRB64.agent_ID = xcRB32.agent_ID;
+ xcRB64.user_defined = xcRB32.user_defined;
+ xcRB64.request_ID = xcRB32.request_ID;
+ xcRB64.request_control_blk_length =
+ xcRB32.request_control_blk_length;
+ xcRB64.request_control_blk_addr =
+ compat_ptr(xcRB32.request_control_blk_addr);
+ xcRB64.request_data_length =
+ xcRB32.request_data_length;
+ xcRB64.request_data_address =
+ compat_ptr(xcRB32.request_data_address);
+ xcRB64.reply_control_blk_length =
+ xcRB32.reply_control_blk_length;
+ xcRB64.reply_control_blk_addr =
+ compat_ptr(xcRB32.reply_control_blk_addr);
+ xcRB64.reply_data_length = xcRB32.reply_data_length;
+ xcRB64.reply_data_addr =
+ compat_ptr(xcRB32.reply_data_addr);
+ xcRB64.priority_window = xcRB32.priority_window;
+ xcRB64.status = xcRB32.status;
+ do {
+ rc = zcrypt_send_cprb(&xcRB64);
+ } while (rc == -EAGAIN);
+ xcRB32.reply_control_blk_length = xcRB64.reply_control_blk_length;
+ xcRB32.reply_data_length = xcRB64.reply_data_length;
+ xcRB32.status = xcRB64.status;
+ if (copy_to_user(uxcRB32, &xcRB32, sizeof(xcRB32)))
+ return -EFAULT;
+ return rc;
+}
+
+long zcrypt_compat_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ if (cmd == ICARSAMODEXPO)
+ return trans_modexpo32(filp, cmd, arg);
+ if (cmd == ICARSACRT)
+ return trans_modexpo_crt32(filp, cmd, arg);
+ if (cmd == ZSECSENDCPRB)
+ return trans_xcRB32(filp, cmd, arg);
+ return zcrypt_unlocked_ioctl(filp, cmd, arg);
+}
+#endif
+
+/**
+ * Misc device file operations.
+ */
+static struct file_operations zcrypt_fops = {
+ .owner = THIS_MODULE,
+ .read = zcrypt_read,
+ .write = zcrypt_write,
+ .unlocked_ioctl = zcrypt_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = zcrypt_compat_ioctl,
+#endif
+ .open = zcrypt_open,
+ .release = zcrypt_release
+};
+
+/**
+ * Misc device.
+ */
+static struct miscdevice zcrypt_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "z90crypt",
+ .fops = &zcrypt_fops,
+};
+
+/**
+ * Deprecated /proc entry support.
+ */
+static struct proc_dir_entry *zcrypt_entry;
+
+static inline int sprintcl(unsigned char *outaddr, unsigned char *addr,
+ unsigned int len)
+{
+ int hl, i;
+
+ hl = 0;
+ for (i = 0; i < len; i++)
+ hl += sprintf(outaddr+hl, "%01x", (unsigned int) addr[i]);
+ hl += sprintf(outaddr+hl, " ");
+ return hl;
+}
+
+static inline int sprintrw(unsigned char *outaddr, unsigned char *addr,
+ unsigned int len)
+{
+ int hl, inl, c, cx;
+
+ hl = sprintf(outaddr, " ");
+ inl = 0;
+ for (c = 0; c < (len / 16); c++) {
+ hl += sprintcl(outaddr+hl, addr+inl, 16);
+ inl += 16;
+ }
+ cx = len%16;
+ if (cx) {
+ hl += sprintcl(outaddr+hl, addr+inl, cx);
+ inl += cx;
+ }
+ hl += sprintf(outaddr+hl, "\n");
+ return hl;
+}
+
+static inline int sprinthx(unsigned char *title, unsigned char *outaddr,
+ unsigned char *addr, unsigned int len)
+{
+ int hl, inl, r, rx;
+
+ hl = sprintf(outaddr, "\n%s\n", title);
+ inl = 0;
+ for (r = 0; r < (len / 64); r++) {
+ hl += sprintrw(outaddr+hl, addr+inl, 64);
+ inl += 64;
+ }
+ rx = len % 64;
+ if (rx) {
+ hl += sprintrw(outaddr+hl, addr+inl, rx);
+ inl += rx;
+ }
+ hl += sprintf(outaddr+hl, "\n");
+ return hl;
+}
+
+static inline int sprinthx4(unsigned char *title, unsigned char *outaddr,
+ unsigned int *array, unsigned int len)
+{
+ int hl, r;
+
+ hl = sprintf(outaddr, "\n%s\n", title);
+ for (r = 0; r < len; r++) {
+ if ((r % 8) == 0)
+ hl += sprintf(outaddr+hl, " ");
+ hl += sprintf(outaddr+hl, "%08X ", array[r]);
+ if ((r % 8) == 7)
+ hl += sprintf(outaddr+hl, "\n");
+ }
+ hl += sprintf(outaddr+hl, "\n");
+ return hl;
+}
+
+static int zcrypt_status_read(char *resp_buff, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ unsigned char *workarea;
+ int len;
+
+ len = 0;
+
+ /* resp_buff is a page. Use the right half for a work area */
+ workarea = resp_buff + 2000;
+ len += sprintf(resp_buff + len, "\nzcrypt version: %d.%d.%d\n",
+ ZCRYPT_VERSION, ZCRYPT_RELEASE, ZCRYPT_VARIANT);
+ len += sprintf(resp_buff + len, "Cryptographic domain: %d\n",
+ ap_domain_index);
+ len += sprintf(resp_buff + len, "Total device count: %d\n",
+ zcrypt_device_count);
+ len += sprintf(resp_buff + len, "PCICA count: %d\n",
+ zcrypt_count_type(ZCRYPT_PCICA));
+ len += sprintf(resp_buff + len, "PCICC count: %d\n",
+ zcrypt_count_type(ZCRYPT_PCICC));
+ len += sprintf(resp_buff + len, "PCIXCC MCL2 count: %d\n",
+ zcrypt_count_type(ZCRYPT_PCIXCC_MCL2));
+ len += sprintf(resp_buff + len, "PCIXCC MCL3 count: %d\n",
+ zcrypt_count_type(ZCRYPT_PCIXCC_MCL3));
+ len += sprintf(resp_buff + len, "CEX2C count: %d\n",
+ zcrypt_count_type(ZCRYPT_CEX2C));
+ len += sprintf(resp_buff + len, "CEX2A count: %d\n",
+ zcrypt_count_type(ZCRYPT_CEX2A));
+ len += sprintf(resp_buff + len, "requestq count: %d\n",
+ zcrypt_requestq_count());
+ len += sprintf(resp_buff + len, "pendingq count: %d\n",
+ zcrypt_pendingq_count());
+ len += sprintf(resp_buff + len, "Total open handles: %d\n\n",
+ atomic_read(&zcrypt_open_count));
+ zcrypt_status_mask(workarea);
+ len += sprinthx("Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) "
+ "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A",
+ resp_buff+len, workarea, AP_DEVICES);
+ zcrypt_qdepth_mask(workarea);
+ len += sprinthx("Waiting work element counts",
+ resp_buff+len, workarea, AP_DEVICES);
+ zcrypt_perdev_reqcnt((unsigned int *) workarea);
+ len += sprinthx4("Per-device successfully completed request counts",
+ resp_buff+len,(unsigned int *) workarea, AP_DEVICES);
+ *eof = 1;
+ memset((void *) workarea, 0x00, AP_DEVICES * sizeof(unsigned int));
+ return len;
+}
+
+static void zcrypt_disable_card(int index)
+{
+ struct zcrypt_device *zdev;
+
+ spin_lock_bh(&zcrypt_device_lock);
+ list_for_each_entry(zdev, &zcrypt_device_list, list)
+ if (AP_QID_DEVICE(zdev->ap_dev->qid) == index) {
+ zdev->online = 0;
+ ap_flush_queue(zdev->ap_dev);
+ break;
+ }
+ spin_unlock_bh(&zcrypt_device_lock);
+}
+
+static void zcrypt_enable_card(int index)
+{
+ struct zcrypt_device *zdev;
+
+ spin_lock_bh(&zcrypt_device_lock);
+ list_for_each_entry(zdev, &zcrypt_device_list, list)
+ if (AP_QID_DEVICE(zdev->ap_dev->qid) == index) {
+ zdev->online = 1;
+ break;
+ }
+ spin_unlock_bh(&zcrypt_device_lock);
+}
+
+static int zcrypt_status_write(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ unsigned char *lbuf, *ptr;
+ unsigned long local_count;
+ int j;
+
+ if (count <= 0)
+ return 0;
+
+#define LBUFSIZE 1200UL
+ lbuf = kmalloc(LBUFSIZE, GFP_KERNEL);
+ if (!lbuf) {
+ PRINTK("kmalloc failed!\n");
+ return 0;
+ }
+
+ local_count = min(LBUFSIZE - 1, count);
+ if (copy_from_user(lbuf, buffer, local_count) != 0) {
+ kfree(lbuf);
+ return -EFAULT;
+ }
+ lbuf[local_count] = '\0';
+
+ ptr = strstr(lbuf, "Online devices");
+ if (!ptr) {
+ PRINTK("Unable to parse data (missing \"Online devices\")\n");
+ goto out;
+ }
+ ptr = strstr(ptr, "\n");
+ if (!ptr) {
+ PRINTK("Unable to parse data (missing newline "
+ "after \"Online devices\")\n");
+ goto out;
+ }
+ ptr++;
+
+ if (strstr(ptr, "Waiting work element counts") == NULL) {
+ PRINTK("Unable to parse data (missing "
+ "\"Waiting work element counts\")\n");
+ goto out;
+ }
+
+ for (j = 0; j < 64 && *ptr; ptr++) {
+ /**
+ * '0' for no device, '1' for PCICA, '2' for PCICC,
+ * '3' for PCIXCC_MCL2, '4' for PCIXCC_MCL3,
+ * '5' for CEX2C and '6' for CEX2A'
+ */
+ if (*ptr >= '0' && *ptr <= '6')
+ j++;
+ else if (*ptr == 'd' || *ptr == 'D')
+ zcrypt_disable_card(j++);
+ else if (*ptr == 'e' || *ptr == 'E')
+ zcrypt_enable_card(j++);
+ else if (*ptr != ' ' && *ptr != '\t')
+ break;
+ }
+out:
+ kfree(lbuf);
+ return count;
+}
+
+/**
+ * The module initialization code.
+ */
+int __init zcrypt_api_init(void)
+{
+ int rc;
+
+ /* 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);
+ 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;
+ }
+ zcrypt_entry->nlink = 1;
+ zcrypt_entry->data = NULL;
+ zcrypt_entry->read_proc = zcrypt_status_read;
+ zcrypt_entry->write_proc = zcrypt_status_write;
+
+ return 0;
+
+out_misc:
+ misc_deregister(&zcrypt_misc_device);
+out:
+ return rc;
+}
+
+/**
+ * The module termination code.
+ */
+void zcrypt_api_exit(void)
+{
+ remove_proc_entry("driver/z90crypt", NULL);
+ misc_deregister(&zcrypt_misc_device);
+}
+
+#ifndef CONFIG_ZCRYPT_MONOLITHIC
+module_init(zcrypt_api_init);
+module_exit(zcrypt_api_exit);
+#endif
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
new file mode 100644
index 000000000000..de4877ee618f
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -0,0 +1,141 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_api.h
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ * Cornelia Huck <cornelia.huck@de.ibm.com>
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *
+ * 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.
+ */
+
+#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, __FUNCTION__ , ## args)
+#define PRINTKN(fmt, args...) \
+ printk(KERN_DEBUG DEV_NAME ": " fmt, ## args)
+#define PRINTKW(fmt, args...) \
+ printk(KERN_WARNING DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
+#define PRINTKC(fmt, args...) \
+ printk(KERN_CRIT DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
+
+#ifdef ZCRYPT_DEBUG
+#define PDEBUG(fmt, args...) \
+ printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
+#else
+#define PDEBUG(fmt, args...) do {} while (0)
+#endif
+
+#include "ap_bus.h"
+#include <asm/zcrypt.h>
+
+/* deprecated status calls */
+#define ICAZ90STATUS _IOR(ZCRYPT_IOCTL_MAGIC, 0x10, struct ica_z90_status)
+#define Z90STAT_PCIXCCCOUNT _IOR(ZCRYPT_IOCTL_MAGIC, 0x43, int)
+
+/**
+ * This structure is deprecated and the corresponding ioctl() has been
+ * replaced with individual ioctl()s for each piece of data!
+ */
+struct ica_z90_status {
+ int totalcount;
+ int leedslitecount; // PCICA
+ int leeds2count; // PCICC
+ // int PCIXCCCount; is not in struct for backward compatibility
+ int requestqWaitCount;
+ int pendingqWaitCount;
+ int totalOpenCount;
+ int cryptoDomain;
+ // status: 0=not there, 1=PCICA, 2=PCICC, 3=PCIXCC_MCL2, 4=PCIXCC_MCL3,
+ // 5=CEX2C
+ unsigned char status[64];
+ // qdepth: # work elements waiting for each device
+ unsigned char qdepth[64];
+};
+
+/**
+ * device type for an actual device is either PCICA, PCICC, PCIXCC_MCL2,
+ * PCIXCC_MCL3, CEX2C, or CEX2A
+ *
+ * NOTE: PCIXCC_MCL3 refers to a PCIXCC with May 2004 version of Licensed
+ * Internal Code (LIC) (EC J12220 level 29).
+ * PCIXCC_MCL2 refers to any LIC before this level.
+ */
+#define ZCRYPT_PCICA 1
+#define ZCRYPT_PCICC 2
+#define ZCRYPT_PCIXCC_MCL2 3
+#define ZCRYPT_PCIXCC_MCL3 4
+#define ZCRYPT_CEX2C 5
+#define ZCRYPT_CEX2A 6
+
+struct zcrypt_device;
+
+struct zcrypt_ops {
+ long (*rsa_modexpo)(struct zcrypt_device *, struct ica_rsa_modexpo *);
+ long (*rsa_modexpo_crt)(struct zcrypt_device *,
+ struct ica_rsa_modexpo_crt *);
+ long (*send_cprb)(struct zcrypt_device *, struct ica_xcRB *);
+};
+
+struct zcrypt_device {
+ struct list_head list; /* Device list. */
+ spinlock_t lock; /* Per device lock. */
+ struct kref refcount; /* device refcounting */
+ struct ap_device *ap_dev; /* The "real" ap device. */
+ struct zcrypt_ops *ops; /* Crypto operations. */
+ int online; /* User online/offline */
+
+ int user_space_type; /* User space device id. */
+ char *type_string; /* User space device name. */
+ int min_mod_size; /* Min number of bits. */
+ int max_mod_size; /* Max number of bits. */
+ int short_crt; /* Card has crt length restriction. */
+ int speed_rating; /* Speed of the crypto device. */
+
+ int request_count; /* # current requests. */
+
+ struct ap_message reply; /* Per-device reply structure. */
+};
+
+struct zcrypt_device *zcrypt_device_alloc(size_t);
+void zcrypt_device_free(struct zcrypt_device *);
+void zcrypt_device_get(struct zcrypt_device *);
+int zcrypt_device_put(struct zcrypt_device *);
+int zcrypt_device_register(struct zcrypt_device *);
+void zcrypt_device_unregister(struct zcrypt_device *);
+int zcrypt_api_init(void);
+void zcrypt_api_exit(void);
+
+#endif /* _ZCRYPT_API_H_ */
diff --git a/drivers/s390/crypto/zcrypt_cca_key.h b/drivers/s390/crypto/zcrypt_cca_key.h
new file mode 100644
index 000000000000..8dbcf0eef3e5
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_cca_key.h
@@ -0,0 +1,350 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_cca_key.h
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *
+ * 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.
+ */
+
+#ifndef _ZCRYPT_CCA_KEY_H_
+#define _ZCRYPT_CCA_KEY_H_
+
+struct T6_keyBlock_hdr {
+ unsigned short blen;
+ unsigned short ulen;
+ unsigned short flags;
+};
+
+/**
+ * mapping for the cca private ME key token.
+ * Three parts of interest here: the header, the private section and
+ * the public section.
+ *
+ * mapping for the cca key token header
+ */
+struct cca_token_hdr {
+ unsigned char token_identifier;
+ unsigned char version;
+ unsigned short token_length;
+ unsigned char reserved[4];
+} __attribute__((packed));
+
+#define CCA_TKN_HDR_ID_EXT 0x1E
+
+/**
+ * mapping for the cca private ME section
+ */
+struct cca_private_ext_ME_sec {
+ unsigned char section_identifier;
+ unsigned char version;
+ unsigned short section_length;
+ unsigned char private_key_hash[20];
+ unsigned char reserved1[4];
+ unsigned char key_format;
+ unsigned char reserved2;
+ unsigned char key_name_hash[20];
+ unsigned char key_use_flags[4];
+ unsigned char reserved3[6];
+ unsigned char reserved4[24];
+ unsigned char confounder[24];
+ unsigned char exponent[128];
+ unsigned char modulus[128];
+} __attribute__((packed));
+
+#define CCA_PVT_USAGE_ALL 0x80
+
+/**
+ * mapping for the cca public section
+ * In a private key, the modulus doesn't appear in the public
+ * section. So, an arbitrary public exponent of 0x010001 will be
+ * used, for a section length of 0x0F always.
+ */
+struct cca_public_sec {
+ unsigned char section_identifier;
+ unsigned char version;
+ unsigned short section_length;
+ unsigned char reserved[2];
+ unsigned short exponent_len;
+ unsigned short modulus_bit_len;
+ unsigned short modulus_byte_len; /* In a private key, this is 0 */
+} __attribute__((packed));
+
+/**
+ * mapping for the cca private CRT key 'token'
+ * The first three parts (the only parts considered in this release)
+ * are: the header, the private section and the public section.
+ * The header and public section are the same as for the
+ * struct cca_private_ext_ME
+ *
+ * Following the structure are the quantities p, q, dp, dq, u, pad,
+ * and modulus, in that order, where pad_len is the modulo 8
+ * complement of the residue modulo 8 of the sum of
+ * (p_len + q_len + dp_len + dq_len + u_len).
+ */
+struct cca_pvt_ext_CRT_sec {
+ unsigned char section_identifier;
+ unsigned char version;
+ unsigned short section_length;
+ unsigned char private_key_hash[20];
+ unsigned char reserved1[4];
+ unsigned char key_format;
+ unsigned char reserved2;
+ unsigned char key_name_hash[20];
+ unsigned char key_use_flags[4];
+ unsigned short p_len;
+ unsigned short q_len;
+ unsigned short dp_len;
+ unsigned short dq_len;
+ unsigned short u_len;
+ unsigned short mod_len;
+ unsigned char reserved3[4];
+ unsigned short pad_len;
+ unsigned char reserved4[52];
+ unsigned char confounder[8];
+} __attribute__((packed));
+
+#define CCA_PVT_EXT_CRT_SEC_ID_PVT 0x08
+#define CCA_PVT_EXT_CRT_SEC_FMT_CL 0x40
+
+/**
+ * Set up private key fields of a type6 MEX message.
+ * Note that all numerics in the key token are big-endian,
+ * while the entries in the key block header are little-endian.
+ *
+ * @mex: pointer to user input data
+ * @p: pointer to memory area for the key
+ *
+ * Returns the size of the key area or -EFAULT
+ */
+static inline int zcrypt_type6_mex_key_de(struct ica_rsa_modexpo *mex,
+ void *p, int big_endian)
+{
+ static struct cca_token_hdr static_pvt_me_hdr = {
+ .token_identifier = 0x1E,
+ .token_length = 0x0183,
+ };
+ static struct cca_private_ext_ME_sec static_pvt_me_sec = {
+ .section_identifier = 0x02,
+ .section_length = 0x016C,
+ .key_use_flags = {0x80,0x00,0x00,0x00},
+ };
+ static struct cca_public_sec static_pub_me_sec = {
+ .section_identifier = 0x04,
+ .section_length = 0x000F,
+ .exponent_len = 0x0003,
+ };
+ static char pk_exponent[3] = { 0x01, 0x00, 0x01 };
+ struct {
+ struct T6_keyBlock_hdr t6_hdr;
+ struct cca_token_hdr pvtMeHdr;
+ struct cca_private_ext_ME_sec pvtMeSec;
+ struct cca_public_sec pubMeSec;
+ char exponent[3];
+ } __attribute__((packed)) *key = p;
+ unsigned char *temp;
+
+ memset(key, 0, sizeof(*key));
+
+ if (big_endian) {
+ key->t6_hdr.blen = cpu_to_be16(0x189);
+ key->t6_hdr.ulen = cpu_to_be16(0x189 - 2);
+ } else {
+ key->t6_hdr.blen = cpu_to_le16(0x189);
+ key->t6_hdr.ulen = cpu_to_le16(0x189 - 2);
+ }
+ key->pvtMeHdr = static_pvt_me_hdr;
+ key->pvtMeSec = static_pvt_me_sec;
+ key->pubMeSec = static_pub_me_sec;
+ /**
+ * In a private key, the modulus doesn't appear in the public
+ * section. So, an arbitrary public exponent of 0x010001 will be
+ * used.
+ */
+ memcpy(key->exponent, pk_exponent, 3);
+
+ /* key parameter block */
+ temp = key->pvtMeSec.exponent +
+ sizeof(key->pvtMeSec.exponent) - mex->inputdatalength;
+ if (copy_from_user(temp, mex->b_key, mex->inputdatalength))
+ return -EFAULT;
+
+ /* modulus */
+ temp = key->pvtMeSec.modulus +
+ sizeof(key->pvtMeSec.modulus) - mex->inputdatalength;
+ if (copy_from_user(temp, mex->n_modulus, mex->inputdatalength))
+ return -EFAULT;
+ key->pubMeSec.modulus_bit_len = 8 * mex->inputdatalength;
+ return sizeof(*key);
+}
+
+/**
+ * Set up private key fields of a type6 MEX message. The _pad variant
+ * strips leading zeroes from the b_key.
+ * Note that all numerics in the key token are big-endian,
+ * while the entries in the key block header are little-endian.
+ *
+ * @mex: pointer to user input data
+ * @p: pointer to memory area for the key
+ *
+ * Returns the size of the key area or -EFAULT
+ */
+static inline int zcrypt_type6_mex_key_en(struct ica_rsa_modexpo *mex,
+ void *p, int big_endian)
+{
+ static struct cca_token_hdr static_pub_hdr = {
+ .token_identifier = 0x1E,
+ };
+ static struct cca_public_sec static_pub_sec = {
+ .section_identifier = 0x04,
+ };
+ struct {
+ struct T6_keyBlock_hdr t6_hdr;
+ struct cca_token_hdr pubHdr;
+ struct cca_public_sec pubSec;
+ char exponent[0];
+ } __attribute__((packed)) *key = p;
+ unsigned char *temp;
+ int i;
+
+ memset(key, 0, sizeof(*key));
+
+ key->pubHdr = static_pub_hdr;
+ key->pubSec = static_pub_sec;
+
+ /* key parameter block */
+ temp = key->exponent;
+ if (copy_from_user(temp, mex->b_key, mex->inputdatalength))
+ return -EFAULT;
+ /* Strip leading zeroes from b_key. */
+ for (i = 0; i < mex->inputdatalength; i++)
+ if (temp[i])
+ break;
+ if (i >= mex->inputdatalength)
+ return -EINVAL;
+ memmove(temp, temp + i, mex->inputdatalength - i);
+ temp += mex->inputdatalength - i;
+ /* modulus */
+ if (copy_from_user(temp, mex->n_modulus, mex->inputdatalength))
+ return -EFAULT;
+
+ key->pubSec.modulus_bit_len = 8 * mex->inputdatalength;
+ key->pubSec.modulus_byte_len = mex->inputdatalength;
+ key->pubSec.exponent_len = mex->inputdatalength - i;
+ key->pubSec.section_length = sizeof(key->pubSec) +
+ 2*mex->inputdatalength - i;
+ key->pubHdr.token_length =
+ key->pubSec.section_length + sizeof(key->pubHdr);
+ if (big_endian) {
+ key->t6_hdr.ulen = cpu_to_be16(key->pubHdr.token_length + 4);
+ key->t6_hdr.blen = cpu_to_be16(key->pubHdr.token_length + 6);
+ } else {
+ key->t6_hdr.ulen = cpu_to_le16(key->pubHdr.token_length + 4);
+ key->t6_hdr.blen = cpu_to_le16(key->pubHdr.token_length + 6);
+ }
+ return sizeof(*key) + 2*mex->inputdatalength - i;
+}
+
+/**
+ * Set up private key fields of a type6 CRT message.
+ * Note that all numerics in the key token are big-endian,
+ * while the entries in the key block header are little-endian.
+ *
+ * @mex: pointer to user input data
+ * @p: pointer to memory area for the key
+ *
+ * Returns the size of the key area or -EFAULT
+ */
+static inline int zcrypt_type6_crt_key(struct ica_rsa_modexpo_crt *crt,
+ void *p, int big_endian)
+{
+ static struct cca_public_sec static_cca_pub_sec = {
+ .section_identifier = 4,
+ .section_length = 0x000f,
+ .exponent_len = 0x0003,
+ };
+ static char pk_exponent[3] = { 0x01, 0x00, 0x01 };
+ struct {
+ struct T6_keyBlock_hdr t6_hdr;
+ struct cca_token_hdr token;
+ struct cca_pvt_ext_CRT_sec pvt;
+ char key_parts[0];
+ } __attribute__((packed)) *key = p;
+ struct cca_public_sec *pub;
+ int short_len, long_len, pad_len, key_len, size;
+
+ memset(key, 0, sizeof(*key));
+
+ short_len = crt->inputdatalength / 2;
+ long_len = short_len + 8;
+ pad_len = -(3*long_len + 2*short_len) & 7;
+ key_len = 3*long_len + 2*short_len + pad_len + crt->inputdatalength;
+ size = sizeof(*key) + key_len + sizeof(*pub) + 3;
+
+ /* parameter block.key block */
+ if (big_endian) {
+ key->t6_hdr.blen = cpu_to_be16(size);
+ key->t6_hdr.ulen = cpu_to_be16(size - 2);
+ } else {
+ key->t6_hdr.blen = cpu_to_le16(size);
+ key->t6_hdr.ulen = cpu_to_le16(size - 2);
+ }
+
+ /* key token header */
+ key->token.token_identifier = CCA_TKN_HDR_ID_EXT;
+ key->token.token_length = size - 6;
+
+ /* private section */
+ key->pvt.section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT;
+ key->pvt.section_length = sizeof(key->pvt) + key_len;
+ key->pvt.key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL;
+ key->pvt.key_use_flags[0] = CCA_PVT_USAGE_ALL;
+ key->pvt.p_len = key->pvt.dp_len = key->pvt.u_len = long_len;
+ key->pvt.q_len = key->pvt.dq_len = short_len;
+ key->pvt.mod_len = crt->inputdatalength;
+ key->pvt.pad_len = pad_len;
+
+ /* key parts */
+ if (copy_from_user(key->key_parts, crt->np_prime, long_len) ||
+ copy_from_user(key->key_parts + long_len,
+ crt->nq_prime, short_len) ||
+ copy_from_user(key->key_parts + long_len + short_len,
+ crt->bp_key, long_len) ||
+ copy_from_user(key->key_parts + 2*long_len + short_len,
+ crt->bq_key, short_len) ||
+ copy_from_user(key->key_parts + 2*long_len + 2*short_len,
+ crt->u_mult_inv, long_len))
+ return -EFAULT;
+ memset(key->key_parts + 3*long_len + 2*short_len + pad_len,
+ 0xff, crt->inputdatalength);
+ pub = (struct cca_public_sec *)(key->key_parts + key_len);
+ *pub = static_cca_pub_sec;
+ pub->modulus_bit_len = 8 * crt->inputdatalength;
+ /**
+ * In a private key, the modulus doesn't appear in the public
+ * section. So, an arbitrary public exponent of 0x010001 will be
+ * used.
+ */
+ memcpy((char *) (pub + 1), pk_exponent, 3);
+ return size;
+}
+
+#endif /* _ZCRYPT_CCA_KEY_H_ */
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
new file mode 100644
index 000000000000..a62b00083d0c
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -0,0 +1,435 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_cex2a.c
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *
+ * 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/err.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_error.h"
+#include "zcrypt_cex2a.h"
+
+#define CEX2A_MIN_MOD_SIZE 1 /* 8 bits */
+#define CEX2A_MAX_MOD_SIZE 256 /* 2048 bits */
+
+#define CEX2A_SPEED_RATING 970
+
+#define CEX2A_MAX_MESSAGE_SIZE 0x390 /* sizeof(struct type50_crb2_msg) */
+#define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */
+
+#define CEX2A_CLEANUP_TIME (15*HZ)
+
+static struct ap_device_id zcrypt_cex2a_ids[] = {
+ { AP_DEVICE(AP_DEVICE_TYPE_CEX2A) },
+ { /* end of list */ },
+};
+
+#ifndef CONFIG_ZCRYPT_MONOLITHIC
+MODULE_DEVICE_TABLE(ap, zcrypt_cex2a_ids);
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("CEX2A Cryptographic Coprocessor device driver, "
+ "Copyright 2001, 2006 IBM Corporation");
+MODULE_LICENSE("GPL");
+#endif
+
+static int zcrypt_cex2a_probe(struct ap_device *ap_dev);
+static void zcrypt_cex2a_remove(struct ap_device *ap_dev);
+static void zcrypt_cex2a_receive(struct ap_device *, struct ap_message *,
+ struct ap_message *);
+
+static struct ap_driver zcrypt_cex2a_driver = {
+ .probe = zcrypt_cex2a_probe,
+ .remove = zcrypt_cex2a_remove,
+ .receive = zcrypt_cex2a_receive,
+ .ids = zcrypt_cex2a_ids,
+};
+
+/**
+ * Convert a ICAMEX message to a type50 MEX message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @mex: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo *mex)
+{
+ unsigned char *mod, *exp, *inp;
+ int mod_len;
+
+ mod_len = mex->inputdatalength;
+
+ if (mod_len <= 128) {
+ struct type50_meb1_msg *meb1 = ap_msg->message;
+ memset(meb1, 0, sizeof(*meb1));
+ ap_msg->length = sizeof(*meb1);
+ meb1->header.msg_type_code = TYPE50_TYPE_CODE;
+ meb1->header.msg_len = sizeof(*meb1);
+ meb1->keyblock_type = TYPE50_MEB1_FMT;
+ mod = meb1->modulus + sizeof(meb1->modulus) - mod_len;
+ exp = meb1->exponent + sizeof(meb1->exponent) - mod_len;
+ inp = meb1->message + sizeof(meb1->message) - mod_len;
+ } else {
+ struct type50_meb2_msg *meb2 = ap_msg->message;
+ memset(meb2, 0, sizeof(*meb2));
+ ap_msg->length = sizeof(*meb2);
+ meb2->header.msg_type_code = TYPE50_TYPE_CODE;
+ meb2->header.msg_len = sizeof(*meb2);
+ meb2->keyblock_type = TYPE50_MEB2_FMT;
+ mod = meb2->modulus + sizeof(meb2->modulus) - mod_len;
+ exp = meb2->exponent + sizeof(meb2->exponent) - mod_len;
+ inp = meb2->message + sizeof(meb2->message) - mod_len;
+ }
+
+ if (copy_from_user(mod, mex->n_modulus, mod_len) ||
+ copy_from_user(exp, mex->b_key, mod_len) ||
+ copy_from_user(inp, mex->inputdata, mod_len))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Convert a ICACRT message to a type50 CRT message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @crt: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ int mod_len, short_len, long_len, long_offset;
+ unsigned char *p, *q, *dp, *dq, *u, *inp;
+
+ mod_len = crt->inputdatalength;
+ short_len = mod_len / 2;
+ long_len = mod_len / 2 + 8;
+
+ /*
+ * CEX2A cannot handle p, dp, or U > 128 bytes.
+ * If we have one of these, we need to do extra checking.
+ */
+ if (long_len > 128) {
+ /*
+ * zcrypt_rsa_crt already checked for the leading
+ * zeroes of np_prime, bp_key and u_mult_inc.
+ */
+ long_offset = long_len - 128;
+ long_len = 128;
+ } else
+ long_offset = 0;
+
+ /*
+ * Instead of doing extra work for p, dp, U > 64 bytes, we'll just use
+ * the larger message structure.
+ */
+ if (long_len <= 64) {
+ struct type50_crb1_msg *crb1 = ap_msg->message;
+ memset(crb1, 0, sizeof(*crb1));
+ ap_msg->length = sizeof(*crb1);
+ crb1->header.msg_type_code = TYPE50_TYPE_CODE;
+ crb1->header.msg_len = sizeof(*crb1);
+ crb1->keyblock_type = TYPE50_CRB1_FMT;
+ p = crb1->p + sizeof(crb1->p) - long_len;
+ q = crb1->q + sizeof(crb1->q) - short_len;
+ dp = crb1->dp + sizeof(crb1->dp) - long_len;
+ dq = crb1->dq + sizeof(crb1->dq) - short_len;
+ u = crb1->u + sizeof(crb1->u) - long_len;
+ inp = crb1->message + sizeof(crb1->message) - mod_len;
+ } else {
+ struct type50_crb2_msg *crb2 = ap_msg->message;
+ memset(crb2, 0, sizeof(*crb2));
+ ap_msg->length = sizeof(*crb2);
+ crb2->header.msg_type_code = TYPE50_TYPE_CODE;
+ crb2->header.msg_len = sizeof(*crb2);
+ crb2->keyblock_type = TYPE50_CRB2_FMT;
+ p = crb2->p + sizeof(crb2->p) - long_len;
+ q = crb2->q + sizeof(crb2->q) - short_len;
+ dp = crb2->dp + sizeof(crb2->dp) - long_len;
+ dq = crb2->dq + sizeof(crb2->dq) - short_len;
+ u = crb2->u + sizeof(crb2->u) - long_len;
+ inp = crb2->message + sizeof(crb2->message) - mod_len;
+ }
+
+ if (copy_from_user(p, crt->np_prime + long_offset, long_len) ||
+ copy_from_user(q, crt->nq_prime, short_len) ||
+ copy_from_user(dp, crt->bp_key + long_offset, long_len) ||
+ copy_from_user(dq, crt->bq_key, short_len) ||
+ copy_from_user(u, crt->u_mult_inv + long_offset, long_len) ||
+ copy_from_user(inp, crt->inputdata, mod_len))
+ return -EFAULT;
+
+
+ return 0;
+}
+
+/**
+ * Copy results from a type 80 reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @data: pointer to user output data
+ * @length: size of user output data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int convert_type80(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ struct type80_hdr *t80h = reply->message;
+ unsigned char *data;
+
+ if (t80h->len < sizeof(*t80h) + outputdatalength) {
+ /* The result is too short, the CEX2A card may not do that.. */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+ BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE);
+ data = reply->message + t80h->len - outputdatalength;
+ if (copy_to_user(outputdata, data, outputdatalength))
+ return -EFAULT;
+ return 0;
+}
+
+static int convert_response(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ /* Response type byte is the second byte in the response. */
+ switch (((unsigned char *) reply->message)[1]) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ return convert_error(zdev, reply);
+ case TYPE80_RSP_CODE:
+ 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. */
+ }
+}
+
+/**
+ * This function is called from the AP bus code after a crypto request
+ * "msg" has finished with the reply message "reply".
+ * It is called from tasklet context.
+ * @ap_dev: pointer to the AP device
+ * @msg: pointer to the AP message
+ * @reply: pointer to the AP reply message
+ */
+static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
+ struct ap_message *msg,
+ struct ap_message *reply)
+{
+ static struct error_hdr error_reply = {
+ .type = TYPE82_RSP_CODE,
+ .reply_code = REP82_ERROR_MACHINE_FAILURE,
+ };
+ struct type80_hdr *t80h = reply->message;
+ int length;
+
+ /* Copy the reply message to the request message buffer. */
+ if (IS_ERR(reply))
+ memcpy(msg->message, &error_reply, sizeof(error_reply));
+ else if (t80h->type == TYPE80_RSP_CODE) {
+ length = min(CEX2A_MAX_RESPONSE_SIZE, (int) t80h->len);
+ memcpy(msg->message, reply->message, length);
+ } else
+ memcpy(msg->message, reply->message, sizeof error_reply);
+ complete((struct completion *) msg->private);
+}
+
+static atomic_t zcrypt_step = ATOMIC_INIT(0);
+
+/**
+ * The request distributor calls this function if it picked the CEX2A
+ * device to handle a modexpo request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * CEX2A device to the request distributor
+ * @mex: pointer to the modexpo request buffer
+ */
+static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo *mex)
+{
+ struct ap_message ap_msg;
+ struct completion work;
+ int rc;
+
+ ap_msg.message = (void *) kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &work;
+ rc = ICAMEX_msg_to_type50MEX_msg(zdev, &ap_msg, mex);
+ if (rc)
+ goto out_free;
+ init_completion(&work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible_timeout(
+ &work, CEX2A_CLEANUP_TIME);
+ if (rc > 0)
+ rc = convert_response(zdev, &ap_msg, mex->outputdata,
+ mex->outputdatalength);
+ else {
+ /* Signal pending or message timed out. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+ if (rc == 0)
+ /* Message timed out. */
+ rc = -ETIME;
+ }
+out_free:
+ kfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the CEX2A
+ * device to handle a modexpo_crt request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * CEX2A device to the request distributor
+ * @crt: pointer to the modexpoc_crt request buffer
+ */
+static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ struct ap_message ap_msg;
+ struct completion work;
+ int rc;
+
+ ap_msg.message = (void *) kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &work;
+ rc = ICACRT_msg_to_type50CRT_msg(zdev, &ap_msg, crt);
+ if (rc)
+ goto out_free;
+ init_completion(&work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible_timeout(
+ &work, CEX2A_CLEANUP_TIME);
+ if (rc > 0)
+ rc = convert_response(zdev, &ap_msg, crt->outputdata,
+ crt->outputdatalength);
+ else {
+ /* Signal pending or message timed out. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+ if (rc == 0)
+ /* Message timed out. */
+ rc = -ETIME;
+ }
+out_free:
+ kfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The crypto operations for a CEX2A card.
+ */
+static struct zcrypt_ops zcrypt_cex2a_ops = {
+ .rsa_modexpo = zcrypt_cex2a_modexpo,
+ .rsa_modexpo_crt = zcrypt_cex2a_modexpo_crt,
+};
+
+/**
+ * Probe function for CEX2A cards. It always accepts the AP device
+ * since the bus_match already checked the hardware type.
+ * @ap_dev: pointer to the AP device.
+ */
+static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev;
+ int rc;
+
+ zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE);
+ if (!zdev)
+ return -ENOMEM;
+ zdev->ap_dev = ap_dev;
+ zdev->ops = &zcrypt_cex2a_ops;
+ zdev->online = 1;
+ zdev->user_space_type = ZCRYPT_CEX2A;
+ zdev->type_string = "CEX2A";
+ zdev->min_mod_size = CEX2A_MIN_MOD_SIZE;
+ zdev->max_mod_size = CEX2A_MAX_MOD_SIZE;
+ zdev->short_crt = 1;
+ zdev->speed_rating = CEX2A_SPEED_RATING;
+ ap_dev->reply = &zdev->reply;
+ ap_dev->private = zdev;
+ rc = zcrypt_device_register(zdev);
+ if (rc)
+ goto out_free;
+ return 0;
+
+out_free:
+ ap_dev->private = NULL;
+ zcrypt_device_free(zdev);
+ return rc;
+}
+
+/**
+ * This is called to remove the extended CEX2A driver information
+ * if an AP device is removed.
+ */
+static void zcrypt_cex2a_remove(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev = ap_dev->private;
+
+ zcrypt_device_unregister(zdev);
+}
+
+int __init zcrypt_cex2a_init(void)
+{
+ return ap_driver_register(&zcrypt_cex2a_driver, THIS_MODULE, "cex2a");
+}
+
+void __exit zcrypt_cex2a_exit(void)
+{
+ ap_driver_unregister(&zcrypt_cex2a_driver);
+}
+
+#ifndef CONFIG_ZCRYPT_MONOLITHIC
+module_init(zcrypt_cex2a_init);
+module_exit(zcrypt_cex2a_exit);
+#endif
diff --git a/drivers/s390/crypto/zcrypt_cex2a.h b/drivers/s390/crypto/zcrypt_cex2a.h
new file mode 100644
index 000000000000..8f69d1dacab8
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_cex2a.h
@@ -0,0 +1,126 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_cex2a.h
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *
+ * 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.
+ */
+
+#ifndef _ZCRYPT_CEX2A_H_
+#define _ZCRYPT_CEX2A_H_
+
+/**
+ * The type 50 message family is associated with a CEX2A card.
+ *
+ * The four members of the family are described below.
+ *
+ * Note that all unsigned char arrays are right-justified and left-padded
+ * with zeroes.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type50_hdr {
+ unsigned char reserved1;
+ unsigned char msg_type_code; /* 0x50 */
+ unsigned short msg_len;
+ unsigned char reserved2;
+ unsigned char ignored;
+ unsigned short reserved3;
+} __attribute__((packed));
+
+#define TYPE50_TYPE_CODE 0x50
+
+#define TYPE50_MEB1_FMT 0x0001
+#define TYPE50_MEB2_FMT 0x0002
+#define TYPE50_CRB1_FMT 0x0011
+#define TYPE50_CRB2_FMT 0x0012
+
+/* Mod-Exp, with a small modulus */
+struct type50_meb1_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0001 */
+ unsigned char reserved[6];
+ unsigned char exponent[128];
+ unsigned char modulus[128];
+ unsigned char message[128];
+} __attribute__((packed));
+
+/* Mod-Exp, with a large modulus */
+struct type50_meb2_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0002 */
+ unsigned char reserved[6];
+ unsigned char exponent[256];
+ unsigned char modulus[256];
+ unsigned char message[256];
+} __attribute__((packed));
+
+/* CRT, with a small modulus */
+struct type50_crb1_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0011 */
+ unsigned char reserved[6];
+ unsigned char p[64];
+ unsigned char q[64];
+ unsigned char dp[64];
+ unsigned char dq[64];
+ unsigned char u[64];
+ unsigned char message[128];
+} __attribute__((packed));
+
+/* CRT, with a large modulus */
+struct type50_crb2_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0012 */
+ unsigned char reserved[6];
+ unsigned char p[128];
+ unsigned char q[128];
+ unsigned char dp[128];
+ unsigned char dq[128];
+ unsigned char u[128];
+ unsigned char message[256];
+} __attribute__((packed));
+
+/**
+ * The type 80 response family is associated with a CEX2A card.
+ *
+ * Note that all unsigned char arrays are right-justified and left-padded
+ * with zeroes.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+
+#define TYPE80_RSP_CODE 0x80
+
+struct type80_hdr {
+ unsigned char reserved1;
+ unsigned char type; /* 0x80 */
+ unsigned short len;
+ unsigned char code; /* 0x00 */
+ unsigned char reserved2[3];
+ unsigned char reserved3[8];
+} __attribute__((packed));
+
+int zcrypt_cex2a_init(void);
+void zcrypt_cex2a_exit(void);
+
+#endif /* _ZCRYPT_CEX2A_H_ */
diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h
new file mode 100644
index 000000000000..2cb616ba8bec
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_error.h
@@ -0,0 +1,133 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_error.h
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *
+ * 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.
+ */
+
+#ifndef _ZCRYPT_ERROR_H_
+#define _ZCRYPT_ERROR_H_
+
+#include "zcrypt_api.h"
+
+/**
+ * Reply Messages
+ *
+ * Error reply messages are of two types:
+ * 82: Error (see below)
+ * 88: Error (see below)
+ * Both type 82 and type 88 have the same structure in the header.
+ *
+ * Request reply messages are of three known types:
+ * 80: Reply from a Type 50 Request (see CEX2A-RELATED STRUCTS)
+ * 84: Reply from a Type 4 Request (see PCICA-RELATED STRUCTS)
+ * 86: Reply from a Type 6 Request (see PCICC/PCIXCC/CEX2C-RELATED STRUCTS)
+ *
+ */
+struct error_hdr {
+ unsigned char reserved1; /* 0x00 */
+ unsigned char type; /* 0x82 or 0x88 */
+ unsigned char reserved2[2]; /* 0x0000 */
+ unsigned char reply_code; /* reply code */
+ unsigned char reserved3[3]; /* 0x000000 */
+};
+
+#define TYPE82_RSP_CODE 0x82
+#define TYPE88_RSP_CODE 0x88
+
+#define REP82_ERROR_MACHINE_FAILURE 0x10
+#define REP82_ERROR_PREEMPT_FAILURE 0x12
+#define REP82_ERROR_CHECKPT_FAILURE 0x14
+#define REP82_ERROR_MESSAGE_TYPE 0x20
+#define REP82_ERROR_INVALID_COMM_CD 0x21 /* Type 84 */
+#define REP82_ERROR_INVALID_MSG_LEN 0x23
+#define REP82_ERROR_RESERVD_FIELD 0x24 /* was 0x50 */
+#define REP82_ERROR_FORMAT_FIELD 0x29
+#define REP82_ERROR_INVALID_COMMAND 0x30
+#define REP82_ERROR_MALFORMED_MSG 0x40
+#define REP82_ERROR_RESERVED_FIELDO 0x50 /* old value */
+#define REP82_ERROR_WORD_ALIGNMENT 0x60
+#define REP82_ERROR_MESSAGE_LENGTH 0x80
+#define REP82_ERROR_OPERAND_INVALID 0x82
+#define REP82_ERROR_OPERAND_SIZE 0x84
+#define REP82_ERROR_EVEN_MOD_IN_OPND 0x85
+#define REP82_ERROR_RESERVED_FIELD 0x88
+#define REP82_ERROR_TRANSPORT_FAIL 0x90
+#define REP82_ERROR_PACKET_TRUNCATED 0xA0
+#define REP82_ERROR_ZERO_BUFFER_LEN 0xB0
+
+#define REP88_ERROR_MODULE_FAILURE 0x10
+
+#define REP88_ERROR_MESSAGE_TYPE 0x20
+#define REP88_ERROR_MESSAGE_MALFORMD 0x22
+#define REP88_ERROR_MESSAGE_LENGTH 0x23
+#define REP88_ERROR_RESERVED_FIELD 0x24
+#define REP88_ERROR_KEY_TYPE 0x34
+#define REP88_ERROR_INVALID_KEY 0x82 /* CEX2A */
+#define REP88_ERROR_OPERAND 0x84 /* CEX2A */
+#define REP88_ERROR_OPERAND_EVEN_MOD 0x85 /* CEX2A */
+
+static inline int convert_error(struct zcrypt_device *zdev,
+ struct ap_message *reply)
+{
+ 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:
+ case REP82_ERROR_EVEN_MOD_IN_OPND:
+ case REP88_ERROR_MESSAGE_MALFORMD:
+ // REP88_ERROR_INVALID_KEY // '82' CEX2A
+ // REP88_ERROR_OPERAND // '84' CEX2A
+ // REP88_ERROR_OPERAND_EVEN_MOD // '85' CEX2A
+ /* Invalid input data. */
+ return -EINVAL;
+ case REP82_ERROR_MESSAGE_TYPE:
+ // REP88_ERROR_MESSAGE_TYPE // '20' CEX2A
+ /**
+ * To sent a message of the wrong type is a bug in the
+ * device driver. Warn about it, disable the device
+ * and then repeat the request.
+ */
+ WARN_ON(1);
+ zdev->online = 0;
+ return -EAGAIN;
+ case REP82_ERROR_TRANSPORT_FAIL:
+ case REP82_ERROR_MACHINE_FAILURE:
+ // REP88_ERROR_MODULE_FAILURE // '10' CEX2A
+ /* If a card fails disable it and repeat the request. */
+ 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. */
+ }
+}
+
+#endif /* _ZCRYPT_ERROR_H_ */
diff --git a/drivers/s390/crypto/zcrypt_mono.c b/drivers/s390/crypto/zcrypt_mono.c
new file mode 100644
index 000000000000..2a9349ad68b7
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_mono.c
@@ -0,0 +1,100 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_mono.c
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *
+ * 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/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/compat.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_pcica.h"
+#include "zcrypt_pcicc.h"
+#include "zcrypt_pcixcc.h"
+#include "zcrypt_cex2a.h"
+
+/**
+ * The module initialization code.
+ */
+int __init zcrypt_init(void)
+{
+ int rc;
+
+ rc = ap_module_init();
+ if (rc)
+ goto out;
+ rc = zcrypt_api_init();
+ if (rc)
+ goto out_ap;
+ rc = zcrypt_pcica_init();
+ if (rc)
+ goto out_api;
+ rc = zcrypt_pcicc_init();
+ if (rc)
+ goto out_pcica;
+ rc = zcrypt_pcixcc_init();
+ if (rc)
+ goto out_pcicc;
+ rc = zcrypt_cex2a_init();
+ if (rc)
+ goto out_pcixcc;
+ return 0;
+
+out_pcixcc:
+ zcrypt_pcixcc_exit();
+out_pcicc:
+ zcrypt_pcicc_exit();
+out_pcica:
+ zcrypt_pcica_exit();
+out_api:
+ zcrypt_api_exit();
+out_ap:
+ ap_module_exit();
+out:
+ return rc;
+}
+
+/**
+ * The module termination code.
+ */
+void __exit zcrypt_exit(void)
+{
+ zcrypt_cex2a_exit();
+ zcrypt_pcixcc_exit();
+ zcrypt_pcicc_exit();
+ zcrypt_pcica_exit();
+ zcrypt_api_exit();
+ ap_module_exit();
+}
+
+module_init(zcrypt_init);
+module_exit(zcrypt_exit);
diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c
new file mode 100644
index 000000000000..b6a4ecdc8025
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_pcica.c
@@ -0,0 +1,418 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_pcica.c
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *
+ * 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/err.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_error.h"
+#include "zcrypt_pcica.h"
+
+#define PCICA_MIN_MOD_SIZE 1 /* 8 bits */
+#define PCICA_MAX_MOD_SIZE 256 /* 2048 bits */
+
+#define PCICA_SPEED_RATING 2800
+
+#define PCICA_MAX_MESSAGE_SIZE 0x3a0 /* sizeof(struct type4_lcr) */
+#define PCICA_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */
+
+#define PCICA_CLEANUP_TIME (15*HZ)
+
+static struct ap_device_id zcrypt_pcica_ids[] = {
+ { AP_DEVICE(AP_DEVICE_TYPE_PCICA) },
+ { /* end of list */ },
+};
+
+#ifndef CONFIG_ZCRYPT_MONOLITHIC
+MODULE_DEVICE_TABLE(ap, zcrypt_pcica_ids);
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("PCICA Cryptographic Coprocessor device driver, "
+ "Copyright 2001, 2006 IBM Corporation");
+MODULE_LICENSE("GPL");
+#endif
+
+static int zcrypt_pcica_probe(struct ap_device *ap_dev);
+static void zcrypt_pcica_remove(struct ap_device *ap_dev);
+static void zcrypt_pcica_receive(struct ap_device *, struct ap_message *,
+ struct ap_message *);
+
+static struct ap_driver zcrypt_pcica_driver = {
+ .probe = zcrypt_pcica_probe,
+ .remove = zcrypt_pcica_remove,
+ .receive = zcrypt_pcica_receive,
+ .ids = zcrypt_pcica_ids,
+};
+
+/**
+ * Convert a ICAMEX message to a type4 MEX message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @mex: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICAMEX_msg_to_type4MEX_msg(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo *mex)
+{
+ unsigned char *modulus, *exponent, *message;
+ int mod_len;
+
+ mod_len = mex->inputdatalength;
+
+ if (mod_len <= 128) {
+ struct type4_sme *sme = ap_msg->message;
+ memset(sme, 0, sizeof(*sme));
+ ap_msg->length = sizeof(*sme);
+ sme->header.msg_fmt = TYPE4_SME_FMT;
+ sme->header.msg_len = sizeof(*sme);
+ sme->header.msg_type_code = TYPE4_TYPE_CODE;
+ sme->header.request_code = TYPE4_REQU_CODE;
+ modulus = sme->modulus + sizeof(sme->modulus) - mod_len;
+ exponent = sme->exponent + sizeof(sme->exponent) - mod_len;
+ message = sme->message + sizeof(sme->message) - mod_len;
+ } else {
+ struct type4_lme *lme = ap_msg->message;
+ memset(lme, 0, sizeof(*lme));
+ ap_msg->length = sizeof(*lme);
+ lme->header.msg_fmt = TYPE4_LME_FMT;
+ lme->header.msg_len = sizeof(*lme);
+ lme->header.msg_type_code = TYPE4_TYPE_CODE;
+ lme->header.request_code = TYPE4_REQU_CODE;
+ modulus = lme->modulus + sizeof(lme->modulus) - mod_len;
+ exponent = lme->exponent + sizeof(lme->exponent) - mod_len;
+ message = lme->message + sizeof(lme->message) - mod_len;
+ }
+
+ if (copy_from_user(modulus, mex->n_modulus, mod_len) ||
+ copy_from_user(exponent, mex->b_key, mod_len) ||
+ copy_from_user(message, mex->inputdata, mod_len))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Convert a ICACRT message to a type4 CRT message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @crt: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICACRT_msg_to_type4CRT_msg(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ unsigned char *p, *q, *dp, *dq, *u, *inp;
+ int mod_len, short_len, long_len;
+
+ mod_len = crt->inputdatalength;
+ short_len = mod_len / 2;
+ long_len = mod_len / 2 + 8;
+
+ if (mod_len <= 128) {
+ struct type4_scr *scr = ap_msg->message;
+ memset(scr, 0, sizeof(*scr));
+ ap_msg->length = sizeof(*scr);
+ scr->header.msg_type_code = TYPE4_TYPE_CODE;
+ scr->header.request_code = TYPE4_REQU_CODE;
+ scr->header.msg_fmt = TYPE4_SCR_FMT;
+ scr->header.msg_len = sizeof(*scr);
+ p = scr->p + sizeof(scr->p) - long_len;
+ q = scr->q + sizeof(scr->q) - short_len;
+ dp = scr->dp + sizeof(scr->dp) - long_len;
+ dq = scr->dq + sizeof(scr->dq) - short_len;
+ u = scr->u + sizeof(scr->u) - long_len;
+ inp = scr->message + sizeof(scr->message) - mod_len;
+ } else {
+ struct type4_lcr *lcr = ap_msg->message;
+ memset(lcr, 0, sizeof(*lcr));
+ ap_msg->length = sizeof(*lcr);
+ lcr->header.msg_type_code = TYPE4_TYPE_CODE;
+ lcr->header.request_code = TYPE4_REQU_CODE;
+ lcr->header.msg_fmt = TYPE4_LCR_FMT;
+ lcr->header.msg_len = sizeof(*lcr);
+ p = lcr->p + sizeof(lcr->p) - long_len;
+ q = lcr->q + sizeof(lcr->q) - short_len;
+ dp = lcr->dp + sizeof(lcr->dp) - long_len;
+ dq = lcr->dq + sizeof(lcr->dq) - short_len;
+ u = lcr->u + sizeof(lcr->u) - long_len;
+ inp = lcr->message + sizeof(lcr->message) - mod_len;
+ }
+
+ if (copy_from_user(p, crt->np_prime, long_len) ||
+ copy_from_user(q, crt->nq_prime, short_len) ||
+ copy_from_user(dp, crt->bp_key, long_len) ||
+ copy_from_user(dq, crt->bq_key, short_len) ||
+ copy_from_user(u, crt->u_mult_inv, long_len) ||
+ copy_from_user(inp, crt->inputdata, mod_len))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Copy results from a type 84 reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @data: pointer to user output data
+ * @length: size of user output data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static inline int convert_type84(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ struct type84_hdr *t84h = reply->message;
+ char *data;
+
+ if (t84h->len < sizeof(*t84h) + outputdatalength) {
+ /* The result is too short, the PCICA card may not do that.. */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+ BUG_ON(t84h->len > PCICA_MAX_RESPONSE_SIZE);
+ data = reply->message + t84h->len - outputdatalength;
+ if (copy_to_user(outputdata, data, outputdatalength))
+ return -EFAULT;
+ return 0;
+}
+
+static int convert_response(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ /* Response type byte is the second byte in the response. */
+ switch (((unsigned char *) reply->message)[1]) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ return convert_error(zdev, reply);
+ case TYPE84_RSP_CODE:
+ 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. */
+ }
+}
+
+/**
+ * This function is called from the AP bus code after a crypto request
+ * "msg" has finished with the reply message "reply".
+ * It is called from tasklet context.
+ * @ap_dev: pointer to the AP device
+ * @msg: pointer to the AP message
+ * @reply: pointer to the AP reply message
+ */
+static void zcrypt_pcica_receive(struct ap_device *ap_dev,
+ struct ap_message *msg,
+ struct ap_message *reply)
+{
+ static struct error_hdr error_reply = {
+ .type = TYPE82_RSP_CODE,
+ .reply_code = REP82_ERROR_MACHINE_FAILURE,
+ };
+ struct type84_hdr *t84h = reply->message;
+ int length;
+
+ /* Copy the reply message to the request message buffer. */
+ if (IS_ERR(reply))
+ memcpy(msg->message, &error_reply, sizeof(error_reply));
+ else if (t84h->code == TYPE84_RSP_CODE) {
+ length = min(PCICA_MAX_RESPONSE_SIZE, (int) t84h->len);
+ memcpy(msg->message, reply->message, length);
+ } else
+ memcpy(msg->message, reply->message, sizeof error_reply);
+ complete((struct completion *) msg->private);
+}
+
+static atomic_t zcrypt_step = ATOMIC_INIT(0);
+
+/**
+ * The request distributor calls this function if it picked the PCICA
+ * device to handle a modexpo request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCICA device to the request distributor
+ * @mex: pointer to the modexpo request buffer
+ */
+static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo *mex)
+{
+ struct ap_message ap_msg;
+ struct completion work;
+ int rc;
+
+ ap_msg.message = (void *) kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &work;
+ rc = ICAMEX_msg_to_type4MEX_msg(zdev, &ap_msg, mex);
+ if (rc)
+ goto out_free;
+ init_completion(&work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible_timeout(
+ &work, PCICA_CLEANUP_TIME);
+ if (rc > 0)
+ rc = convert_response(zdev, &ap_msg, mex->outputdata,
+ mex->outputdatalength);
+ else {
+ /* Signal pending or message timed out. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+ if (rc == 0)
+ /* Message timed out. */
+ rc = -ETIME;
+ }
+out_free:
+ kfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCICA
+ * device to handle a modexpo_crt request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCICA device to the request distributor
+ * @crt: pointer to the modexpoc_crt request buffer
+ */
+static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ struct ap_message ap_msg;
+ struct completion work;
+ int rc;
+
+ ap_msg.message = (void *) kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &work;
+ rc = ICACRT_msg_to_type4CRT_msg(zdev, &ap_msg, crt);
+ if (rc)
+ goto out_free;
+ init_completion(&work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible_timeout(
+ &work, PCICA_CLEANUP_TIME);
+ if (rc > 0)
+ rc = convert_response(zdev, &ap_msg, crt->outputdata,
+ crt->outputdatalength);
+ else {
+ /* Signal pending or message timed out. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+ if (rc == 0)
+ /* Message timed out. */
+ rc = -ETIME;
+ }
+out_free:
+ kfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The crypto operations for a PCICA card.
+ */
+static struct zcrypt_ops zcrypt_pcica_ops = {
+ .rsa_modexpo = zcrypt_pcica_modexpo,
+ .rsa_modexpo_crt = zcrypt_pcica_modexpo_crt,
+};
+
+/**
+ * Probe function for PCICA cards. It always accepts the AP device
+ * since the bus_match already checked the hardware type.
+ * @ap_dev: pointer to the AP device.
+ */
+static int zcrypt_pcica_probe(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev;
+ int rc;
+
+ zdev = zcrypt_device_alloc(PCICA_MAX_RESPONSE_SIZE);
+ if (!zdev)
+ return -ENOMEM;
+ zdev->ap_dev = ap_dev;
+ zdev->ops = &zcrypt_pcica_ops;
+ zdev->online = 1;
+ zdev->user_space_type = ZCRYPT_PCICA;
+ zdev->type_string = "PCICA";
+ zdev->min_mod_size = PCICA_MIN_MOD_SIZE;
+ zdev->max_mod_size = PCICA_MAX_MOD_SIZE;
+ zdev->speed_rating = PCICA_SPEED_RATING;
+ ap_dev->reply = &zdev->reply;
+ ap_dev->private = zdev;
+ rc = zcrypt_device_register(zdev);
+ if (rc)
+ goto out_free;
+ return 0;
+
+out_free:
+ ap_dev->private = NULL;
+ zcrypt_device_free(zdev);
+ return rc;
+}
+
+/**
+ * This is called to remove the extended PCICA driver information
+ * if an AP device is removed.
+ */
+static void zcrypt_pcica_remove(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev = ap_dev->private;
+
+ zcrypt_device_unregister(zdev);
+}
+
+int __init zcrypt_pcica_init(void)
+{
+ return ap_driver_register(&zcrypt_pcica_driver, THIS_MODULE, "pcica");
+}
+
+void zcrypt_pcica_exit(void)
+{
+ ap_driver_unregister(&zcrypt_pcica_driver);
+}
+
+#ifndef CONFIG_ZCRYPT_MONOLITHIC
+module_init(zcrypt_pcica_init);
+module_exit(zcrypt_pcica_exit);
+#endif
diff --git a/drivers/s390/crypto/zcrypt_pcica.h b/drivers/s390/crypto/zcrypt_pcica.h
new file mode 100644
index 000000000000..3be11187f6df
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_pcica.h
@@ -0,0 +1,117 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_pcica.h
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *
+ * 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.
+ */
+
+#ifndef _ZCRYPT_PCICA_H_
+#define _ZCRYPT_PCICA_H_
+
+/**
+ * The type 4 message family is associated with a PCICA card.
+ *
+ * The four members of the family are described below.
+ *
+ * Note that all unsigned char arrays are right-justified and left-padded
+ * with zeroes.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type4_hdr {
+ unsigned char reserved1;
+ unsigned char msg_type_code; /* 0x04 */
+ unsigned short msg_len;
+ unsigned char request_code; /* 0x40 */
+ unsigned char msg_fmt;
+ unsigned short reserved2;
+} __attribute__((packed));
+
+#define TYPE4_TYPE_CODE 0x04
+#define TYPE4_REQU_CODE 0x40
+
+#define TYPE4_SME_FMT 0x00
+#define TYPE4_LME_FMT 0x10
+#define TYPE4_SCR_FMT 0x40
+#define TYPE4_LCR_FMT 0x50
+
+/* Mod-Exp, with a small modulus */
+struct type4_sme {
+ struct type4_hdr header;
+ unsigned char message[128];
+ unsigned char exponent[128];
+ unsigned char modulus[128];
+} __attribute__((packed));
+
+/* Mod-Exp, with a large modulus */
+struct type4_lme {
+ struct type4_hdr header;
+ unsigned char message[256];
+ unsigned char exponent[256];
+ unsigned char modulus[256];
+} __attribute__((packed));
+
+/* CRT, with a small modulus */
+struct type4_scr {
+ struct type4_hdr header;
+ unsigned char message[128];
+ unsigned char dp[72];
+ unsigned char dq[64];
+ unsigned char p[72];
+ unsigned char q[64];
+ unsigned char u[72];
+} __attribute__((packed));
+
+/* CRT, with a large modulus */
+struct type4_lcr {
+ struct type4_hdr header;
+ unsigned char message[256];
+ unsigned char dp[136];
+ unsigned char dq[128];
+ unsigned char p[136];
+ unsigned char q[128];
+ unsigned char u[136];
+} __attribute__((packed));
+
+/**
+ * The type 84 response family is associated with a PCICA card.
+ *
+ * Note that all unsigned char arrays are right-justified and left-padded
+ * with zeroes.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+
+struct type84_hdr {
+ unsigned char reserved1;
+ unsigned char code;
+ unsigned short len;
+ unsigned char reserved2[4];
+} __attribute__((packed));
+
+#define TYPE84_RSP_CODE 0x84
+
+int zcrypt_pcica_init(void);
+void zcrypt_pcica_exit(void);
+
+#endif /* _ZCRYPT_PCICA_H_ */
diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c
new file mode 100644
index 000000000000..f295a403b29a
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_pcicc.c
@@ -0,0 +1,630 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_pcicc.c
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *
+ * 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/err.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_error.h"
+#include "zcrypt_pcicc.h"
+#include "zcrypt_cca_key.h"
+
+#define PCICC_MIN_MOD_SIZE 64 /* 512 bits */
+#define PCICC_MAX_MOD_SIZE_OLD 128 /* 1024 bits */
+#define PCICC_MAX_MOD_SIZE 256 /* 2048 bits */
+
+/**
+ * PCICC cards need a speed rating of 0. This keeps them at the end of
+ * the zcrypt device list (see zcrypt_api.c). PCICC cards are only
+ * used if no other cards are present because they are slow and can only
+ * cope with PKCS12 padded requests. The logic is queer. PKCS11 padded
+ * requests are rejected. The modexpo function encrypts PKCS12 padded data
+ * and decrypts any non-PKCS12 padded data (except PKCS11) in the assumption
+ * that it's encrypted PKCS12 data. The modexpo_crt function always decrypts
+ * the data in the assumption that its PKCS12 encrypted data.
+ */
+#define PCICC_SPEED_RATING 0
+
+#define PCICC_MAX_MESSAGE_SIZE 0x710 /* max size type6 v1 crt message */
+#define PCICC_MAX_RESPONSE_SIZE 0x710 /* max size type86 v1 reply */
+
+#define PCICC_CLEANUP_TIME (15*HZ)
+
+static struct ap_device_id zcrypt_pcicc_ids[] = {
+ { AP_DEVICE(AP_DEVICE_TYPE_PCICC) },
+ { /* end of list */ },
+};
+
+#ifndef CONFIG_ZCRYPT_MONOLITHIC
+MODULE_DEVICE_TABLE(ap, zcrypt_pcicc_ids);
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("PCICC Cryptographic Coprocessor device driver, "
+ "Copyright 2001, 2006 IBM Corporation");
+MODULE_LICENSE("GPL");
+#endif
+
+static int zcrypt_pcicc_probe(struct ap_device *ap_dev);
+static void zcrypt_pcicc_remove(struct ap_device *ap_dev);
+static void zcrypt_pcicc_receive(struct ap_device *, struct ap_message *,
+ struct ap_message *);
+
+static struct ap_driver zcrypt_pcicc_driver = {
+ .probe = zcrypt_pcicc_probe,
+ .remove = zcrypt_pcicc_remove,
+ .receive = zcrypt_pcicc_receive,
+ .ids = zcrypt_pcicc_ids,
+};
+
+/**
+ * The following is used to initialize the CPRB passed to the PCICC card
+ * in a type6 message. The 3 fields that must be filled in at execution
+ * time are req_parml, rpl_parml and usage_domain. Note that all three
+ * fields are *little*-endian. Actually, everything about this interface
+ * is ascii/little-endian, since the device has 'Intel inside'.
+ *
+ * The CPRB is followed immediately by the parm block.
+ * The parm block contains:
+ * - function code ('PD' 0x5044 or 'PK' 0x504B)
+ * - rule block (0x0A00 'PKCS-1.2' or 0x0A00 'ZERO-PAD')
+ * - VUD block
+ */
+static struct CPRB static_cprb = {
+ .cprb_len = __constant_cpu_to_le16(0x0070),
+ .cprb_ver_id = 0x41,
+ .func_id = {0x54,0x32},
+ .checkpoint_flag= 0x01,
+ .svr_namel = __constant_cpu_to_le16(0x0008),
+ .svr_name = {'I','C','S','F',' ',' ',' ',' '}
+};
+
+/**
+ * Check the message for PKCS11 padding.
+ */
+static inline int is_PKCS11_padded(unsigned char *buffer, int length)
+{
+ int i;
+ if ((buffer[0] != 0x00) || (buffer[1] != 0x01))
+ return 0;
+ for (i = 2; i < length; i++)
+ if (buffer[i] != 0xFF)
+ break;
+ if (i < 10 || i == length)
+ return 0;
+ if (buffer[i] != 0x00)
+ return 0;
+ return 1;
+}
+
+/**
+ * Check the message for PKCS12 padding.
+ */
+static inline int is_PKCS12_padded(unsigned char *buffer, int length)
+{
+ int i;
+ if ((buffer[0] != 0x00) || (buffer[1] != 0x02))
+ return 0;
+ for (i = 2; i < length; i++)
+ if (buffer[i] == 0x00)
+ break;
+ if ((i < 10) || (i == length))
+ return 0;
+ if (buffer[i] != 0x00)
+ return 0;
+ return 1;
+}
+
+/**
+ * Convert a ICAMEX message to a type6 MEX message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @mex: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICAMEX_msg_to_type6MEX_msg(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo *mex)
+{
+ static struct type6_hdr static_type6_hdr = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ .agent_id = {0x01,0x00,0x43,0x43,0x41,0x2D,0x41,0x50,
+ 0x50,0x4C,0x20,0x20,0x20,0x01,0x01,0x01},
+ .function_code = {'P','K'},
+ };
+ static struct function_and_rules_block static_pke_function_and_rules ={
+ .function_code = {'P','K'},
+ .ulen = __constant_cpu_to_le16(10),
+ .only_rule = {'P','K','C','S','-','1','.','2'}
+ };
+ struct {
+ struct type6_hdr hdr;
+ struct CPRB cprb;
+ struct function_and_rules_block fr;
+ unsigned short length;
+ char text[0];
+ } __attribute__((packed)) *msg = ap_msg->message;
+ int vud_len, pad_len, size;
+
+ /* VUD.ciphertext */
+ if (copy_from_user(msg->text, mex->inputdata, mex->inputdatalength))
+ return -EFAULT;
+
+ if (is_PKCS11_padded(msg->text, mex->inputdatalength))
+ return -EINVAL;
+
+ /* static message header and f&r */
+ msg->hdr = static_type6_hdr;
+ msg->fr = static_pke_function_and_rules;
+
+ if (is_PKCS12_padded(msg->text, mex->inputdatalength)) {
+ /* strip the padding and adjust the data length */
+ pad_len = strnlen(msg->text + 2, mex->inputdatalength - 2) + 3;
+ if (pad_len <= 9 || pad_len >= mex->inputdatalength)
+ return -ENODEV;
+ vud_len = mex->inputdatalength - pad_len;
+ memmove(msg->text, msg->text + pad_len, vud_len);
+ msg->length = cpu_to_le16(vud_len + 2);
+
+ /* Set up key after the variable length text. */
+ size = zcrypt_type6_mex_key_en(mex, msg->text + vud_len, 0);
+ if (size < 0)
+ return size;
+ size += sizeof(*msg) + vud_len; /* total size of msg */
+ } else {
+ vud_len = mex->inputdatalength;
+ msg->length = cpu_to_le16(2 + vud_len);
+
+ msg->hdr.function_code[1] = 'D';
+ msg->fr.function_code[1] = 'D';
+
+ /* Set up key after the variable length text. */
+ size = zcrypt_type6_mex_key_de(mex, msg->text + vud_len, 0);
+ if (size < 0)
+ return size;
+ size += sizeof(*msg) + vud_len; /* total size of msg */
+ }
+
+ /* message header, cprb and f&r */
+ msg->hdr.ToCardLen1 = (size - sizeof(msg->hdr) + 3) & -4;
+ msg->hdr.FromCardLen1 = PCICC_MAX_RESPONSE_SIZE - sizeof(msg->hdr);
+
+ msg->cprb = static_cprb;
+ msg->cprb.usage_domain[0]= AP_QID_QUEUE(zdev->ap_dev->qid);
+ msg->cprb.req_parml = cpu_to_le16(size - sizeof(msg->hdr) -
+ sizeof(msg->cprb));
+ msg->cprb.rpl_parml = cpu_to_le16(msg->hdr.FromCardLen1);
+
+ ap_msg->length = (size + 3) & -4;
+ return 0;
+}
+
+/**
+ * Convert a ICACRT message to a type6 CRT message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @crt: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICACRT_msg_to_type6CRT_msg(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ static struct type6_hdr static_type6_hdr = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ .agent_id = {0x01,0x00,0x43,0x43,0x41,0x2D,0x41,0x50,
+ 0x50,0x4C,0x20,0x20,0x20,0x01,0x01,0x01},
+ .function_code = {'P','D'},
+ };
+ static struct function_and_rules_block static_pkd_function_and_rules ={
+ .function_code = {'P','D'},
+ .ulen = __constant_cpu_to_le16(10),
+ .only_rule = {'P','K','C','S','-','1','.','2'}
+ };
+ struct {
+ struct type6_hdr hdr;
+ struct CPRB cprb;
+ struct function_and_rules_block fr;
+ unsigned short length;
+ char text[0];
+ } __attribute__((packed)) *msg = ap_msg->message;
+ int size;
+
+ /* VUD.ciphertext */
+ msg->length = cpu_to_le16(2 + crt->inputdatalength);
+ if (copy_from_user(msg->text, crt->inputdata, crt->inputdatalength))
+ return -EFAULT;
+
+ if (is_PKCS11_padded(msg->text, crt->inputdatalength))
+ return -EINVAL;
+
+ /* Set up key after the variable length text. */
+ size = zcrypt_type6_crt_key(crt, msg->text + crt->inputdatalength, 0);
+ if (size < 0)
+ return size;
+ size += sizeof(*msg) + crt->inputdatalength; /* total size of msg */
+
+ /* message header, cprb and f&r */
+ msg->hdr = static_type6_hdr;
+ msg->hdr.ToCardLen1 = (size - sizeof(msg->hdr) + 3) & -4;
+ msg->hdr.FromCardLen1 = PCICC_MAX_RESPONSE_SIZE - sizeof(msg->hdr);
+
+ msg->cprb = static_cprb;
+ msg->cprb.usage_domain[0] = AP_QID_QUEUE(zdev->ap_dev->qid);
+ msg->cprb.req_parml = msg->cprb.rpl_parml =
+ cpu_to_le16(size - sizeof(msg->hdr) - sizeof(msg->cprb));
+
+ msg->fr = static_pkd_function_and_rules;
+
+ ap_msg->length = (size + 3) & -4;
+ return 0;
+}
+
+/**
+ * Copy results from a type 86 reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @data: pointer to user output data
+ * @length: size of user output data
+ *
+ * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
+ */
+struct type86_reply {
+ struct type86_hdr hdr;
+ struct type86_fmt2_ext fmt2;
+ struct CPRB cprb;
+ unsigned char pad[4]; /* 4 byte function code/rules block ? */
+ unsigned short length;
+ char text[0];
+} __attribute__((packed));
+
+static int convert_type86(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ static unsigned char static_pad[] = {
+ 0x00,0x02,
+ 0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD,
+ 0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57,
+ 0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B,
+ 0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39,
+ 0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5,
+ 0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D,
+ 0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB,
+ 0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F,
+ 0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9,
+ 0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45,
+ 0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9,
+ 0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F,
+ 0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD,
+ 0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D,
+ 0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD,
+ 0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9,
+ 0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B,
+ 0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B,
+ 0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B,
+ 0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD,
+ 0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7,
+ 0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1,
+ 0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3,
+ 0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23,
+ 0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55,
+ 0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43,
+ 0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F,
+ 0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F,
+ 0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5,
+ 0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD,
+ 0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41,
+ 0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09
+ };
+ struct type86_reply *msg = reply->message;
+ unsigned short service_rc, service_rs;
+ unsigned int reply_len, pad_len;
+ char *data;
+
+ 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");
+ return -EINVAL;
+ }
+ if (service_rc == 8 && service_rs == 65) {
+ PDEBUG("Probably an even modulus on PCICC\n");
+ 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. */
+ }
+ data = msg->text;
+ reply_len = le16_to_cpu(msg->length) - 2;
+ if (reply_len > outputdatalength)
+ return -EINVAL;
+ /**
+ * For all encipher requests, the length of the ciphertext (reply_len)
+ * will always equal the modulus length. For MEX decipher requests
+ * the output needs to get padded. Minimum pad size is 10.
+ *
+ * Currently, the cases where padding will be added is for:
+ * - PCIXCC_MCL2 using a CRT form token (since PKD didn't support
+ * ZERO-PAD and CRT is only supported for PKD requests)
+ * - PCICC, always
+ */
+ pad_len = outputdatalength - reply_len;
+ if (pad_len > 0) {
+ if (pad_len < 10)
+ return -EINVAL;
+ /* 'restore' padding left in the PCICC/PCIXCC card. */
+ if (copy_to_user(outputdata, static_pad, pad_len - 1))
+ return -EFAULT;
+ if (put_user(0, outputdata + pad_len - 1))
+ return -EFAULT;
+ }
+ /* Copy the crypto response to user space. */
+ if (copy_to_user(outputdata + pad_len, data, reply_len))
+ return -EFAULT;
+ return 0;
+}
+
+static int convert_response(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ struct type86_reply *msg = reply->message;
+
+ /* Response type byte is the second byte in the response. */
+ switch (msg->hdr.type) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ return convert_error(zdev, reply);
+ case TYPE86_RSP_CODE:
+ if (msg->hdr.reply_code)
+ return convert_error(zdev, reply);
+ if (msg->cprb.cprb_ver_id == 0x01)
+ return convert_type86(zdev, reply,
+ 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. */
+ }
+}
+
+/**
+ * This function is called from the AP bus code after a crypto request
+ * "msg" has finished with the reply message "reply".
+ * It is called from tasklet context.
+ * @ap_dev: pointer to the AP device
+ * @msg: pointer to the AP message
+ * @reply: pointer to the AP reply message
+ */
+static void zcrypt_pcicc_receive(struct ap_device *ap_dev,
+ struct ap_message *msg,
+ struct ap_message *reply)
+{
+ static struct error_hdr error_reply = {
+ .type = TYPE82_RSP_CODE,
+ .reply_code = REP82_ERROR_MACHINE_FAILURE,
+ };
+ struct type86_reply *t86r = reply->message;
+ int length;
+
+ /* Copy the reply message to the request message buffer. */
+ if (IS_ERR(reply))
+ memcpy(msg->message, &error_reply, sizeof(error_reply));
+ else if (t86r->hdr.type == TYPE86_RSP_CODE &&
+ t86r->cprb.cprb_ver_id == 0x01) {
+ length = sizeof(struct type86_reply) + t86r->length - 2;
+ length = min(PCICC_MAX_RESPONSE_SIZE, length);
+ memcpy(msg->message, reply->message, length);
+ } else
+ memcpy(msg->message, reply->message, sizeof error_reply);
+ complete((struct completion *) msg->private);
+}
+
+static atomic_t zcrypt_step = ATOMIC_INIT(0);
+
+/**
+ * The request distributor calls this function if it picked the PCICC
+ * device to handle a modexpo request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCICC device to the request distributor
+ * @mex: pointer to the modexpo request buffer
+ */
+static long zcrypt_pcicc_modexpo(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo *mex)
+{
+ struct ap_message ap_msg;
+ struct completion work;
+ int rc;
+
+ ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.length = PAGE_SIZE;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &work;
+ rc = ICAMEX_msg_to_type6MEX_msg(zdev, &ap_msg, mex);
+ if (rc)
+ goto out_free;
+ init_completion(&work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible_timeout(
+ &work, PCICC_CLEANUP_TIME);
+ if (rc > 0)
+ rc = convert_response(zdev, &ap_msg, mex->outputdata,
+ mex->outputdatalength);
+ else {
+ /* Signal pending or message timed out. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+ if (rc == 0)
+ /* Message timed out. */
+ rc = -ETIME;
+ }
+out_free:
+ free_page((unsigned long) ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCICC
+ * device to handle a modexpo_crt request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCICC device to the request distributor
+ * @crt: pointer to the modexpoc_crt request buffer
+ */
+static long zcrypt_pcicc_modexpo_crt(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ struct ap_message ap_msg;
+ struct completion work;
+ int rc;
+
+ ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.length = PAGE_SIZE;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &work;
+ rc = ICACRT_msg_to_type6CRT_msg(zdev, &ap_msg, crt);
+ if (rc)
+ goto out_free;
+ init_completion(&work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible_timeout(
+ &work, PCICC_CLEANUP_TIME);
+ if (rc > 0)
+ rc = convert_response(zdev, &ap_msg, crt->outputdata,
+ crt->outputdatalength);
+ else {
+ /* Signal pending or message timed out. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+ if (rc == 0)
+ /* Message timed out. */
+ rc = -ETIME;
+ }
+out_free:
+ free_page((unsigned long) ap_msg.message);
+ return rc;
+}
+
+/**
+ * The crypto operations for a PCICC card.
+ */
+static struct zcrypt_ops zcrypt_pcicc_ops = {
+ .rsa_modexpo = zcrypt_pcicc_modexpo,
+ .rsa_modexpo_crt = zcrypt_pcicc_modexpo_crt,
+};
+
+/**
+ * Probe function for PCICC cards. It always accepts the AP device
+ * since the bus_match already checked the hardware type.
+ * @ap_dev: pointer to the AP device.
+ */
+static int zcrypt_pcicc_probe(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev;
+ int rc;
+
+ zdev = zcrypt_device_alloc(PCICC_MAX_RESPONSE_SIZE);
+ if (!zdev)
+ return -ENOMEM;
+ zdev->ap_dev = ap_dev;
+ zdev->ops = &zcrypt_pcicc_ops;
+ zdev->online = 1;
+ zdev->user_space_type = ZCRYPT_PCICC;
+ zdev->type_string = "PCICC";
+ zdev->min_mod_size = PCICC_MIN_MOD_SIZE;
+ zdev->max_mod_size = PCICC_MAX_MOD_SIZE;
+ zdev->speed_rating = PCICC_SPEED_RATING;
+ ap_dev->reply = &zdev->reply;
+ ap_dev->private = zdev;
+ rc = zcrypt_device_register(zdev);
+ if (rc)
+ goto out_free;
+ return 0;
+
+ out_free:
+ ap_dev->private = NULL;
+ zcrypt_device_free(zdev);
+ return rc;
+}
+
+/**
+ * This is called to remove the extended PCICC driver information
+ * if an AP device is removed.
+ */
+static void zcrypt_pcicc_remove(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev = ap_dev->private;
+
+ zcrypt_device_unregister(zdev);
+}
+
+int __init zcrypt_pcicc_init(void)
+{
+ return ap_driver_register(&zcrypt_pcicc_driver, THIS_MODULE, "pcicc");
+}
+
+void zcrypt_pcicc_exit(void)
+{
+ ap_driver_unregister(&zcrypt_pcicc_driver);
+}
+
+#ifndef CONFIG_ZCRYPT_MONOLITHIC
+module_init(zcrypt_pcicc_init);
+module_exit(zcrypt_pcicc_exit);
+#endif
diff --git a/drivers/s390/crypto/zcrypt_pcicc.h b/drivers/s390/crypto/zcrypt_pcicc.h
new file mode 100644
index 000000000000..6d4454846c8f
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_pcicc.h
@@ -0,0 +1,176 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_pcicc.h
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *
+ * 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.
+ */
+
+#ifndef _ZCRYPT_PCICC_H_
+#define _ZCRYPT_PCICC_H_
+
+/**
+ * The type 6 message family is associated with PCICC or PCIXCC cards.
+ *
+ * It contains a message header followed by a CPRB, both of which
+ * are described below.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type6_hdr {
+ unsigned char reserved1; /* 0x00 */
+ unsigned char type; /* 0x06 */
+ unsigned char reserved2[2]; /* 0x0000 */
+ unsigned char right[4]; /* 0x00000000 */
+ unsigned char reserved3[2]; /* 0x0000 */
+ unsigned char reserved4[2]; /* 0x0000 */
+ unsigned char apfs[4]; /* 0x00000000 */
+ unsigned int offset1; /* 0x00000058 (offset to CPRB) */
+ unsigned int offset2; /* 0x00000000 */
+ unsigned int offset3; /* 0x00000000 */
+ unsigned int offset4; /* 0x00000000 */
+ unsigned char agent_id[16]; /* PCICC: */
+ /* 0x0100 */
+ /* 0x4343412d4150504c202020 */
+ /* 0x010101 */
+ /* PCIXCC: */
+ /* 0x4341000000000000 */
+ /* 0x0000000000000000 */
+ unsigned char rqid[2]; /* rqid. internal to 603 */
+ unsigned char reserved5[2]; /* 0x0000 */
+ unsigned char function_code[2]; /* for PKD, 0x5044 (ascii 'PD') */
+ unsigned char reserved6[2]; /* 0x0000 */
+ unsigned int ToCardLen1; /* (request CPRB len + 3) & -4 */
+ unsigned int ToCardLen2; /* db len 0x00000000 for PKD */
+ unsigned int ToCardLen3; /* 0x00000000 */
+ unsigned int ToCardLen4; /* 0x00000000 */
+ unsigned int FromCardLen1; /* response buffer length */
+ unsigned int FromCardLen2; /* db len 0x00000000 for PKD */
+ unsigned int FromCardLen3; /* 0x00000000 */
+ unsigned int FromCardLen4; /* 0x00000000 */
+} __attribute__((packed));
+
+/**
+ * CPRB
+ * Note that all shorts, ints and longs are little-endian.
+ * All pointer fields are 32-bits long, and mean nothing
+ *
+ * A request CPRB is followed by a request_parameter_block.
+ *
+ * The request (or reply) parameter block is organized thus:
+ * function code
+ * VUD block
+ * key block
+ */
+struct CPRB {
+ unsigned short cprb_len; /* CPRB length */
+ unsigned char cprb_ver_id; /* CPRB version id. */
+ unsigned char pad_000; /* Alignment pad byte. */
+ unsigned char srpi_rtcode[4]; /* SRPI return code LELONG */
+ unsigned char srpi_verb; /* SRPI verb type */
+ unsigned char flags; /* flags */
+ unsigned char func_id[2]; /* function id */
+ unsigned char checkpoint_flag; /* */
+ unsigned char resv2; /* reserved */
+ unsigned short req_parml; /* request parameter buffer */
+ /* length 16-bit little endian */
+ unsigned char req_parmp[4]; /* request parameter buffer *
+ * pointer (means nothing: the *
+ * parameter buffer follows *
+ * the CPRB). */
+ unsigned char req_datal[4]; /* request data buffer */
+ /* length ULELONG */
+ unsigned char req_datap[4]; /* request data buffer */
+ /* pointer */
+ unsigned short rpl_parml; /* reply parameter buffer */
+ /* length 16-bit little endian */
+ unsigned char pad_001[2]; /* Alignment pad bytes. ULESHORT */
+ unsigned char rpl_parmp[4]; /* reply parameter buffer *
+ * pointer (means nothing: the *
+ * parameter buffer follows *
+ * the CPRB). */
+ unsigned char rpl_datal[4]; /* reply data buffer len ULELONG */
+ unsigned char rpl_datap[4]; /* reply data buffer */
+ /* pointer */
+ unsigned short ccp_rscode; /* server reason code ULESHORT */
+ unsigned short ccp_rtcode; /* server return code ULESHORT */
+ unsigned char repd_parml[2]; /* replied parameter len ULESHORT*/
+ unsigned char mac_data_len[2]; /* Mac Data Length ULESHORT */
+ unsigned char repd_datal[4]; /* replied data length ULELONG */
+ unsigned char req_pc[2]; /* PC identifier */
+ unsigned char res_origin[8]; /* resource origin */
+ unsigned char mac_value[8]; /* Mac Value */
+ unsigned char logon_id[8]; /* Logon Identifier */
+ unsigned char usage_domain[2]; /* cdx */
+ unsigned char resv3[18]; /* reserved for requestor */
+ unsigned short svr_namel; /* server name length ULESHORT */
+ unsigned char svr_name[8]; /* server name */
+} __attribute__((packed));
+
+/**
+ * The type 86 message family is associated with PCICC and PCIXCC cards.
+ *
+ * It contains a message header followed by a CPRB. The CPRB is
+ * the same as the request CPRB, which is described above.
+ *
+ * If format is 1, an error condition exists and no data beyond
+ * the 8-byte message header is of interest.
+ *
+ * The non-error message is shown below.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type86_hdr {
+ unsigned char reserved1; /* 0x00 */
+ unsigned char type; /* 0x86 */
+ unsigned char format; /* 0x01 (error) or 0x02 (ok) */
+ unsigned char reserved2; /* 0x00 */
+ unsigned char reply_code; /* reply code (see above) */
+ unsigned char reserved3[3]; /* 0x000000 */
+} __attribute__((packed));
+
+#define TYPE86_RSP_CODE 0x86
+#define TYPE86_FMT2 0x02
+
+struct type86_fmt2_ext {
+ unsigned char reserved[4]; /* 0x00000000 */
+ unsigned char apfs[4]; /* final status */
+ unsigned int count1; /* length of CPRB + parameters */
+ unsigned int offset1; /* offset to CPRB */
+ unsigned int count2; /* 0x00000000 */
+ unsigned int offset2; /* db offset 0x00000000 for PKD */
+ unsigned int count3; /* 0x00000000 */
+ unsigned int offset3; /* 0x00000000 */
+ unsigned int count4; /* 0x00000000 */
+ unsigned int offset4; /* 0x00000000 */
+} __attribute__((packed));
+
+struct function_and_rules_block {
+ unsigned char function_code[2];
+ unsigned short ulen;
+ unsigned char only_rule[8];
+} __attribute__((packed));
+
+int zcrypt_pcicc_init(void);
+void zcrypt_pcicc_exit(void);
+
+#endif /* _ZCRYPT_PCICC_H_ */
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
new file mode 100644
index 000000000000..2da8b9381407
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -0,0 +1,951 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_pcixcc.c
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *
+ * 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/err.h>
+#include <linux/delay.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_error.h"
+#include "zcrypt_pcicc.h"
+#include "zcrypt_pcixcc.h"
+#include "zcrypt_cca_key.h"
+
+#define PCIXCC_MIN_MOD_SIZE 16 /* 128 bits */
+#define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */
+#define PCIXCC_MAX_MOD_SIZE 256 /* 2048 bits */
+
+#define PCIXCC_MCL2_SPEED_RATING 7870 /* FIXME: needs finetuning */
+#define PCIXCC_MCL3_SPEED_RATING 7870
+#define CEX2C_SPEED_RATING 8540
+
+#define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c /* max size type6 v2 crt message */
+#define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */
+
+#define PCIXCC_MAX_XCRB_MESSAGE_SIZE (12*1024)
+#define PCIXCC_MAX_XCRB_RESPONSE_SIZE PCIXCC_MAX_XCRB_MESSAGE_SIZE
+#define PCIXCC_MAX_XCRB_DATA_SIZE (11*1024)
+#define PCIXCC_MAX_XCRB_REPLY_SIZE (5*1024)
+
+#define PCIXCC_MAX_RESPONSE_SIZE PCIXCC_MAX_XCRB_RESPONSE_SIZE
+
+#define PCIXCC_CLEANUP_TIME (15*HZ)
+
+#define CEIL4(x) ((((x)+3)/4)*4)
+
+struct response_type {
+ struct completion work;
+ int type;
+};
+#define PCIXCC_RESPONSE_TYPE_ICA 0
+#define PCIXCC_RESPONSE_TYPE_XCRB 1
+
+static struct ap_device_id zcrypt_pcixcc_ids[] = {
+ { AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) },
+ { AP_DEVICE(AP_DEVICE_TYPE_CEX2C) },
+ { /* end of list */ },
+};
+
+#ifndef CONFIG_ZCRYPT_MONOLITHIC
+MODULE_DEVICE_TABLE(ap, zcrypt_pcixcc_ids);
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("PCIXCC Cryptographic Coprocessor device driver, "
+ "Copyright 2001, 2006 IBM Corporation");
+MODULE_LICENSE("GPL");
+#endif
+
+static int zcrypt_pcixcc_probe(struct ap_device *ap_dev);
+static void zcrypt_pcixcc_remove(struct ap_device *ap_dev);
+static void zcrypt_pcixcc_receive(struct ap_device *, struct ap_message *,
+ struct ap_message *);
+
+static struct ap_driver zcrypt_pcixcc_driver = {
+ .probe = zcrypt_pcixcc_probe,
+ .remove = zcrypt_pcixcc_remove,
+ .receive = zcrypt_pcixcc_receive,
+ .ids = zcrypt_pcixcc_ids,
+};
+
+/**
+ * The following is used to initialize the CPRBX passed to the PCIXCC/CEX2C
+ * card in a type6 message. The 3 fields that must be filled in at execution
+ * time are req_parml, rpl_parml and usage_domain.
+ * Everything about this interface is ascii/big-endian, since the
+ * device does *not* have 'Intel inside'.
+ *
+ * The CPRBX is followed immediately by the parm block.
+ * The parm block contains:
+ * - function code ('PD' 0x5044 or 'PK' 0x504B)
+ * - rule block (one of:)
+ * + 0x000A 'PKCS-1.2' (MCL2 'PD')
+ * + 0x000A 'ZERO-PAD' (MCL2 'PK')
+ * + 0x000A 'ZERO-PAD' (MCL3 'PD' or CEX2C 'PD')
+ * + 0x000A 'MRP ' (MCL3 'PK' or CEX2C 'PK')
+ * - VUD block
+ */
+static struct CPRBX static_cprbx = {
+ .cprb_len = 0x00DC,
+ .cprb_ver_id = 0x02,
+ .func_id = {0x54,0x32},
+};
+
+/**
+ * Convert a ICAMEX message to a type6 MEX message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @mex: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo *mex)
+{
+ static struct type6_hdr static_type6_hdrX = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ .agent_id = {'C','A',},
+ .function_code = {'P','K'},
+ };
+ static struct function_and_rules_block static_pke_fnr = {
+ .function_code = {'P','K'},
+ .ulen = 10,
+ .only_rule = {'M','R','P',' ',' ',' ',' ',' '}
+ };
+ static struct function_and_rules_block static_pke_fnr_MCL2 = {
+ .function_code = {'P','K'},
+ .ulen = 10,
+ .only_rule = {'Z','E','R','O','-','P','A','D'}
+ };
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ struct function_and_rules_block fr;
+ unsigned short length;
+ char text[0];
+ } __attribute__((packed)) *msg = ap_msg->message;
+ int size;
+
+ /* VUD.ciphertext */
+ msg->length = mex->inputdatalength + 2;
+ if (copy_from_user(msg->text, mex->inputdata, mex->inputdatalength))
+ return -EFAULT;
+
+ /* Set up key which is located after the variable length text. */
+ size = zcrypt_type6_mex_key_en(mex, msg->text+mex->inputdatalength, 1);
+ if (size < 0)
+ return size;
+ size += sizeof(*msg) + mex->inputdatalength;
+
+ /* message header, cprbx and f&r */
+ msg->hdr = static_type6_hdrX;
+ msg->hdr.ToCardLen1 = size - sizeof(msg->hdr);
+ msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
+
+ msg->cprbx = static_cprbx;
+ msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
+ msg->cprbx.rpl_msgbl = msg->hdr.FromCardLen1;
+
+ msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
+ static_pke_fnr_MCL2 : static_pke_fnr;
+
+ msg->cprbx.req_parml = size - sizeof(msg->hdr) - sizeof(msg->cprbx);
+
+ ap_msg->length = size;
+ return 0;
+}
+
+/**
+ * Convert a ICACRT message to a type6 CRT message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @crt: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ static struct type6_hdr static_type6_hdrX = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ .agent_id = {'C','A',},
+ .function_code = {'P','D'},
+ };
+ static struct function_and_rules_block static_pkd_fnr = {
+ .function_code = {'P','D'},
+ .ulen = 10,
+ .only_rule = {'Z','E','R','O','-','P','A','D'}
+ };
+
+ static struct function_and_rules_block static_pkd_fnr_MCL2 = {
+ .function_code = {'P','D'},
+ .ulen = 10,
+ .only_rule = {'P','K','C','S','-','1','.','2'}
+ };
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ struct function_and_rules_block fr;
+ unsigned short length;
+ char text[0];
+ } __attribute__((packed)) *msg = ap_msg->message;
+ int size;
+
+ /* VUD.ciphertext */
+ msg->length = crt->inputdatalength + 2;
+ if (copy_from_user(msg->text, crt->inputdata, crt->inputdatalength))
+ return -EFAULT;
+
+ /* Set up key which is located after the variable length text. */
+ size = zcrypt_type6_crt_key(crt, msg->text + crt->inputdatalength, 1);
+ if (size < 0)
+ return size;
+ size += sizeof(*msg) + crt->inputdatalength; /* total size of msg */
+
+ /* message header, cprbx and f&r */
+ msg->hdr = static_type6_hdrX;
+ msg->hdr.ToCardLen1 = size - sizeof(msg->hdr);
+ msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
+
+ msg->cprbx = static_cprbx;
+ msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
+ msg->cprbx.req_parml = msg->cprbx.rpl_msgbl =
+ size - sizeof(msg->hdr) - sizeof(msg->cprbx);
+
+ msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
+ static_pkd_fnr_MCL2 : static_pkd_fnr;
+
+ ap_msg->length = size;
+ return 0;
+}
+
+/**
+ * Convert a XCRB message to a type6 CPRB message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @xcRB: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+struct type86_fmt2_msg {
+ struct type86_hdr hdr;
+ struct type86_fmt2_ext fmt2;
+} __attribute__((packed));
+
+static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_xcRB *xcRB)
+{
+ static struct type6_hdr static_type6_hdrX = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ };
+ struct {
+ struct type6_hdr hdr;
+ struct ica_CPRBX cprbx;
+ } __attribute__((packed)) *msg = ap_msg->message;
+
+ int rcblen = CEIL4(xcRB->request_control_blk_length);
+ int replylen;
+ char *req_data = ap_msg->message + sizeof(struct type6_hdr) + rcblen;
+ char *function_code;
+
+ /* length checks */
+ 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);
+ 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);
+ 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);
+ 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 */
+ msg->hdr = static_type6_hdrX;
+ memcpy(msg->hdr.agent_id , &(xcRB->agent_ID), sizeof(xcRB->agent_ID));
+ msg->hdr.ToCardLen1 = xcRB->request_control_blk_length;
+ if (xcRB->request_data_length) {
+ msg->hdr.offset2 = msg->hdr.offset1 + rcblen;
+ msg->hdr.ToCardLen2 = xcRB->request_data_length;
+ }
+ msg->hdr.FromCardLen1 = xcRB->reply_control_blk_length;
+ msg->hdr.FromCardLen2 = xcRB->reply_data_length;
+
+ /* prepare CPRB */
+ if (copy_from_user(&(msg->cprbx), xcRB->request_control_blk_addr,
+ 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);
+ return -EFAULT;
+ }
+ function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
+ memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code));
+
+ /* copy data block */
+ if (xcRB->request_data_length &&
+ copy_from_user(req_data, xcRB->request_data_address,
+ xcRB->request_data_length))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Copy results from a type 86 ICA reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @data: pointer to user output data
+ * @length: size of user output data
+ *
+ * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
+ */
+struct type86x_reply {
+ struct type86_hdr hdr;
+ struct type86_fmt2_ext fmt2;
+ struct CPRBX cprbx;
+ unsigned char pad[4]; /* 4 byte function code/rules block ? */
+ unsigned short length;
+ char text[0];
+} __attribute__((packed));
+
+static int convert_type86_ica(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ static unsigned char static_pad[] = {
+ 0x00,0x02,
+ 0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD,
+ 0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57,
+ 0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B,
+ 0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39,
+ 0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5,
+ 0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D,
+ 0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB,
+ 0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F,
+ 0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9,
+ 0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45,
+ 0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9,
+ 0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F,
+ 0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD,
+ 0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D,
+ 0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD,
+ 0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9,
+ 0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B,
+ 0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B,
+ 0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B,
+ 0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD,
+ 0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7,
+ 0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1,
+ 0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3,
+ 0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23,
+ 0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55,
+ 0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43,
+ 0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F,
+ 0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F,
+ 0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5,
+ 0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD,
+ 0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41,
+ 0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09
+ };
+ struct type86x_reply *msg = reply->message;
+ unsigned short service_rc, service_rs;
+ unsigned int reply_len, pad_len;
+ char *data;
+
+ 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");
+ return -EINVAL;
+ }
+ if (service_rc == 8 && service_rs == 65) {
+ PDEBUG("Probably an even modulus on PCIXCC/CEX2C\n");
+ return -EINVAL;
+ }
+ if (service_rc == 8 && service_rs == 770) {
+ PDEBUG("Invalid key length on PCIXCC/CEX2C\n");
+ zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD;
+ return -EAGAIN;
+ }
+ 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;
+ }
+ 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. */
+ }
+ data = msg->text;
+ reply_len = msg->length - 2;
+ if (reply_len > outputdatalength)
+ return -EINVAL;
+ /**
+ * For all encipher requests, the length of the ciphertext (reply_len)
+ * will always equal the modulus length. For MEX decipher requests
+ * the output needs to get padded. Minimum pad size is 10.
+ *
+ * Currently, the cases where padding will be added is for:
+ * - PCIXCC_MCL2 using a CRT form token (since PKD didn't support
+ * ZERO-PAD and CRT is only supported for PKD requests)
+ * - PCICC, always
+ */
+ pad_len = outputdatalength - reply_len;
+ if (pad_len > 0) {
+ if (pad_len < 10)
+ return -EINVAL;
+ /* 'restore' padding left in the PCICC/PCIXCC card. */
+ if (copy_to_user(outputdata, static_pad, pad_len - 1))
+ return -EFAULT;
+ if (put_user(0, outputdata + pad_len - 1))
+ return -EFAULT;
+ }
+ /* Copy the crypto response to user space. */
+ if (copy_to_user(outputdata + pad_len, data, reply_len))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Copy results from a type 86 XCRB reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @xcRB: pointer to XCRB
+ *
+ * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
+ */
+static int convert_type86_xcrb(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ struct ica_xcRB *xcRB)
+{
+ struct type86_fmt2_msg *msg = reply->message;
+ char *data = reply->message;
+
+ /* Copy CPRB to user */
+ if (copy_to_user(xcRB->reply_control_blk_addr,
+ data + msg->fmt2.offset1, msg->fmt2.count1))
+ return -EFAULT;
+ xcRB->reply_control_blk_length = msg->fmt2.count1;
+
+ /* Copy data buffer to user */
+ if (msg->fmt2.count2)
+ if (copy_to_user(xcRB->reply_data_addr,
+ data + msg->fmt2.offset2, msg->fmt2.count2))
+ return -EFAULT;
+ xcRB->reply_data_length = msg->fmt2.count2;
+ return 0;
+}
+
+static int convert_response_ica(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ struct type86x_reply *msg = reply->message;
+
+ /* Response type byte is the second byte in the response. */
+ switch (((unsigned char *) reply->message)[1]) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ return convert_error(zdev, reply);
+ case TYPE86_RSP_CODE:
+ if (msg->hdr.reply_code)
+ return convert_error(zdev, reply);
+ if (msg->cprbx.cprb_ver_id == 0x02)
+ return convert_type86_ica(zdev, reply,
+ 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. */
+ }
+}
+
+static int convert_response_xcrb(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ struct ica_xcRB *xcRB)
+{
+ struct type86x_reply *msg = reply->message;
+
+ /* Response type byte is the second byte in the response. */
+ switch (((unsigned char *) reply->message)[1]) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
+ return convert_error(zdev, reply);
+ case TYPE86_RSP_CODE:
+ if (msg->hdr.reply_code) {
+ memcpy(&(xcRB->status), msg->fmt2.apfs, sizeof(u32));
+ return convert_error(zdev, reply);
+ }
+ if (msg->cprbx.cprb_ver_id == 0x02)
+ 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. */
+ }
+}
+
+/**
+ * This function is called from the AP bus code after a crypto request
+ * "msg" has finished with the reply message "reply".
+ * It is called from tasklet context.
+ * @ap_dev: pointer to the AP device
+ * @msg: pointer to the AP message
+ * @reply: pointer to the AP reply message
+ */
+static void zcrypt_pcixcc_receive(struct ap_device *ap_dev,
+ struct ap_message *msg,
+ struct ap_message *reply)
+{
+ static struct error_hdr error_reply = {
+ .type = TYPE82_RSP_CODE,
+ .reply_code = REP82_ERROR_MACHINE_FAILURE,
+ };
+ struct response_type *resp_type =
+ (struct response_type *) msg->private;
+ struct type86x_reply *t86r = reply->message;
+ int length;
+
+ /* Copy the reply message to the request message buffer. */
+ if (IS_ERR(reply))
+ memcpy(msg->message, &error_reply, sizeof(error_reply));
+ else if (t86r->hdr.type == TYPE86_RSP_CODE &&
+ t86r->cprbx.cprb_ver_id == 0x02) {
+ switch (resp_type->type) {
+ case PCIXCC_RESPONSE_TYPE_ICA:
+ length = sizeof(struct type86x_reply)
+ + t86r->length - 2;
+ length = min(PCIXCC_MAX_ICA_RESPONSE_SIZE, length);
+ memcpy(msg->message, reply->message, length);
+ break;
+ case PCIXCC_RESPONSE_TYPE_XCRB:
+ length = t86r->fmt2.offset2 + t86r->fmt2.count2;
+ length = min(PCIXCC_MAX_XCRB_RESPONSE_SIZE, length);
+ 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);
+ }
+ } else
+ memcpy(msg->message, reply->message, sizeof error_reply);
+ complete(&(resp_type->work));
+}
+
+static atomic_t zcrypt_step = ATOMIC_INIT(0);
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a modexpo request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCIXCC/CEX2C device to the request distributor
+ * @mex: pointer to the modexpo request buffer
+ */
+static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo *mex)
+{
+ struct ap_message ap_msg;
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_ICA,
+ };
+ int rc;
+
+ ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &resp_type;
+ rc = ICAMEX_msg_to_type6MEX_msgX(zdev, &ap_msg, mex);
+ if (rc)
+ goto out_free;
+ init_completion(&resp_type.work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible_timeout(
+ &resp_type.work, PCIXCC_CLEANUP_TIME);
+ if (rc > 0)
+ rc = convert_response_ica(zdev, &ap_msg, mex->outputdata,
+ mex->outputdatalength);
+ else {
+ /* Signal pending or message timed out. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+ if (rc == 0)
+ /* Message timed out. */
+ rc = -ETIME;
+ }
+out_free:
+ free_page((unsigned long) ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a modexpo_crt request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCIXCC/CEX2C device to the request distributor
+ * @crt: pointer to the modexpoc_crt request buffer
+ */
+static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ struct ap_message ap_msg;
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_ICA,
+ };
+ int rc;
+
+ ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &resp_type;
+ rc = ICACRT_msg_to_type6CRT_msgX(zdev, &ap_msg, crt);
+ if (rc)
+ goto out_free;
+ init_completion(&resp_type.work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible_timeout(
+ &resp_type.work, PCIXCC_CLEANUP_TIME);
+ if (rc > 0)
+ rc = convert_response_ica(zdev, &ap_msg, crt->outputdata,
+ crt->outputdatalength);
+ else {
+ /* Signal pending or message timed out. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+ if (rc == 0)
+ /* Message timed out. */
+ rc = -ETIME;
+ }
+out_free:
+ free_page((unsigned long) ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a send_cprb request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCIXCC/CEX2C device to the request distributor
+ * @xcRB: pointer to the send_cprb request buffer
+ */
+long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev, struct ica_xcRB *xcRB)
+{
+ struct ap_message ap_msg;
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_XCRB,
+ };
+ int rc;
+
+ ap_msg.message = (void *) kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &resp_type;
+ rc = XCRB_msg_to_type6CPRB_msgX(zdev, &ap_msg, xcRB);
+ if (rc)
+ goto out_free;
+ init_completion(&resp_type.work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible_timeout(
+ &resp_type.work, PCIXCC_CLEANUP_TIME);
+ if (rc > 0)
+ rc = convert_response_xcrb(zdev, &ap_msg, xcRB);
+ else {
+ /* Signal pending or message timed out. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+ if (rc == 0)
+ /* Message timed out. */
+ rc = -ETIME;
+ }
+out_free:
+ memset(ap_msg.message, 0x0, ap_msg.length);
+ kfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The crypto operations for a PCIXCC/CEX2C card.
+ */
+static struct zcrypt_ops zcrypt_pcixcc_ops = {
+ .rsa_modexpo = zcrypt_pcixcc_modexpo,
+ .rsa_modexpo_crt = zcrypt_pcixcc_modexpo_crt,
+ .send_cprb = zcrypt_pcixcc_send_cprb,
+};
+
+/**
+ * Micro-code detection function. Its sends a message to a pcixcc card
+ * to find out the microcode level.
+ * @ap_dev: pointer to the AP device.
+ */
+static int zcrypt_pcixcc_mcl(struct ap_device *ap_dev)
+{
+ static unsigned char msg[] = {
+ 0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x43,0x41,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x00,
+ 0x00,0x00,0x01,0xC4,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x07,0x24,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0xDC,0x02,0x00,0x00,0x00,0x54,0x32,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE8,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x0A,
+ 0x4D,0x52,0x50,0x20,0x20,0x20,0x20,0x20,
+ 0x00,0x42,0x00,0x01,0x02,0x03,0x04,0x05,
+ 0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,
+ 0x0E,0x0F,0x00,0x11,0x22,0x33,0x44,0x55,
+ 0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,
+ 0xEE,0xFF,0xFF,0xEE,0xDD,0xCC,0xBB,0xAA,
+ 0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22,
+ 0x11,0x00,0x01,0x23,0x45,0x67,0x89,0xAB,
+ 0xCD,0xEF,0xFE,0xDC,0xBA,0x98,0x76,0x54,
+ 0x32,0x10,0x00,0x9A,0x00,0x98,0x00,0x00,
+ 0x1E,0x00,0x00,0x94,0x00,0x00,0x00,0x00,
+ 0x04,0x00,0x00,0x8C,0x00,0x00,0x00,0x40,
+ 0x02,0x00,0x00,0x40,0xBA,0xE8,0x23,0x3C,
+ 0x75,0xF3,0x91,0x61,0xD6,0x73,0x39,0xCF,
+ 0x7B,0x6D,0x8E,0x61,0x97,0x63,0x9E,0xD9,
+ 0x60,0x55,0xD6,0xC7,0xEF,0xF8,0x1E,0x63,
+ 0x95,0x17,0xCC,0x28,0x45,0x60,0x11,0xC5,
+ 0xC4,0x4E,0x66,0xC6,0xE6,0xC3,0xDE,0x8A,
+ 0x19,0x30,0xCF,0x0E,0xD7,0xAA,0xDB,0x01,
+ 0xD8,0x00,0xBB,0x8F,0x39,0x9F,0x64,0x28,
+ 0xF5,0x7A,0x77,0x49,0xCC,0x6B,0xA3,0x91,
+ 0x97,0x70,0xE7,0x60,0x1E,0x39,0xE1,0xE5,
+ 0x33,0xE1,0x15,0x63,0x69,0x08,0x80,0x4C,
+ 0x67,0xC4,0x41,0x8F,0x48,0xDF,0x26,0x98,
+ 0xF1,0xD5,0x8D,0x88,0xD9,0x6A,0xA4,0x96,
+ 0xC5,0x84,0xD9,0x30,0x49,0x67,0x7D,0x19,
+ 0xB1,0xB3,0x45,0x4D,0xB2,0x53,0x9A,0x47,
+ 0x3C,0x7C,0x55,0xBF,0xCC,0x85,0x00,0x36,
+ 0xF1,0x3D,0x93,0x53
+ };
+ unsigned long long psmid;
+ struct CPRBX *cprbx;
+ char *reply;
+ int rc, i;
+
+ reply = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!reply)
+ return -ENOMEM;
+
+ rc = ap_send(ap_dev->qid, 0x0102030405060708ULL, msg, sizeof(msg));
+ if (rc)
+ goto out_free;
+
+ /* Wait for the test message to complete. */
+ for (i = 0; i < 6; i++) {
+ mdelay(300);
+ rc = ap_recv(ap_dev->qid, &psmid, reply, 4096);
+ if (rc == 0 && psmid == 0x0102030405060708ULL)
+ break;
+ }
+
+ if (i >= 6) {
+ /* Got no answer. */
+ rc = -ENODEV;
+ goto out_free;
+ }
+
+ cprbx = (struct CPRBX *) (reply + 48);
+ if (cprbx->ccp_rtcode == 8 && cprbx->ccp_rscode == 33)
+ rc = ZCRYPT_PCIXCC_MCL2;
+ else
+ rc = ZCRYPT_PCIXCC_MCL3;
+out_free:
+ free_page((unsigned long) reply);
+ return rc;
+}
+
+/**
+ * Probe function for PCIXCC/CEX2C cards. It always accepts the AP device
+ * since the bus_match already checked the hardware type. The PCIXCC
+ * cards come in two flavours: micro code level 2 and micro code level 3.
+ * This is checked by sending a test message to the device.
+ * @ap_dev: pointer to the AP device.
+ */
+static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev;
+ int rc;
+
+ zdev = zcrypt_device_alloc(PCIXCC_MAX_RESPONSE_SIZE);
+ if (!zdev)
+ return -ENOMEM;
+ zdev->ap_dev = ap_dev;
+ zdev->ops = &zcrypt_pcixcc_ops;
+ zdev->online = 1;
+ if (ap_dev->device_type == AP_DEVICE_TYPE_PCIXCC) {
+ rc = zcrypt_pcixcc_mcl(ap_dev);
+ if (rc < 0) {
+ zcrypt_device_free(zdev);
+ return rc;
+ }
+ zdev->user_space_type = rc;
+ if (rc == ZCRYPT_PCIXCC_MCL2) {
+ zdev->type_string = "PCIXCC_MCL2";
+ zdev->speed_rating = PCIXCC_MCL2_SPEED_RATING;
+ zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD;
+ zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE;
+ } else {
+ zdev->type_string = "PCIXCC_MCL3";
+ zdev->speed_rating = PCIXCC_MCL3_SPEED_RATING;
+ zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE;
+ zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE;
+ }
+ } else {
+ zdev->user_space_type = ZCRYPT_CEX2C;
+ zdev->type_string = "CEX2C";
+ zdev->speed_rating = CEX2C_SPEED_RATING;
+ zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE;
+ zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE;
+ }
+ ap_dev->reply = &zdev->reply;
+ ap_dev->private = zdev;
+ rc = zcrypt_device_register(zdev);
+ if (rc)
+ goto out_free;
+ return 0;
+
+ out_free:
+ ap_dev->private = NULL;
+ zcrypt_device_free(zdev);
+ return rc;
+}
+
+/**
+ * This is called to remove the extended PCIXCC/CEX2C driver information
+ * if an AP device is removed.
+ */
+static void zcrypt_pcixcc_remove(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev = ap_dev->private;
+
+ zcrypt_device_unregister(zdev);
+}
+
+int __init zcrypt_pcixcc_init(void)
+{
+ return ap_driver_register(&zcrypt_pcixcc_driver, THIS_MODULE, "pcixcc");
+}
+
+void zcrypt_pcixcc_exit(void)
+{
+ ap_driver_unregister(&zcrypt_pcixcc_driver);
+}
+
+#ifndef CONFIG_ZCRYPT_MONOLITHIC
+module_init(zcrypt_pcixcc_init);
+module_exit(zcrypt_pcixcc_exit);
+#endif
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.h b/drivers/s390/crypto/zcrypt_pcixcc.h
new file mode 100644
index 000000000000..a78ff307fd19
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_pcixcc.h
@@ -0,0 +1,79 @@
+/*
+ * linux/drivers/s390/crypto/zcrypt_pcixcc.h
+ *
+ * zcrypt 2.1.0
+ *
+ * Copyright (C) 2001, 2006 IBM Corporation
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *
+ * 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.
+ */
+
+#ifndef _ZCRYPT_PCIXCC_H_
+#define _ZCRYPT_PCIXCC_H_
+
+/**
+ * CPRBX
+ * Note that all shorts and ints are big-endian.
+ * All pointer fields are 16 bytes long, and mean nothing.
+ *
+ * A request CPRB is followed by a request_parameter_block.
+ *
+ * The request (or reply) parameter block is organized thus:
+ * function code
+ * VUD block
+ * key block
+ */
+struct CPRBX {
+ unsigned short cprb_len; /* CPRB length 220 */
+ unsigned char cprb_ver_id; /* CPRB version id. 0x02 */
+ unsigned char pad_000[3]; /* Alignment pad bytes */
+ unsigned char func_id[2]; /* function id 0x5432 */
+ unsigned char cprb_flags[4]; /* Flags */
+ unsigned int req_parml; /* request parameter buffer len */
+ unsigned int req_datal; /* request data buffer */
+ unsigned int rpl_msgbl; /* reply message block length */
+ unsigned int rpld_parml; /* replied parameter block len */
+ unsigned int rpl_datal; /* reply data block len */
+ unsigned int rpld_datal; /* replied data block len */
+ unsigned int req_extbl; /* request extension block len */
+ unsigned char pad_001[4]; /* reserved */
+ unsigned int rpld_extbl; /* replied extension block len */
+ unsigned char req_parmb[16]; /* request parm block 'address' */
+ unsigned char req_datab[16]; /* request data block 'address' */
+ unsigned char rpl_parmb[16]; /* reply parm block 'address' */
+ unsigned char rpl_datab[16]; /* reply data block 'address' */
+ unsigned char req_extb[16]; /* request extension block 'addr'*/
+ unsigned char rpl_extb[16]; /* reply extension block 'addres'*/
+ unsigned short ccp_rtcode; /* server return code */
+ unsigned short ccp_rscode; /* server reason code */
+ unsigned int mac_data_len; /* Mac Data Length */
+ unsigned char logon_id[8]; /* Logon Identifier */
+ unsigned char mac_value[8]; /* Mac Value */
+ unsigned char mac_content_flgs;/* Mac content flag byte */
+ unsigned char pad_002; /* Alignment */
+ unsigned short domain; /* Domain */
+ unsigned char pad_003[12]; /* Domain masks */
+ unsigned char pad_004[36]; /* reserved */
+} __attribute__((packed));
+
+int zcrypt_pcixcc_init(void);
+void zcrypt_pcixcc_exit(void);
+
+#endif /* _ZCRYPT_PCIXCC_H_ */
diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig
index 548854754921..1a93fa684e9f 100644
--- a/drivers/s390/net/Kconfig
+++ b/drivers/s390/net/Kconfig
@@ -92,15 +92,6 @@ config QETH_VLAN
If CONFIG_QETH is switched on, this option will include IEEE
802.1q VLAN support in the qeth device driver.
-config QETH_PERF_STATS
- bool "Performance statistics in /proc"
- depends on QETH
- help
- When switched on, this option will add a file in the proc-fs
- (/proc/qeth_perf_stats) containing performance statistics. It
- may slightly impact performance, so this is only recommended for
- internal tuning of the device driver.
-
config CCWGROUP
tristate
default (LCS || CTC || QETH)
diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile
index 6775a837d646..4777e36a922f 100644
--- a/drivers/s390/net/Makefile
+++ b/drivers/s390/net/Makefile
@@ -10,7 +10,6 @@ obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
obj-$(CONFIG_CTC) += ctc.o fsm.o cu3088.o
obj-$(CONFIG_LCS) += lcs.o cu3088.o
obj-$(CONFIG_CLAW) += claw.o cu3088.o
-obj-$(CONFIG_MPC) += ctcmpc.o fsm.o cu3088.o
qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o qeth_eddp.o
qeth-$(CONFIG_PROC_FS) += qeth_proc.o
obj-$(CONFIG_QETH) += qeth.o
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index 23d53bf9daf1..95f4e105cb96 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -529,7 +529,7 @@ claw_open(struct net_device *dev)
printk(KERN_INFO "%s:%s Enter \n",dev->name,__FUNCTION__);
#endif
CLAW_DBF_TEXT(4,trace,"open");
- if (!dev | (dev->name[0] == 0x00)) {
+ if (!dev || (dev->name[0] == 0x00)) {
CLAW_DBF_TEXT(2,trace,"BadDev");
printk(KERN_WARNING "claw: Bad device at open failing \n");
return -ENODEV;
diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c
index 20c8eb16f464..3257c22dd79c 100644
--- a/drivers/s390/net/ctcmain.c
+++ b/drivers/s390/net/ctcmain.c
@@ -1714,6 +1714,9 @@ add_channel(struct ccw_device *cdev, enum channel_types type)
kfree(ch);
return 0;
}
+
+ spin_lock_init(&ch->collect_lock);
+
fsm_settimer(ch->fsm, &ch->timer);
skb_queue_head_init(&ch->io_queue);
skb_queue_head_init(&ch->collect_queue);
@@ -2686,9 +2689,17 @@ static struct attribute_group ctc_attr_group = {
static int
ctc_add_attributes(struct device *dev)
{
- device_create_file(dev, &dev_attr_loglevel);
- device_create_file(dev, &dev_attr_stats);
- return 0;
+ int rc;
+
+ rc = device_create_file(dev, &dev_attr_loglevel);
+ if (rc)
+ goto out;
+ rc = device_create_file(dev, &dev_attr_stats);
+ if (!rc)
+ goto out;
+ device_remove_file(dev, &dev_attr_loglevel);
+out:
+ return rc;
}
static void
@@ -2901,7 +2912,12 @@ ctc_new_device(struct ccwgroup_device *cgdev)
goto out;
}
- ctc_add_attributes(&cgdev->dev);
+ if (ctc_add_attributes(&cgdev->dev)) {
+ ctc_netdev_unregister(dev);
+ dev->priv = NULL;
+ ctc_free_netdevice(dev, 1);
+ goto out;
+ }
strlcpy(privptr->fsm->name, dev->name, sizeof (privptr->fsm->name));
diff --git a/drivers/s390/net/fsm.c b/drivers/s390/net/fsm.c
index 7145e2134cf0..2c1db8036b7c 100644
--- a/drivers/s390/net/fsm.c
+++ b/drivers/s390/net/fsm.c
@@ -4,7 +4,6 @@
*/
#include "fsm.h"
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/timer.h>
diff --git a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c
index e0c7deb98831..809dd8d7f47a 100644
--- a/drivers/s390/net/iucv.c
+++ b/drivers/s390/net/iucv.c
@@ -33,7 +33,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/config.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
@@ -336,8 +335,8 @@ do { \
#else
-#define iucv_debug(lvl, fmt, args...)
-#define iucv_dumpit(title, buf, len)
+#define iucv_debug(lvl, fmt, args...) do { } while (0)
+#define iucv_dumpit(title, buf, len) do { } while (0)
#endif
@@ -535,19 +534,15 @@ iucv_add_handler (handler *new)
*
* Returns: return code from CP's IUCV call
*/
-static __inline__ ulong
-b2f0(__u32 code, void *parm)
+static inline ulong b2f0(__u32 code, void *parm)
{
+ register unsigned long reg0 asm ("0");
+ register unsigned long reg1 asm ("1");
iucv_dumpit("iparml before b2f0 call:", parm, sizeof(iucv_param));
- asm volatile (
- "LRA 1,0(%1)\n\t"
- "LR 0,%0\n\t"
- ".long 0xb2f01000"
- :
- : "d" (code), "a" (parm)
- : "0", "1"
- );
+ reg0 = code;
+ reg1 = virt_to_phys(parm);
+ asm volatile(".long 0xb2f01000" : : "d" (reg0), "a" (reg1));
iucv_dumpit("iparml after b2f0 call:", parm, sizeof(iucv_param));
@@ -693,7 +688,7 @@ iucv_retrieve_buffer (void)
iucv_debug(1, "entering");
if (iucv_cpuid != -1) {
smp_call_function_on(iucv_retrieve_buffer_cpuid,
- 0, 0, 1, iucv_cpuid);
+ NULL, 0, 1, iucv_cpuid);
/* Release the cpu reserved by iucv_declare_buffer. */
smp_put_cpu(iucv_cpuid);
iucv_cpuid = -1;
@@ -1249,6 +1244,8 @@ iucv_purge (__u16 pathid, __u32 msgid, __u32 srccls, __u32 *audit)
static int
iucv_query_generic(int want_maxconn)
{
+ register unsigned long reg0 asm ("0");
+ register unsigned long reg1 asm ("1");
iparml_purge *parm = (iparml_purge *)grab_param();
int bufsize, maxconn;
int ccode;
@@ -1257,18 +1254,15 @@ iucv_query_generic(int want_maxconn)
* Call b2f0 and store R0 (max buffer size),
* R1 (max connections) and CC.
*/
- asm volatile (
- "LRA 1,0(%4)\n\t"
- "LR 0,%3\n\t"
- ".long 0xb2f01000\n\t"
- "IPM %0\n\t"
- "SRL %0,28\n\t"
- "ST 0,%1\n\t"
- "ST 1,%2\n\t"
- : "=d" (ccode), "=m" (bufsize), "=m" (maxconn)
- : "d" (QUERY), "a" (parm)
- : "0", "1", "cc"
- );
+ reg0 = QUERY;
+ reg1 = virt_to_phys(parm);
+ asm volatile(
+ " .long 0xb2f01000\n"
+ " ipm %0\n"
+ " srl %0,28\n"
+ : "=d" (ccode), "+d" (reg0), "+d" (reg1) : : "cc");
+ bufsize = reg0;
+ maxconn = reg1;
release_param(parm);
if (ccode)
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index f94419b334f7..16ac68c27a27 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -670,9 +670,8 @@ lcs_ready_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
int index, rc;
LCS_DBF_TEXT(5, trace, "rdybuff");
- if (buffer->state != BUF_STATE_LOCKED &&
- buffer->state != BUF_STATE_PROCESSED)
- BUG();
+ BUG_ON(buffer->state != BUF_STATE_LOCKED &&
+ buffer->state != BUF_STATE_PROCESSED);
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
buffer->state = BUF_STATE_READY;
index = buffer - channel->iob;
@@ -696,8 +695,7 @@ __lcs_processed_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
int index, prev, next;
LCS_DBF_TEXT(5, trace, "prcsbuff");
- if (buffer->state != BUF_STATE_READY)
- BUG();
+ BUG_ON(buffer->state != BUF_STATE_READY);
buffer->state = BUF_STATE_PROCESSED;
index = buffer - channel->iob;
prev = (index - 1) & (LCS_NUM_BUFFS - 1);
@@ -729,9 +727,8 @@ lcs_release_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
unsigned long flags;
LCS_DBF_TEXT(5, trace, "relbuff");
- if (buffer->state != BUF_STATE_LOCKED &&
- buffer->state != BUF_STATE_PROCESSED)
- BUG();
+ BUG_ON(buffer->state != BUF_STATE_LOCKED &&
+ buffer->state != BUF_STATE_PROCESSED);
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
buffer->state = BUF_STATE_EMPTY;
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
@@ -1140,10 +1137,9 @@ list_modified:
}
}
/* re-insert all entries from the failed_list into ipm_list */
- list_for_each_entry_safe(ipm, tmp, &failed_list, list) {
- list_del_init(&ipm->list);
- list_add_tail(&ipm->list, &card->ipm_list);
- }
+ list_for_each_entry_safe(ipm, tmp, &failed_list, list)
+ list_move_tail(&ipm->list, &card->ipm_list);
+
spin_unlock_irqrestore(&card->ipm_lock, flags);
}
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index b452cc1afd55..d7d1cc0a5c8e 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -112,7 +112,12 @@ struct iucv_connection {
/**
* Linked list of all connection structs.
*/
-static struct iucv_connection *iucv_connections;
+struct iucv_connection_struct {
+ struct iucv_connection *iucv_connections;
+ rwlock_t iucv_rwlock;
+};
+
+static struct iucv_connection_struct iucv_conns;
/**
* Representation of event-data for the
@@ -1368,8 +1373,10 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf,
struct net_device *ndev = priv->conn->netdev;
char *p;
char *tmp;
- char username[10];
+ char username[9];
int i;
+ struct iucv_connection **clist = &iucv_conns.iucv_connections;
+ unsigned long flags;
IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
if (count>9) {
@@ -1382,7 +1389,7 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf,
tmp = strsep((char **) &buf, "\n");
for (i=0, p=tmp; i<8 && *p; i++, p++) {
if (isalnum(*p) || (*p == '$'))
- username[i]= *p;
+ username[i]= toupper(*p);
else if (*p == '\n') {
/* trailing lf, grr */
break;
@@ -1395,11 +1402,11 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf,
return -EINVAL;
}
}
- while (i<9)
+ while (i<8)
username[i++] = ' ';
- username[9] = '\0';
+ username[8] = '\0';
- if (memcmp(username, priv->conn->userid, 8)) {
+ if (memcmp(username, priv->conn->userid, 9)) {
/* username changed */
if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
PRINT_WARN(
@@ -1410,6 +1417,19 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf,
return -EBUSY;
}
}
+ read_lock_irqsave(&iucv_conns.iucv_rwlock, flags);
+ while (*clist) {
+ if (!strncmp(username, (*clist)->userid, 9) ||
+ ((*clist)->netdev != ndev))
+ break;
+ clist = &((*clist)->next);
+ }
+ read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
+ if (*clist) {
+ PRINT_WARN("netiucv: Connection to %s already exists\n",
+ username);
+ return -EEXIST;
+ }
memcpy(priv->conn->userid, username, 9);
return count;
@@ -1781,13 +1801,15 @@ netiucv_unregister_device(struct device *dev)
static struct iucv_connection *
netiucv_new_connection(struct net_device *dev, char *username)
{
- struct iucv_connection **clist = &iucv_connections;
+ unsigned long flags;
+ struct iucv_connection **clist = &iucv_conns.iucv_connections;
struct iucv_connection *conn =
kzalloc(sizeof(struct iucv_connection), GFP_KERNEL);
if (conn) {
skb_queue_head_init(&conn->collect_queue);
skb_queue_head_init(&conn->commit_queue);
+ spin_lock_init(&conn->collect_lock);
conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
conn->netdev = dev;
@@ -1822,8 +1844,10 @@ netiucv_new_connection(struct net_device *dev, char *username)
fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
}
+ write_lock_irqsave(&iucv_conns.iucv_rwlock, flags);
conn->next = *clist;
*clist = conn;
+ write_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
}
return conn;
}
@@ -1835,14 +1859,17 @@ netiucv_new_connection(struct net_device *dev, char *username)
static void
netiucv_remove_connection(struct iucv_connection *conn)
{
- struct iucv_connection **clist = &iucv_connections;
+ struct iucv_connection **clist = &iucv_conns.iucv_connections;
+ unsigned long flags;
IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
if (conn == NULL)
return;
+ write_lock_irqsave(&iucv_conns.iucv_rwlock, flags);
while (*clist) {
if (*clist == conn) {
*clist = conn->next;
+ write_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
if (conn->handle) {
iucv_unregister_program(conn->handle);
conn->handle = NULL;
@@ -1855,6 +1882,7 @@ netiucv_remove_connection(struct iucv_connection *conn)
}
clist = &((*clist)->next);
}
+ write_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
}
/**
@@ -1947,9 +1975,11 @@ static ssize_t
conn_write(struct device_driver *drv, const char *buf, size_t count)
{
char *p;
- char username[10];
+ char username[9];
int i, ret;
struct net_device *dev;
+ struct iucv_connection **clist = &iucv_conns.iucv_connections;
+ unsigned long flags;
IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
if (count>9) {
@@ -1960,7 +1990,7 @@ conn_write(struct device_driver *drv, const char *buf, size_t count)
for (i=0, p=(char *)buf; i<8 && *p; i++, p++) {
if (isalnum(*p) || (*p == '$'))
- username[i]= *p;
+ username[i]= toupper(*p);
else if (*p == '\n') {
/* trailing lf, grr */
break;
@@ -1971,9 +2001,22 @@ conn_write(struct device_driver *drv, const char *buf, size_t count)
return -EINVAL;
}
}
- while (i<9)
+ while (i<8)
username[i++] = ' ';
- username[9] = '\0';
+ username[8] = '\0';
+
+ read_lock_irqsave(&iucv_conns.iucv_rwlock, flags);
+ while (*clist) {
+ if (!strncmp(username, (*clist)->userid, 9))
+ break;
+ clist = &((*clist)->next);
+ }
+ read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
+ if (*clist) {
+ PRINT_WARN("netiucv: Connection to %s already exists\n",
+ username);
+ return -EEXIST;
+ }
dev = netiucv_init_netdevice(username);
if (!dev) {
PRINT_WARN(
@@ -2015,7 +2058,8 @@ DRIVER_ATTR(connection, 0200, NULL, conn_write);
static ssize_t
remove_write (struct device_driver *drv, const char *buf, size_t count)
{
- struct iucv_connection **clist = &iucv_connections;
+ struct iucv_connection **clist = &iucv_conns.iucv_connections;
+ unsigned long flags;
struct net_device *ndev;
struct netiucv_priv *priv;
struct device *dev;
@@ -2026,10 +2070,10 @@ remove_write (struct device_driver *drv, const char *buf, size_t count)
IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
if (count >= IFNAMSIZ)
- count = IFNAMSIZ-1;
+ count = IFNAMSIZ - 1;;
for (i=0, p=(char *)buf; i<count && *p; i++, p++) {
- if ((*p == '\n') | (*p == ' ')) {
+ if ((*p == '\n') || (*p == ' ')) {
/* trailing lf, grr */
break;
} else {
@@ -2038,6 +2082,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count)
}
name[i] = '\0';
+ read_lock_irqsave(&iucv_conns.iucv_rwlock, flags);
while (*clist) {
ndev = (*clist)->netdev;
priv = (struct netiucv_priv*)ndev->priv;
@@ -2047,6 +2092,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count)
clist = &((*clist)->next);
continue;
}
+ read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
PRINT_WARN(
"netiucv: net device %s active with peer %s\n",
@@ -2060,6 +2106,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count)
netiucv_unregister_device(dev);
return count;
}
+ read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
PRINT_WARN("netiucv: net device %s unknown\n", name);
IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
return -EINVAL;
@@ -2077,8 +2124,8 @@ static void __exit
netiucv_exit(void)
{
IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
- while (iucv_connections) {
- struct net_device *ndev = iucv_connections->netdev;
+ while (iucv_conns.iucv_connections) {
+ struct net_device *ndev = iucv_conns.iucv_connections->netdev;
struct netiucv_priv *priv = (struct netiucv_priv*)ndev->priv;
struct device *dev = priv->dev;
@@ -2120,6 +2167,7 @@ netiucv_init(void)
if (!ret) {
ret = driver_create_file(&netiucv_driver, &driver_attr_remove);
netiucv_banner();
+ rwlock_init(&iucv_conns.iucv_rwlock);
} else {
PRINT_ERR("NETIUCV: failed to add driver attribute.\n");
IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_create_file\n", ret);
diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h
index 619f4a0c7160..821383d8cbe7 100644
--- a/drivers/s390/net/qeth.h
+++ b/drivers/s390/net/qeth.h
@@ -176,7 +176,6 @@ extern struct ccwgroup_driver qeth_ccwgroup_driver;
/**
* card stuff
*/
-#ifdef CONFIG_QETH_PERF_STATS
struct qeth_perf_stats {
unsigned int bufs_rec;
unsigned int bufs_sent;
@@ -211,8 +210,10 @@ struct qeth_perf_stats {
unsigned int large_send_cnt;
unsigned int sg_skbs_sent;
unsigned int sg_frags_sent;
+ /* initial values when measuring starts */
+ unsigned long initial_rx_packets;
+ unsigned long initial_tx_packets;
};
-#endif /* CONFIG_QETH_PERF_STATS */
/* Routing stuff */
struct qeth_routing_info {
@@ -462,6 +463,7 @@ enum qeth_qdio_info_states {
QETH_QDIO_UNINITIALIZED,
QETH_QDIO_ALLOCATED,
QETH_QDIO_ESTABLISHED,
+ QETH_QDIO_CLEANING
};
struct qeth_buffer_pool_entry {
@@ -536,7 +538,7 @@ struct qeth_qdio_out_q {
} __attribute__ ((aligned(256)));
struct qeth_qdio_info {
- volatile enum qeth_qdio_info_states state;
+ atomic_t state;
/* input */
struct qeth_qdio_q *in_q;
struct qeth_qdio_buffer_pool in_buf_pool;
@@ -767,6 +769,7 @@ struct qeth_card_options {
int fake_ll;
int layer2;
enum qeth_large_send_types large_send;
+ int performance_stats;
};
/*
@@ -819,9 +822,7 @@ struct qeth_card {
struct list_head cmd_waiter_list;
/* QDIO buffer handling */
struct qeth_qdio_info qdio;
-#ifdef CONFIG_QETH_PERF_STATS
struct qeth_perf_stats perf_stats;
-#endif /* CONFIG_QETH_PERF_STATS */
int use_hard_stop;
int (*orig_hard_header)(struct sk_buff *,struct net_device *,
unsigned short,void *,void *,unsigned);
@@ -859,23 +860,18 @@ qeth_get_ipa_adp_type(enum qeth_link_types link_type)
}
}
-static inline int
-qeth_realloc_headroom(struct qeth_card *card, struct sk_buff **skb, int size)
+static inline struct sk_buff *
+qeth_realloc_headroom(struct qeth_card *card, struct sk_buff *skb, int size)
{
- struct sk_buff *new_skb = NULL;
-
- if (skb_headroom(*skb) < size){
- new_skb = skb_realloc_headroom(*skb, size);
- if (!new_skb) {
- PRINT_ERR("qeth_prepare_skb: could "
- "not realloc headroom for qeth_hdr "
- "on interface %s", QETH_CARD_IFNAME(card));
- return -ENOMEM;
- }
- kfree_skb(*skb);
- *skb = new_skb;
- }
- return 0;
+ struct sk_buff *new_skb = skb;
+
+ if (skb_headroom(skb) >= size)
+ return skb;
+ new_skb = skb_realloc_headroom(skb, size);
+ if (!new_skb)
+ PRINT_ERR("Could not realloc headroom for qeth_hdr "
+ "on interface %s", QETH_CARD_IFNAME(card));
+ return new_skb;
}
static inline struct sk_buff *
@@ -885,16 +881,15 @@ qeth_pskb_unshare(struct sk_buff *skb, int pri)
if (!skb_cloned(skb))
return skb;
nskb = skb_copy(skb, pri);
- kfree_skb(skb); /* free our shared copy */
return nskb;
}
static inline void *
-qeth_push_skb(struct qeth_card *card, struct sk_buff **skb, int size)
+qeth_push_skb(struct qeth_card *card, struct sk_buff *skb, int size)
{
void *hdr;
- hdr = (void *) skb_push(*skb, size);
+ hdr = (void *) skb_push(skb, size);
/*
* sanity check, the Linux memory allocation scheme should
* never present us cases like this one (the qdio header size plus
@@ -903,8 +898,7 @@ qeth_push_skb(struct qeth_card *card, struct sk_buff **skb, int size)
if ((((unsigned long) hdr) & (~(PAGE_SIZE - 1))) !=
(((unsigned long) hdr + size +
QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
- PRINT_ERR("qeth_prepare_skb: misaligned "
- "packet on interface %s. Discarded.",
+ PRINT_ERR("Misaligned packet on interface %s. Discarded.",
QETH_CARD_IFNAME(card));
return NULL;
}
@@ -1056,13 +1050,11 @@ qeth_get_arphdr_type(int cardtype, int linktype)
}
}
-#ifdef CONFIG_QETH_PERF_STATS
static inline int
qeth_get_micros(void)
{
return (int) (get_clock() >> 12);
}
-#endif
static inline int
qeth_get_qdio_q_format(struct qeth_card *card)
@@ -1096,10 +1088,11 @@ qeth_string_to_ipaddr4(const char *buf, __u8 *addr)
{
int count = 0, rc = 0;
int in[4];
+ char c;
- rc = sscanf(buf, "%d.%d.%d.%d%n",
- &in[0], &in[1], &in[2], &in[3], &count);
- if (rc != 4 || count<=0)
+ rc = sscanf(buf, "%u.%u.%u.%u%c",
+ &in[0], &in[1], &in[2], &in[3], &c);
+ if (rc != 4 && (rc != 5 || c != '\n'))
return -EINVAL;
for (count = 0; count < 4; count++) {
if (in[count] > 255)
@@ -1123,24 +1116,28 @@ qeth_ipaddr6_to_string(const __u8 *addr, char *buf)
static inline int
qeth_string_to_ipaddr6(const char *buf, __u8 *addr)
{
- char *end, *start;
+ const char *end, *end_tmp, *start;
__u16 *in;
char num[5];
int num2, cnt, out, found, save_cnt;
unsigned short in_tmp[8] = {0, };
cnt = out = found = save_cnt = num2 = 0;
- end = start = (char *) buf;
+ end = start = buf;
in = (__u16 *) addr;
memset(in, 0, 16);
- while (end) {
- end = strchr(end,':');
+ while (*end) {
+ end = strchr(start,':');
if (end == NULL) {
- end = (char *)buf + (strlen(buf));
- out = 1;
+ end = buf + strlen(buf);
+ if ((end_tmp = strchr(start, '\n')) != NULL)
+ end = end_tmp;
+ out = 1;
}
if ((end - start)) {
memset(num, 0, 5);
+ if ((end - start) > 4)
+ return -EINVAL;
memcpy(num, start, end - start);
if (!qeth_isxdigit(num))
return -EINVAL;
@@ -1158,6 +1155,8 @@ qeth_string_to_ipaddr6(const char *buf, __u8 *addr)
}
start = ++end;
}
+ if (cnt + save_cnt > 8)
+ return -EINVAL;
cnt = 7;
while (save_cnt)
in[cnt--] = in_tmp[--save_cnt];
diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c
index 38aad8321456..a363721cf28d 100644
--- a/drivers/s390/net/qeth_eddp.c
+++ b/drivers/s390/net/qeth_eddp.c
@@ -8,7 +8,6 @@
* Author(s): Thomas Spatzier <tspat@de.ibm.com>
*
*/
-#include <linux/config.h>
#include <linux/errno.h>
#include <linux/ip.h>
#include <linux/inetdevice.h>
@@ -180,9 +179,8 @@ out_check:
flush_cnt++;
}
} else {
-#ifdef CONFIG_QETH_PERF_STATS
- queue->card->perf_stats.skbs_sent_pack++;
-#endif
+ if (queue->card->options.performance_stats)
+ queue->card->perf_stats.skbs_sent_pack++;
QETH_DBF_TEXT(trace, 6, "fillbfpa");
if (buf->next_element_to_fill >=
QETH_MAX_BUFFER_ELEMENTS(queue->card)) {
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c
index 56009d768326..8364d5475ac7 100644
--- a/drivers/s390/net/qeth_main.c
+++ b/drivers/s390/net/qeth_main.c
@@ -27,7 +27,6 @@
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
@@ -85,6 +84,8 @@ static debug_info_t *qeth_dbf_qerr = NULL;
DEFINE_PER_CPU(char[256], qeth_dbf_txt_buf);
+static struct lock_class_key qdio_out_skb_queue_key;
+
/**
* some more definitions and declarations
*/
@@ -1072,6 +1073,7 @@ qeth_set_intial_options(struct qeth_card *card)
card->options.layer2 = 1;
else
card->options.layer2 = 0;
+ card->options.performance_stats = 1;
}
/**
@@ -1707,6 +1709,7 @@ qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
"IP address reset.\n",
QETH_CARD_IFNAME(card),
card->info.chpid);
+ netif_carrier_on(card->dev);
qeth_schedule_recovery(card);
return NULL;
case IPA_CMD_MODCCID:
@@ -2463,24 +2466,6 @@ qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
qeth_rebuild_skb_fake_ll_eth(card, skb, hdr);
}
-static inline void
-qeth_rebuild_skb_vlan(struct qeth_card *card, struct sk_buff *skb,
- struct qeth_hdr *hdr)
-{
-#ifdef CONFIG_QETH_VLAN
- u16 *vlan_tag;
-
- if (hdr->hdr.l3.ext_flags &
- (QETH_HDR_EXT_VLAN_FRAME | QETH_HDR_EXT_INCLUDE_VLAN_TAG)) {
- vlan_tag = (u16 *) skb_push(skb, VLAN_HLEN);
- *vlan_tag = (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_VLAN_FRAME)?
- hdr->hdr.l3.vlan_id : *((u16 *)&hdr->hdr.l3.dest_addr[12]);
- *(vlan_tag + 1) = skb->protocol;
- skb->protocol = __constant_htons(ETH_P_8021Q);
- }
-#endif /* CONFIG_QETH_VLAN */
-}
-
static inline __u16
qeth_layer2_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
struct qeth_hdr *hdr)
@@ -2509,15 +2494,16 @@ qeth_layer2_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
return vlan_id;
}
-static inline void
+static inline __u16
qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
struct qeth_hdr *hdr)
{
+ unsigned short vlan_id = 0;
#ifdef CONFIG_QETH_IPV6
if (hdr->hdr.l3.flags & QETH_HDR_PASSTHRU) {
skb->pkt_type = PACKET_HOST;
skb->protocol = qeth_type_trans(skb, card->dev);
- return;
+ return 0;
}
#endif /* CONFIG_QETH_IPV6 */
skb->protocol = htons((hdr->hdr.l3.flags & QETH_HDR_IPV6)? ETH_P_IPV6 :
@@ -2539,7 +2525,13 @@ qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
default:
skb->pkt_type = PACKET_HOST;
}
- qeth_rebuild_skb_vlan(card, skb, hdr);
+
+ if (hdr->hdr.l3.ext_flags &
+ (QETH_HDR_EXT_VLAN_FRAME | QETH_HDR_EXT_INCLUDE_VLAN_TAG)) {
+ vlan_id = (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_VLAN_FRAME)?
+ hdr->hdr.l3.vlan_id : *((u16 *)&hdr->hdr.l3.dest_addr[12]);
+ }
+
if (card->options.fake_ll)
qeth_rebuild_skb_fake_ll(card, skb, hdr);
else
@@ -2555,6 +2547,7 @@ qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
else
skb->ip_summed = SW_CHECKSUMMING;
}
+ return vlan_id;
}
static inline void
@@ -2567,20 +2560,20 @@ qeth_process_inbound_buffer(struct qeth_card *card,
int offset;
int rxrc;
__u16 vlan_tag = 0;
+ __u16 *vlan_addr;
/* get first element of current buffer */
element = (struct qdio_buffer_element *)&buf->buffer->element[0];
offset = 0;
-#ifdef CONFIG_QETH_PERF_STATS
- card->perf_stats.bufs_rec++;
-#endif
+ if (card->options.performance_stats)
+ card->perf_stats.bufs_rec++;
while((skb = qeth_get_next_skb(card, buf->buffer, &element,
&offset, &hdr))) {
skb->dev = card->dev;
if (hdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2)
vlan_tag = qeth_layer2_rebuild_skb(card, skb, hdr);
else if (hdr->hdr.l3.id == QETH_HEADER_TYPE_LAYER3)
- qeth_rebuild_skb(card, skb, hdr);
+ vlan_tag = qeth_rebuild_skb(card, skb, hdr);
else { /*in case of OSN*/
skb_push(skb, sizeof(struct qeth_hdr));
memcpy(skb->data, hdr, sizeof(struct qeth_hdr));
@@ -2590,14 +2583,19 @@ qeth_process_inbound_buffer(struct qeth_card *card,
dev_kfree_skb_any(skb);
continue;
}
+ if (card->info.type == QETH_CARD_TYPE_OSN)
+ rxrc = card->osn_info.data_cb(skb);
+ else
#ifdef CONFIG_QETH_VLAN
if (vlan_tag)
- vlan_hwaccel_rx(skb, card->vlangrp, vlan_tag);
+ if (card->vlangrp)
+ vlan_hwaccel_rx(skb, card->vlangrp, vlan_tag);
+ else {
+ dev_kfree_skb_any(skb);
+ continue;
+ }
else
#endif
- if (card->info.type == QETH_CARD_TYPE_OSN)
- rxrc = card->osn_info.data_cb(skb);
- else
rxrc = netif_rx(skb);
card->dev->last_rx = jiffies;
card->stats.rx_packets++;
@@ -2625,7 +2623,7 @@ qeth_init_input_buffer(struct qeth_card *card, struct qeth_qdio_buffer *buf)
{
struct qeth_buffer_pool_entry *pool_entry;
int i;
-
+
pool_entry = qeth_get_buffer_pool_entry(card);
/*
* since the buffer is accessed only from the input_tasklet
@@ -2699,17 +2697,18 @@ qeth_queue_input_buffer(struct qeth_card *card, int index)
* 'index') un-requeued -> this buffer is the first buffer that
* will be requeued the next time
*/
-#ifdef CONFIG_QETH_PERF_STATS
- card->perf_stats.inbound_do_qdio_cnt++;
- card->perf_stats.inbound_do_qdio_start_time = qeth_get_micros();
-#endif
+ if (card->options.performance_stats) {
+ card->perf_stats.inbound_do_qdio_cnt++;
+ 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);
-#ifdef CONFIG_QETH_PERF_STATS
- card->perf_stats.inbound_do_qdio_time += qeth_get_micros() -
- card->perf_stats.inbound_do_qdio_start_time;
-#endif
+ if (card->options.performance_stats)
+ card->perf_stats.inbound_do_qdio_time +=
+ qeth_get_micros() -
+ card->perf_stats.inbound_do_qdio_start_time;
if (rc){
PRINT_WARN("qeth_queue_input_buffer's do_QDIO "
"return %i (device %s).\n",
@@ -2745,10 +2744,10 @@ qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status,
QETH_DBF_TEXT(trace, 6, "qdinput");
card = (struct qeth_card *) card_ptr;
net_dev = card->dev;
-#ifdef CONFIG_QETH_PERF_STATS
- card->perf_stats.inbound_cnt++;
- card->perf_stats.inbound_start_time = qeth_get_micros();
-#endif
+ if (card->options.performance_stats) {
+ 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");
@@ -2770,10 +2769,9 @@ qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status,
qeth_put_buffer_pool_entry(card, buffer->pool_entry);
qeth_queue_input_buffer(card, index);
}
-#ifdef CONFIG_QETH_PERF_STATS
- card->perf_stats.inbound_time += qeth_get_micros() -
- card->perf_stats.inbound_start_time;
-#endif
+ if (card->options.performance_stats)
+ card->perf_stats.inbound_time += qeth_get_micros() -
+ card->perf_stats.inbound_start_time;
}
static inline int
@@ -2863,10 +2861,11 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
}
queue->card->dev->trans_start = jiffies;
-#ifdef CONFIG_QETH_PERF_STATS
- queue->card->perf_stats.outbound_do_qdio_cnt++;
- queue->card->perf_stats.outbound_do_qdio_start_time = qeth_get_micros();
-#endif
+ if (queue->card->options.performance_stats) {
+ queue->card->perf_stats.outbound_do_qdio_cnt++;
+ queue->card->perf_stats.outbound_do_qdio_start_time =
+ qeth_get_micros();
+ }
if (under_int)
rc = do_QDIO(CARD_DDEV(queue->card),
QDIO_FLAG_SYNC_OUTPUT | QDIO_FLAG_UNDER_INTERRUPT,
@@ -2874,10 +2873,10 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
else
rc = do_QDIO(CARD_DDEV(queue->card), QDIO_FLAG_SYNC_OUTPUT,
queue->queue_no, index, count, NULL);
-#ifdef CONFIG_QETH_PERF_STATS
- queue->card->perf_stats.outbound_do_qdio_time += qeth_get_micros() -
- queue->card->perf_stats.outbound_do_qdio_start_time;
-#endif
+ if (queue->card->options.performance_stats)
+ queue->card->perf_stats.outbound_do_qdio_time +=
+ qeth_get_micros() -
+ queue->card->perf_stats.outbound_do_qdio_start_time;
if (rc){
QETH_DBF_TEXT(trace, 2, "flushbuf");
QETH_DBF_TEXT_(trace, 2, " err%d", rc);
@@ -2889,9 +2888,8 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
return;
}
atomic_add(count, &queue->used_buffers);
-#ifdef CONFIG_QETH_PERF_STATS
- queue->card->perf_stats.bufs_sent += count;
-#endif
+ if (queue->card->options.performance_stats)
+ queue->card->perf_stats.bufs_sent += count;
}
/*
@@ -2906,9 +2904,8 @@ qeth_switch_to_packing_if_needed(struct qeth_qdio_out_q *queue)
>= QETH_HIGH_WATERMARK_PACK){
/* switch non-PACKING -> PACKING */
QETH_DBF_TEXT(trace, 6, "np->pack");
-#ifdef CONFIG_QETH_PERF_STATS
- queue->card->perf_stats.sc_dp_p++;
-#endif
+ if (queue->card->options.performance_stats)
+ queue->card->perf_stats.sc_dp_p++;
queue->do_pack = 1;
}
}
@@ -2931,9 +2928,8 @@ qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue)
<= QETH_LOW_WATERMARK_PACK) {
/* switch PACKING -> non-PACKING */
QETH_DBF_TEXT(trace, 6, "pack->np");
-#ifdef CONFIG_QETH_PERF_STATS
- queue->card->perf_stats.sc_p_dp++;
-#endif
+ if (queue->card->options.performance_stats)
+ queue->card->perf_stats.sc_p_dp++;
queue->do_pack = 0;
/* flush packing buffers */
buffer = &queue->bufs[queue->next_buf_to_fill];
@@ -2945,7 +2941,7 @@ qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue)
queue->next_buf_to_fill =
(queue->next_buf_to_fill + 1) %
QDIO_MAX_BUFFERS_PER_Q;
- }
+ }
}
}
return flush_count;
@@ -3001,11 +2997,10 @@ qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
!atomic_read(&queue->set_pci_flags_count))
flush_cnt +=
qeth_flush_buffers_on_no_pci(queue);
-#ifdef CONFIG_QETH_PERF_STATS
- if (q_was_packing)
+ if (queue->card->options.performance_stats &&
+ q_was_packing)
queue->card->perf_stats.bufs_sent_pack +=
flush_cnt;
-#endif
if (flush_cnt)
qeth_flush_buffers(queue, 1, index, flush_cnt);
atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
@@ -3035,10 +3030,11 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
return;
}
}
-#ifdef CONFIG_QETH_PERF_STATS
- card->perf_stats.outbound_handler_cnt++;
- card->perf_stats.outbound_handler_start_time = qeth_get_micros();
-#endif
+ if (card->options.performance_stats) {
+ card->perf_stats.outbound_handler_cnt++;
+ card->perf_stats.outbound_handler_start_time =
+ qeth_get_micros();
+ }
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 */
@@ -3057,10 +3053,9 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
qeth_check_outbound_queue(queue);
netif_wake_queue(queue->card->dev);
-#ifdef CONFIG_QETH_PERF_STATS
- card->perf_stats.outbound_handler_time += qeth_get_micros() -
- card->perf_stats.outbound_handler_start_time;
-#endif
+ if (card->options.performance_stats)
+ card->perf_stats.outbound_handler_time += qeth_get_micros() -
+ card->perf_stats.outbound_handler_start_time;
}
static void
@@ -3184,13 +3179,14 @@ qeth_alloc_qdio_buffers(struct qeth_card *card)
QETH_DBF_TEXT(setup, 2, "allcqdbf");
- if (card->qdio.state == QETH_QDIO_ALLOCATED)
+ if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED,
+ QETH_QDIO_ALLOCATED) != QETH_QDIO_UNINITIALIZED)
return 0;
card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q),
GFP_KERNEL|GFP_DMA);
if (!card->qdio.in_q)
- return - ENOMEM;
+ goto out_nomem;
QETH_DBF_TEXT(setup, 2, "inq");
QETH_DBF_HEX(setup, 2, &card->qdio.in_q, sizeof(void *));
memset(card->qdio.in_q, 0, sizeof(struct qeth_qdio_q));
@@ -3199,27 +3195,19 @@ qeth_alloc_qdio_buffers(struct qeth_card *card)
card->qdio.in_q->bufs[i].buffer =
&card->qdio.in_q->qdio_bufs[i];
/* inbound buffer pool */
- if (qeth_alloc_buffer_pool(card)){
- kfree(card->qdio.in_q);
- return -ENOMEM;
- }
+ if (qeth_alloc_buffer_pool(card))
+ goto out_freeinq;
/* outbound */
card->qdio.out_qs =
kmalloc(card->qdio.no_out_queues *
sizeof(struct qeth_qdio_out_q *), GFP_KERNEL);
- if (!card->qdio.out_qs){
- qeth_free_buffer_pool(card);
- return -ENOMEM;
- }
- for (i = 0; i < card->qdio.no_out_queues; ++i){
+ if (!card->qdio.out_qs)
+ goto out_freepool;
+ for (i = 0; i < card->qdio.no_out_queues; ++i) {
card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q),
GFP_KERNEL|GFP_DMA);
- if (!card->qdio.out_qs[i]){
- while (i > 0)
- kfree(card->qdio.out_qs[--i]);
- kfree(card->qdio.out_qs);
- return -ENOMEM;
- }
+ if (!card->qdio.out_qs[i])
+ goto out_freeoutq;
QETH_DBF_TEXT_(setup, 2, "outq %i", i);
QETH_DBF_HEX(setup, 2, &card->qdio.out_qs[i], sizeof(void *));
memset(card->qdio.out_qs[i], 0, sizeof(struct qeth_qdio_out_q));
@@ -3230,11 +3218,25 @@ qeth_alloc_qdio_buffers(struct qeth_card *card)
&card->qdio.out_qs[i]->qdio_bufs[j];
skb_queue_head_init(&card->qdio.out_qs[i]->bufs[j].
skb_list);
+ lockdep_set_class(
+ &card->qdio.out_qs[i]->bufs[j].skb_list.lock,
+ &qdio_out_skb_queue_key);
INIT_LIST_HEAD(&card->qdio.out_qs[i]->bufs[j].ctx_list);
}
}
- card->qdio.state = QETH_QDIO_ALLOCATED;
return 0;
+
+out_freeoutq:
+ while (i > 0)
+ kfree(card->qdio.out_qs[--i]);
+ kfree(card->qdio.out_qs);
+out_freepool:
+ qeth_free_buffer_pool(card);
+out_freeinq:
+ kfree(card->qdio.in_q);
+out_nomem:
+ atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED);
+ return -ENOMEM;
}
static void
@@ -3243,7 +3245,8 @@ qeth_free_qdio_buffers(struct qeth_card *card)
int i, j;
QETH_DBF_TEXT(trace, 2, "freeqdbf");
- if (card->qdio.state == QETH_QDIO_UNINITIALIZED)
+ if (atomic_swap(&card->qdio.state, QETH_QDIO_UNINITIALIZED) ==
+ QETH_QDIO_UNINITIALIZED)
return;
kfree(card->qdio.in_q);
/* inbound buffer pool */
@@ -3256,7 +3259,6 @@ qeth_free_qdio_buffers(struct qeth_card *card)
kfree(card->qdio.out_qs[i]);
}
kfree(card->qdio.out_qs);
- card->qdio.state = QETH_QDIO_UNINITIALIZED;
}
static void
@@ -3278,7 +3280,7 @@ static void
qeth_init_qdio_info(struct qeth_card *card)
{
QETH_DBF_TEXT(setup, 4, "intqdinf");
- card->qdio.state = QETH_QDIO_UNINITIALIZED;
+ atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED);
/* inbound */
card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT;
card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT;
@@ -3341,7 +3343,7 @@ qeth_qdio_establish(struct qeth_card *card)
struct qdio_buffer **in_sbal_ptrs;
struct qdio_buffer **out_sbal_ptrs;
int i, j, k;
- int rc;
+ int rc = 0;
QETH_DBF_TEXT(setup, 2, "qdioest");
@@ -3400,8 +3402,10 @@ qeth_qdio_establish(struct qeth_card *card)
init_data.input_sbal_addr_array = (void **) in_sbal_ptrs;
init_data.output_sbal_addr_array = (void **) out_sbal_ptrs;
- if (!(rc = qdio_initialize(&init_data)))
- card->qdio.state = QETH_QDIO_ESTABLISHED;
+ if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED,
+ QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED)
+ if ((rc = qdio_initialize(&init_data)))
+ atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED);
kfree(out_sbal_ptrs);
kfree(in_sbal_ptrs);
@@ -3517,13 +3521,20 @@ qeth_qdio_clear_card(struct qeth_card *card, int use_halt)
int rc = 0;
QETH_DBF_TEXT(trace,3,"qdioclr");
- if (card->qdio.state == QETH_QDIO_ESTABLISHED){
+ switch (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ESTABLISHED,
+ QETH_QDIO_CLEANING)) {
+ case QETH_QDIO_ESTABLISHED:
if ((rc = qdio_cleanup(CARD_DDEV(card),
- (card->info.type == QETH_CARD_TYPE_IQD) ?
- QDIO_FLAG_CLEANUP_USING_HALT :
- QDIO_FLAG_CLEANUP_USING_CLEAR)))
+ (card->info.type == QETH_CARD_TYPE_IQD) ?
+ QDIO_FLAG_CLEANUP_USING_HALT :
+ QDIO_FLAG_CLEANUP_USING_CLEAR)))
QETH_DBF_TEXT_(trace, 3, "1err%d", rc);
- card->qdio.state = QETH_QDIO_ALLOCATED;
+ atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED);
+ break;
+ case QETH_QDIO_CLEANING:
+ return rc;
+ default:
+ break;
}
if ((rc = qeth_clear_halt_card(card, use_halt)))
QETH_DBF_TEXT_(trace, 3, "2err%d", rc);
@@ -3683,10 +3694,10 @@ qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* return OK; otherwise ksoftirqd goes to 100% */
return NETDEV_TX_OK;
}
-#ifdef CONFIG_QETH_PERF_STATS
- card->perf_stats.outbound_cnt++;
- card->perf_stats.outbound_start_time = qeth_get_micros();
-#endif
+ if (card->options.performance_stats) {
+ card->perf_stats.outbound_cnt++;
+ card->perf_stats.outbound_start_time = qeth_get_micros();
+ }
netif_stop_queue(dev);
if ((rc = qeth_send_packet(card, skb))) {
if (rc == -EBUSY) {
@@ -3700,10 +3711,9 @@ qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
}
netif_wake_queue(dev);
-#ifdef CONFIG_QETH_PERF_STATS
- card->perf_stats.outbound_time += qeth_get_micros() -
- card->perf_stats.outbound_start_time;
-#endif
+ if (card->options.performance_stats)
+ card->perf_stats.outbound_time += qeth_get_micros() -
+ card->perf_stats.outbound_start_time;
return rc;
}
@@ -3918,49 +3928,59 @@ qeth_get_ip_version(struct sk_buff *skb)
}
}
-static inline int
-qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
- struct qeth_hdr **hdr, int ipv)
+static inline struct qeth_hdr *
+__qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb, int ipv)
{
- int rc = 0;
#ifdef CONFIG_QETH_VLAN
u16 *tag;
-#endif
-
- QETH_DBF_TEXT(trace, 6, "prepskb");
- if (card->info.type == QETH_CARD_TYPE_OSN) {
- *hdr = (struct qeth_hdr *)(*skb)->data;
- return rc;
- }
- rc = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr));
- if (rc)
- return rc;
-#ifdef CONFIG_QETH_VLAN
- if (card->vlangrp && vlan_tx_tag_present(*skb) &&
+ if (card->vlangrp && vlan_tx_tag_present(skb) &&
((ipv == 6) || card->options.layer2) ) {
/*
* Move the mac addresses (6 bytes src, 6 bytes dest)
* to the beginning of the new header. We are using three
* memcpys instead of one memmove to save cycles.
*/
- skb_push(*skb, VLAN_HLEN);
- memcpy((*skb)->data, (*skb)->data + 4, 4);
- memcpy((*skb)->data + 4, (*skb)->data + 8, 4);
- memcpy((*skb)->data + 8, (*skb)->data + 12, 4);
- tag = (u16 *)((*skb)->data + 12);
+ skb_push(skb, VLAN_HLEN);
+ memcpy(skb->data, skb->data + 4, 4);
+ memcpy(skb->data + 4, skb->data + 8, 4);
+ memcpy(skb->data + 8, skb->data + 12, 4);
+ tag = (u16 *)(skb->data + 12);
/*
* first two bytes = ETH_P_8021Q (0x8100)
* second two bytes = VLANID
*/
*tag = __constant_htons(ETH_P_8021Q);
- *(tag + 1) = htons(vlan_tx_tag_get(*skb));
+ *(tag + 1) = htons(vlan_tx_tag_get(skb));
}
#endif
- *hdr = (struct qeth_hdr *)
- qeth_push_skb(card, skb, sizeof(struct qeth_hdr));
- if (*hdr == NULL)
- return -EINVAL;
- return 0;
+ return ((struct qeth_hdr *)
+ qeth_push_skb(card, skb, sizeof(struct qeth_hdr)));
+}
+
+static inline void
+__qeth_free_new_skb(struct sk_buff *orig_skb, struct sk_buff *new_skb)
+{
+ if (orig_skb != new_skb)
+ dev_kfree_skb_any(new_skb);
+}
+
+static inline struct sk_buff *
+qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb,
+ struct qeth_hdr **hdr, int ipv)
+{
+ struct sk_buff *new_skb;
+
+ QETH_DBF_TEXT(trace, 6, "prepskb");
+
+ new_skb = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr));
+ if (new_skb == NULL)
+ return NULL;
+ *hdr = __qeth_prepare_skb(card, new_skb, ipv);
+ if (*hdr == NULL) {
+ __qeth_free_new_skb(skb, new_skb);
+ return NULL;
+ }
+ return new_skb;
}
static inline u8
@@ -4202,9 +4222,8 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue,
flush_cnt = 1;
} else {
QETH_DBF_TEXT(trace, 6, "fillbfpa");
-#ifdef CONFIG_QETH_PERF_STATS
- queue->card->perf_stats.skbs_sent_pack++;
-#endif
+ if (queue->card->options.performance_stats)
+ queue->card->perf_stats.skbs_sent_pack++;
if (buf->next_element_to_fill >=
QETH_MAX_BUFFER_ELEMENTS(queue->card)) {
/*
@@ -4241,21 +4260,15 @@ qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue,
* check if buffer is empty to make sure that we do not 'overtake'
* ourselves and try to fill a buffer that is already primed
*/
- if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) {
- card->stats.tx_dropped++;
- atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
- return -EBUSY;
- }
+ if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY)
+ goto out;
if (ctx == NULL)
queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
QDIO_MAX_BUFFERS_PER_Q;
else {
buffers_needed = qeth_eddp_check_buffers_for_context(queue,ctx);
- if (buffers_needed < 0) {
- card->stats.tx_dropped++;
- atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
- return -EBUSY;
- }
+ if (buffers_needed < 0)
+ goto out;
queue->next_buf_to_fill =
(queue->next_buf_to_fill + buffers_needed) %
QDIO_MAX_BUFFERS_PER_Q;
@@ -4270,6 +4283,9 @@ qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue,
qeth_flush_buffers(queue, 0, index, flush_cnt);
}
return 0;
+out:
+ atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
+ return -EBUSY;
}
static inline int
@@ -4295,8 +4311,7 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
* check if buffer is empty to make sure that we do not 'overtake'
* ourselves and try to fill a buffer that is already primed
*/
- if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){
- card->stats.tx_dropped++;
+ if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) {
atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
return -EBUSY;
}
@@ -4319,7 +4334,6 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
* again */
if (atomic_read(&buffer->state) !=
QETH_QDIO_BUF_EMPTY){
- card->stats.tx_dropped++;
qeth_flush_buffers(queue, 0, start_index, flush_count);
atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
return -EBUSY;
@@ -4330,7 +4344,6 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
* free buffers) to handle eddp context */
if (qeth_eddp_check_buffers_for_context(queue,ctx) < 0){
printk("eddp tx_dropped 1\n");
- card->stats.tx_dropped++;
rc = -EBUSY;
goto out;
}
@@ -4342,7 +4355,6 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
tmp = qeth_eddp_fill_buffer(queue,ctx,queue->next_buf_to_fill);
if (tmp < 0) {
printk("eddp tx_dropped 2\n");
- card->stats.tx_dropped++;
rc = - EBUSY;
goto out;
}
@@ -4376,10 +4388,8 @@ out:
qeth_flush_buffers(queue, 0, start_index, flush_count);
}
/* at this point the queue is UNLOCKED again */
-#ifdef CONFIG_QETH_PERF_STATS
- if (do_pack)
+ if (queue->card->options.performance_stats && do_pack)
queue->card->perf_stats.bufs_sent_pack += flush_count;
-#endif /* CONFIG_QETH_PERF_STATS */
return rc;
}
@@ -4390,21 +4400,21 @@ qeth_get_elements_no(struct qeth_card *card, void *hdr,
{
int elements_needed = 0;
- if (skb_shinfo(skb)->nr_frags > 0) {
+ if (skb_shinfo(skb)->nr_frags > 0)
elements_needed = (skb_shinfo(skb)->nr_frags + 1);
- }
- if (elements_needed == 0 )
+ if (elements_needed == 0)
elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE)
+ skb->len) >> PAGE_SHIFT);
if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)){
- PRINT_ERR("qeth_do_send_packet: invalid size of "
- "IP packet (Number=%d / Length=%d). Discarded.\n",
+ PRINT_ERR("Invalid size of IP packet "
+ "(Number=%d / Length=%d). Discarded.\n",
(elements_needed+elems), skb->len);
return 0;
}
return elements_needed;
}
+
static inline int
qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
{
@@ -4418,108 +4428,110 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
int tx_bytes = skb->len;
unsigned short nr_frags = skb_shinfo(skb)->nr_frags;
unsigned short tso_size = skb_shinfo(skb)->gso_size;
+ struct sk_buff *new_skb, *new_skb2;
int rc;
QETH_DBF_TEXT(trace, 6, "sendpkt");
+ new_skb = skb;
+ if ((card->info.type == QETH_CARD_TYPE_OSN) &&
+ (skb->protocol == htons(ETH_P_IPV6)))
+ return -EPERM;
+ cast_type = qeth_get_cast_type(card, skb);
+ if ((cast_type == RTN_BROADCAST) &&
+ (card->info.broadcast_capable == 0))
+ return -EPERM;
+ queue = card->qdio.out_qs
+ [qeth_get_priority_queue(card, skb, ipv, cast_type)];
if (!card->options.layer2) {
ipv = qeth_get_ip_version(skb);
if ((card->dev->hard_header == qeth_fake_header) && ipv) {
- if ((skb = qeth_pskb_unshare(skb,GFP_ATOMIC)) == NULL) {
- card->stats.tx_dropped++;
- dev_kfree_skb_irq(skb);
- return 0;
- }
+ new_skb = qeth_pskb_unshare(skb, GFP_ATOMIC);
+ if (!new_skb)
+ return -ENOMEM;
if(card->dev->type == ARPHRD_IEEE802_TR){
- skb_pull(skb, QETH_FAKE_LL_LEN_TR);
+ skb_pull(new_skb, QETH_FAKE_LL_LEN_TR);
} else {
- skb_pull(skb, QETH_FAKE_LL_LEN_ETH);
+ skb_pull(new_skb, QETH_FAKE_LL_LEN_ETH);
}
}
}
- if ((card->info.type == QETH_CARD_TYPE_OSN) &&
- (skb->protocol == htons(ETH_P_IPV6))) {
- dev_kfree_skb_any(skb);
- return 0;
- }
- cast_type = qeth_get_cast_type(card, skb);
- if ((cast_type == RTN_BROADCAST) &&
- (card->info.broadcast_capable == 0)){
- card->stats.tx_dropped++;
- card->stats.tx_errors++;
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- }
- queue = card->qdio.out_qs
- [qeth_get_priority_queue(card, skb, ipv, cast_type)];
-
- if (skb_shinfo(skb)->gso_size)
+ if (skb_is_gso(skb))
large_send = card->options.large_send;
-
- /*are we able to do TSO ? If so ,prepare and send it from here */
+ /* check on OSN device*/
+ if (card->info.type == QETH_CARD_TYPE_OSN)
+ hdr = (struct qeth_hdr *)new_skb->data;
+ /*are we able to do TSO ? */
if ((large_send == QETH_LARGE_SEND_TSO) &&
(cast_type == RTN_UNSPEC)) {
- rc = qeth_tso_prepare_packet(card, skb, ipv, cast_type);
+ rc = qeth_tso_prepare_packet(card, new_skb, ipv, cast_type);
if (rc) {
- card->stats.tx_dropped++;
- card->stats.tx_errors++;
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
+ __qeth_free_new_skb(skb, new_skb);
+ return rc;
}
elements_needed++;
- } else {
- if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))) {
- QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc);
- return rc;
+ } else if (card->info.type != QETH_CARD_TYPE_OSN) {
+ new_skb2 = qeth_prepare_skb(card, new_skb, &hdr, ipv);
+ if (!new_skb2) {
+ __qeth_free_new_skb(skb, new_skb);
+ return -EINVAL;
}
- if (card->info.type != QETH_CARD_TYPE_OSN)
- qeth_fill_header(card, hdr, skb, ipv, cast_type);
+ if (new_skb != skb)
+ __qeth_free_new_skb(new_skb2, new_skb);
+ new_skb = new_skb2;
+ qeth_fill_header(card, hdr, new_skb, ipv, cast_type);
}
-
if (large_send == QETH_LARGE_SEND_EDDP) {
- ctx = qeth_eddp_create_context(card, skb, hdr);
+ ctx = qeth_eddp_create_context(card, new_skb, hdr);
if (ctx == NULL) {
+ __qeth_free_new_skb(skb, new_skb);
PRINT_WARN("could not create eddp context\n");
return -EINVAL;
}
} else {
- int elems = qeth_get_elements_no(card,(void*) hdr, skb,
+ int elems = qeth_get_elements_no(card,(void*) hdr, new_skb,
elements_needed);
- if (!elems)
+ if (!elems) {
+ __qeth_free_new_skb(skb, new_skb);
return -EINVAL;
+ }
elements_needed += elems;
}
if (card->info.type != QETH_CARD_TYPE_IQD)
- rc = qeth_do_send_packet(card, queue, skb, hdr,
+ rc = qeth_do_send_packet(card, queue, new_skb, hdr,
elements_needed, ctx);
else
- rc = qeth_do_send_packet_fast(card, queue, skb, hdr,
+ rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
elements_needed, ctx);
- if (!rc){
+ if (!rc) {
card->stats.tx_packets++;
card->stats.tx_bytes += tx_bytes;
-#ifdef CONFIG_QETH_PERF_STATS
- if (tso_size &&
- !(large_send == QETH_LARGE_SEND_NO)) {
- card->perf_stats.large_send_bytes += tx_bytes;
- card->perf_stats.large_send_cnt++;
- }
- if (nr_frags > 0){
- card->perf_stats.sg_skbs_sent++;
- /* nr_frags + skb->data */
- card->perf_stats.sg_frags_sent +=
- nr_frags + 1;
+ if (new_skb != skb)
+ dev_kfree_skb_any(skb);
+ if (card->options.performance_stats) {
+ if (tso_size &&
+ !(large_send == QETH_LARGE_SEND_NO)) {
+ card->perf_stats.large_send_bytes += tx_bytes;
+ card->perf_stats.large_send_cnt++;
+ }
+ if (nr_frags > 0) {
+ card->perf_stats.sg_skbs_sent++;
+ /* nr_frags + skb->data */
+ card->perf_stats.sg_frags_sent +=
+ nr_frags + 1;
+ }
}
-#endif /* CONFIG_QETH_PERF_STATS */
+ } else {
+ card->stats.tx_dropped++;
+ __qeth_free_new_skb(skb, new_skb);
}
if (ctx != NULL) {
/* drop creator's reference */
qeth_eddp_put_context(ctx);
/* free skb; it's not referenced by a buffer */
- if (rc == 0)
- dev_kfree_skb_any(skb);
-
+ if (!rc)
+ dev_kfree_skb_any(new_skb);
}
return rc;
}
@@ -4798,7 +4810,7 @@ static struct qeth_cmd_buffer *
qeth_get_setassparms_cmd(struct qeth_card *, enum qeth_ipa_funcs,
__u16, __u16, enum qeth_prot_versions);
static int
-qeth_arp_query(struct qeth_card *card, char *udata)
+qeth_arp_query(struct qeth_card *card, char __user *udata)
{
struct qeth_cmd_buffer *iob;
struct qeth_arp_query_info qinfo = {0, };
@@ -4931,7 +4943,7 @@ qeth_get_adapter_cmd(struct qeth_card *card, __u32 command, __u32 cmdlen)
* function to send SNMP commands to OSA-E card
*/
static int
-qeth_snmp_command(struct qeth_card *card, char *udata)
+qeth_snmp_command(struct qeth_card *card, char __user *udata)
{
struct qeth_cmd_buffer *iob;
struct qeth_ipa_cmd *cmd;
@@ -5273,6 +5285,7 @@ qeth_free_vlan_buffer(struct qeth_card *card, struct qeth_qdio_out_buffer *buf,
struct sk_buff_head tmp_list;
skb_queue_head_init(&tmp_list);
+ lockdep_set_class(&tmp_list.lock, &qdio_out_skb_queue_key);
for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i){
while ((skb = skb_dequeue(&buf->skb_list))){
if (vlan_tx_tag_present(skb) &&
@@ -7331,6 +7344,8 @@ qeth_setrouting_v6(struct qeth_card *card)
QETH_DBF_TEXT(trace,3,"setrtg6");
#ifdef CONFIG_QETH_IPV6
+ if (!qeth_is_supported(card, IPA_IPV6))
+ return 0;
qeth_correct_routing_type(card, &card->options.route6.type,
QETH_PROT_IPV6);
@@ -7869,12 +7884,12 @@ __qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode)
QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
goto out_remove;
}
- card->state = CARD_STATE_SOFTSETUP;
if ((rc = qeth_init_qdio_queues(card))){
QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
goto out_remove;
}
+ card->state = CARD_STATE_SOFTSETUP;
netif_carrier_on(card->dev);
qeth_set_allowed_threads(card, 0xffffffff, 0);
@@ -7902,9 +7917,9 @@ qeth_set_online(struct ccwgroup_device *gdev)
}
static struct ccw_device_id qeth_ids[] = {
- {CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE},
- {CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD},
- {CCW_DEVICE(0x1731, 0x06), driver_info:QETH_CARD_TYPE_OSN},
+ {CCW_DEVICE(0x1731, 0x01), .driver_info = QETH_CARD_TYPE_OSAE},
+ {CCW_DEVICE(0x1731, 0x05), .driver_info = QETH_CARD_TYPE_IQD},
+ {CCW_DEVICE(0x1731, 0x06), .driver_info = QETH_CARD_TYPE_OSN},
{},
};
MODULE_DEVICE_TABLE(ccw, qeth_ids);
@@ -8052,7 +8067,7 @@ qeth_arp_constructor(struct neighbour *neigh)
neigh->parms = neigh_parms_clone(parms);
rcu_read_unlock();
- neigh->type = inet_addr_type(*(u32 *) neigh->primary_key);
+ neigh->type = inet_addr_type(*(__be32 *) neigh->primary_key);
neigh->nud_state = NUD_NOARP;
neigh->ops = arp_direct_ops;
neigh->output = neigh->ops->queue_xmit;
@@ -8373,7 +8388,7 @@ out:
static struct notifier_block qeth_ip_notifier = {
qeth_ip_event,
- 0
+ NULL,
};
#ifdef CONFIG_QETH_IPV6
@@ -8426,7 +8441,7 @@ out:
static struct notifier_block qeth_ip6_notifier = {
qeth_ip6_event,
- 0
+ NULL,
};
#endif
@@ -8444,16 +8459,17 @@ __qeth_reboot_event_card(struct device *dev, void *data)
static int
qeth_reboot_event(struct notifier_block *this, unsigned long event, void *ptr)
{
+ int ret;
- driver_for_each_device(&qeth_ccwgroup_driver.driver, NULL, NULL,
- __qeth_reboot_event_card);
- return NOTIFY_DONE;
+ ret = driver_for_each_device(&qeth_ccwgroup_driver.driver, NULL, NULL,
+ __qeth_reboot_event_card);
+ return ret ? NOTIFY_BAD : NOTIFY_DONE;
}
static struct notifier_block qeth_reboot_notifier = {
qeth_reboot_event,
- 0
+ NULL,
};
static int
@@ -8502,9 +8518,9 @@ static int
qeth_ipv6_init(void)
{
qeth_old_arp_constructor = arp_tbl.constructor;
- write_lock(&arp_tbl.lock);
+ write_lock_bh(&arp_tbl.lock);
arp_tbl.constructor = qeth_arp_constructor;
- write_unlock(&arp_tbl.lock);
+ write_unlock_bh(&arp_tbl.lock);
arp_direct_ops = (struct neigh_ops*)
kmalloc(sizeof(struct neigh_ops), GFP_KERNEL);
@@ -8520,9 +8536,9 @@ qeth_ipv6_init(void)
static void
qeth_ipv6_uninit(void)
{
- write_lock(&arp_tbl.lock);
+ write_lock_bh(&arp_tbl.lock);
arp_tbl.constructor = qeth_old_arp_constructor;
- write_unlock(&arp_tbl.lock);
+ write_unlock_bh(&arp_tbl.lock);
kfree(arp_direct_ops);
}
#endif /* CONFIG_QETH_IPV6 */
@@ -8530,34 +8546,44 @@ qeth_ipv6_uninit(void)
static void
qeth_sysfs_unregister(void)
{
+ s390_root_dev_unregister(qeth_root_dev);
qeth_remove_driver_attributes();
ccw_driver_unregister(&qeth_ccw_driver);
ccwgroup_driver_unregister(&qeth_ccwgroup_driver);
- s390_root_dev_unregister(qeth_root_dev);
}
+
/**
* register qeth at sysfs
*/
static int
qeth_sysfs_register(void)
{
- int rc=0;
+ int rc;
rc = ccwgroup_driver_register(&qeth_ccwgroup_driver);
if (rc)
- return rc;
+ goto out;
+
rc = ccw_driver_register(&qeth_ccw_driver);
if (rc)
- return rc;
+ goto out_ccw_driver;
+
rc = qeth_create_driver_attributes();
if (rc)
- return rc;
+ goto out_qeth_attr;
+
qeth_root_dev = s390_root_dev_register("qeth");
- if (IS_ERR(qeth_root_dev)) {
- rc = PTR_ERR(qeth_root_dev);
- return rc;
- }
- return 0;
+ rc = IS_ERR(qeth_root_dev) ? PTR_ERR(qeth_root_dev) : 0;
+ if (!rc)
+ goto out;
+
+ qeth_remove_driver_attributes();
+out_qeth_attr:
+ ccw_driver_unregister(&qeth_ccw_driver);
+out_ccw_driver:
+ ccwgroup_driver_unregister(&qeth_ccwgroup_driver);
+out:
+ return rc;
}
/***
@@ -8566,7 +8592,7 @@ qeth_sysfs_register(void)
static int __init
qeth_init(void)
{
- int rc=0;
+ int rc;
PRINT_INFO("loading %s\n", version);
@@ -8575,20 +8601,26 @@ qeth_init(void)
spin_lock_init(&qeth_notify_lock);
rwlock_init(&qeth_card_list.rwlock);
- if (qeth_register_dbf_views())
+ rc = qeth_register_dbf_views();
+ if (rc)
goto out_err;
- if (qeth_sysfs_register())
- goto out_sysfs;
+
+ rc = qeth_sysfs_register();
+ if (rc)
+ goto out_dbf;
#ifdef CONFIG_QETH_IPV6
- if (qeth_ipv6_init()) {
- PRINT_ERR("Out of memory during ipv6 init.\n");
+ rc = qeth_ipv6_init();
+ if (rc) {
+ PRINT_ERR("Out of memory during ipv6 init code = %d\n", rc);
goto out_sysfs;
}
#endif /* QETH_IPV6 */
- if (qeth_register_notifiers())
+ rc = qeth_register_notifiers();
+ if (rc)
goto out_ipv6;
- if (qeth_create_procfs_entries())
+ rc = qeth_create_procfs_entries();
+ if (rc)
goto out_notifiers;
return rc;
@@ -8598,12 +8630,13 @@ out_notifiers:
out_ipv6:
#ifdef CONFIG_QETH_IPV6
qeth_ipv6_uninit();
-#endif /* QETH_IPV6 */
out_sysfs:
+#endif /* QETH_IPV6 */
qeth_sysfs_unregister();
+out_dbf:
qeth_unregister_dbf_views();
out_err:
- PRINT_ERR("Initialization failed");
+ PRINT_ERR("Initialization failed with code %d\n", rc);
return rc;
}
diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c
index 66f2da14e6e3..faa768e59257 100644
--- a/drivers/s390/net/qeth_proc.c
+++ b/drivers/s390/net/qeth_proc.c
@@ -173,7 +173,6 @@ static struct file_operations qeth_procfile_fops = {
#define QETH_PERF_PROCFILE_NAME "qeth_perf"
static struct proc_dir_entry *qeth_perf_procfile;
-#ifdef CONFIG_QETH_PERF_STATS
static int
qeth_perf_procfile_seq_show(struct seq_file *s, void *it)
{
@@ -192,14 +191,21 @@ qeth_perf_procfile_seq_show(struct seq_file *s, void *it)
CARD_DDEV_ID(card),
QETH_CARD_IFNAME(card)
);
+ if (!card->options.performance_stats)
+ seq_printf(s, "Performance statistics are deactivated.\n");
seq_printf(s, " Skb's/buffers received : %lu/%u\n"
" Skb's/buffers sent : %lu/%u\n\n",
- card->stats.rx_packets, card->perf_stats.bufs_rec,
- card->stats.tx_packets, card->perf_stats.bufs_sent
+ card->stats.rx_packets -
+ card->perf_stats.initial_rx_packets,
+ card->perf_stats.bufs_rec,
+ card->stats.tx_packets -
+ card->perf_stats.initial_tx_packets,
+ card->perf_stats.bufs_sent
);
seq_printf(s, " Skb's/buffers sent without packing : %lu/%u\n"
" Skb's/buffers sent with packing : %u/%u\n\n",
- card->stats.tx_packets - card->perf_stats.skbs_sent_pack,
+ card->stats.tx_packets - card->perf_stats.initial_tx_packets
+ - card->perf_stats.skbs_sent_pack,
card->perf_stats.bufs_sent - card->perf_stats.bufs_sent_pack,
card->perf_stats.skbs_sent_pack,
card->perf_stats.bufs_sent_pack
@@ -275,11 +281,6 @@ static struct file_operations qeth_perf_procfile_fops = {
.release = seq_release,
};
-#define qeth_perf_procfile_created qeth_perf_procfile
-#else
-#define qeth_perf_procfile_created 1
-#endif /* CONFIG_QETH_PERF_STATS */
-
int __init
qeth_create_procfs_entries(void)
{
@@ -288,15 +289,13 @@ qeth_create_procfs_entries(void)
if (qeth_procfile)
qeth_procfile->proc_fops = &qeth_procfile_fops;
-#ifdef CONFIG_QETH_PERF_STATS
qeth_perf_procfile = create_proc_entry(QETH_PERF_PROCFILE_NAME,
S_IFREG | 0444, NULL);
if (qeth_perf_procfile)
qeth_perf_procfile->proc_fops = &qeth_perf_procfile_fops;
-#endif /* CONFIG_QETH_PERF_STATS */
if (qeth_procfile &&
- qeth_perf_procfile_created)
+ qeth_perf_procfile)
return 0;
else
return -ENOMEM;
diff --git a/drivers/s390/net/qeth_sys.c b/drivers/s390/net/qeth_sys.c
index 185a9cfbcbdc..5836737ac58f 100644
--- a/drivers/s390/net/qeth_sys.c
+++ b/drivers/s390/net/qeth_sys.c
@@ -743,6 +743,47 @@ static DEVICE_ATTR(layer2, 0644, qeth_dev_layer2_show,
qeth_dev_layer2_store);
static ssize_t
+qeth_dev_performance_stats_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev->driver_data;
+
+ if (!card)
+ return -EINVAL;
+
+ return sprintf(buf, "%i\n", card->options.performance_stats ? 1:0);
+}
+
+static ssize_t
+qeth_dev_performance_stats_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev->driver_data;
+ char *tmp;
+ int i;
+
+ if (!card)
+ return -EINVAL;
+
+ i = simple_strtoul(buf, &tmp, 16);
+ if ((i == 0) || (i == 1)) {
+ if (i == card->options.performance_stats)
+ return count;
+ card->options.performance_stats = i;
+ if (i == 0)
+ memset(&card->perf_stats, 0,
+ sizeof(struct qeth_perf_stats));
+ card->perf_stats.initial_rx_packets = card->stats.rx_packets;
+ card->perf_stats.initial_tx_packets = card->stats.tx_packets;
+ } else {
+ PRINT_WARN("performance_stats: write 0 or 1 to this file!\n");
+ return -EINVAL;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(performance_stats, 0644, qeth_dev_performance_stats_show,
+ qeth_dev_performance_stats_store);
+
+static ssize_t
qeth_dev_large_send_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev->driver_data;
@@ -928,6 +969,7 @@ static struct device_attribute * qeth_device_attrs[] = {
&dev_attr_canonical_macaddr,
&dev_attr_layer2,
&dev_attr_large_send,
+ &dev_attr_performance_stats,
NULL,
};
@@ -1110,12 +1152,12 @@ qeth_parse_ipatoe(const char* buf, enum qeth_prot_versions proto,
{
const char *start, *end;
char *tmp;
- char buffer[49] = {0, };
+ char buffer[40] = {0, };
start = buf;
/* get address string */
end = strchr(start, '/');
- if (!end || (end-start >= 49)){
+ if (!end || (end - start >= 40)){
PRINT_WARN("Invalid format for ipato_addx/delx. "
"Use <ip addr>/<mask bits>\n");
return -EINVAL;
@@ -1127,7 +1169,12 @@ qeth_parse_ipatoe(const char* buf, enum qeth_prot_versions proto,
}
start = end + 1;
*mask_bits = simple_strtoul(start, &tmp, 10);
-
+ if (!strlen(start) ||
+ (tmp == start) ||
+ (*mask_bits > ((proto == QETH_PROT_IPV4) ? 32 : 128))) {
+ PRINT_WARN("Invalid mask bits for ipato_addx/delx !\n");
+ return -EINVAL;
+ }
return 0;
}
@@ -1698,11 +1745,16 @@ qeth_create_device_attributes(struct device *dev)
sysfs_remove_group(&dev->kobj, &qeth_device_attr_group);
sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group);
sysfs_remove_group(&dev->kobj, &qeth_device_vipa_group);
+ return ret;
}
- if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_blkt_group)))
+ if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_blkt_group))){
+ sysfs_remove_group(&dev->kobj, &qeth_device_attr_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_vipa_group);
+ sysfs_remove_group(&dev->kobj, &qeth_device_rxip_group);
return ret;
-
- return ret;
+ }
+ return 0;
}
void
@@ -1755,7 +1807,7 @@ qeth_driver_group_store(struct device_driver *ddrv, const char *buf,
}
-static DRIVER_ATTR(group, 0200, 0, qeth_driver_group_store);
+static DRIVER_ATTR(group, 0200, NULL, qeth_driver_group_store);
static ssize_t
qeth_driver_notifier_register_store(struct device_driver *ddrv, const char *buf,
@@ -1783,7 +1835,7 @@ qeth_driver_notifier_register_store(struct device_driver *ddrv, const char *buf,
return count;
}
-static DRIVER_ATTR(notifier_register, 0200, 0,
+static DRIVER_ATTR(notifier_register, 0200, NULL,
qeth_driver_notifier_register_store);
int
diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h
index 593f298142c1..14504afb044e 100644
--- a/drivers/s390/net/qeth_tso.h
+++ b/drivers/s390/net/qeth_tso.h
@@ -24,7 +24,7 @@ static inline struct qeth_hdr_tso *
qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb)
{
QETH_DBF_TEXT(trace, 5, "tsoprsk");
- return qeth_push_skb(card, skb, sizeof(struct qeth_hdr_tso));
+ return qeth_push_skb(card, *skb, sizeof(struct qeth_hdr_tso));
}
/**
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index 72118ee68954..b8179c27ceb6 100644
--- a/drivers/s390/net/smsgiucv.c
+++ b/drivers/s390/net/smsgiucv.c
@@ -66,7 +66,7 @@ smsg_message_pending(iucv_MessagePending *eib, void *pgm_data)
return;
}
rc = iucv_receive(eib->ippathid, eib->ipmsgid, eib->iptrgcls,
- msg, len, 0, 0, 0);
+ msg, len, NULL, NULL, NULL);
if (rc == 0) {
msg[len] = 0;
EBCASC(msg, len);
@@ -122,7 +122,7 @@ smsg_unregister_callback(char *prefix, void (*callback)(char *from, char *str))
struct smsg_callback *cb, *tmp;
spin_lock(&smsg_list_lock);
- cb = 0;
+ cb = NULL;
list_for_each_entry(tmp, &smsg_list, list)
if (tmp->callback == callback &&
strcmp(tmp->prefix, prefix) == 0) {
@@ -139,7 +139,7 @@ smsg_exit(void)
{
if (smsg_handle > 0) {
cpcmd("SET SMSG OFF", NULL, 0, NULL);
- iucv_sever(smsg_pathid, 0);
+ iucv_sever(smsg_pathid, NULL);
iucv_unregister_program(smsg_handle);
driver_unregister(&smsg_driver);
}
@@ -162,19 +162,19 @@ smsg_init(void)
return rc;
}
smsg_handle = iucv_register_program("SMSGIUCV ", "*MSG ",
- pgmmask, &smsg_ops, 0);
+ pgmmask, &smsg_ops, NULL);
if (!smsg_handle) {
printk(KERN_ERR "SMSGIUCV: failed to register to iucv");
driver_unregister(&smsg_driver);
return -EIO; /* better errno ? */
}
- rc = iucv_connect (&smsg_pathid, 255, 0, "*MSG ", 0, 0, 0, 0,
- smsg_handle, 0);
+ rc = iucv_connect (&smsg_pathid, 255, NULL, "*MSG ", NULL, 0,
+ NULL, NULL, smsg_handle, NULL);
if (rc) {
printk(KERN_ERR "SMSGIUCV: failed to connect to *MSG");
iucv_unregister_program(smsg_handle);
driver_unregister(&smsg_driver);
- smsg_handle = 0;
+ smsg_handle = NULL;
return -EIO;
}
cpcmd("SET SMSG IUCV", NULL, 0, NULL);
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
index f99e55308b32..479364d0332a 100644
--- a/drivers/s390/s390mach.c
+++ b/drivers/s390/s390mach.c
@@ -8,20 +8,17 @@
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/workqueue.h>
#include <linux/time.h>
+#include <linux/kthread.h>
#include <asm/lowcore.h>
#include "s390mach.h"
-#define DBG printk
-// #define DBG(args,...) do {} while (0);
-
static struct semaphore m_sem;
extern int css_process_crw(int, int);
@@ -56,8 +53,6 @@ s390_collect_crw_info(void *param)
unsigned int chain;
sem = (struct semaphore *)param;
- /* Set a nice name. */
- daemonize("kmcheck");
repeat:
down_interruptible(sem);
slow = 0;
@@ -85,11 +80,11 @@ repeat:
ccode = stcrw(&crw[chain]);
if (ccode != 0)
break;
- DBG(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
- "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
- crw[chain].slct, crw[chain].oflw, crw[chain].chn,
- crw[chain].rsc, crw[chain].anc, crw[chain].erc,
- crw[chain].rsid);
+ printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
+ "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+ crw[chain].slct, crw[chain].oflw, crw[chain].chn,
+ crw[chain].rsc, crw[chain].anc, crw[chain].erc,
+ crw[chain].rsid);
/* Check for overflows. */
if (crw[chain].oflw) {
pr_debug("%s: crw overflow detected!\n", __FUNCTION__);
@@ -113,6 +108,16 @@ repeat:
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. */
ret = chp_process_crw(crw[0].rsid, 1);
@@ -248,11 +253,12 @@ s390_revalidate_registers(struct mci *mci)
kill_task = 1;
#ifndef CONFIG_64BIT
- asm volatile("ld 0,0(%0)\n"
- "ld 2,8(%0)\n"
- "ld 4,16(%0)\n"
- "ld 6,24(%0)"
- : : "a" (&S390_lowcore.floating_pt_save_area));
+ asm volatile(
+ " ld 0,0(%0)\n"
+ " ld 2,8(%0)\n"
+ " ld 4,16(%0)\n"
+ " ld 6,24(%0)"
+ : : "a" (&S390_lowcore.floating_pt_save_area));
#endif
if (MACHINE_HAS_IEEE) {
@@ -269,37 +275,36 @@ s390_revalidate_registers(struct mci *mci)
* Floating point control register can't be restored.
* Task will be terminated.
*/
- asm volatile ("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
+ asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
kill_task = 1;
- }
- else
- asm volatile (
- "lfpc 0(%0)"
- : : "a" (fpt_creg_save_area));
-
- asm volatile("ld 0,0(%0)\n"
- "ld 1,8(%0)\n"
- "ld 2,16(%0)\n"
- "ld 3,24(%0)\n"
- "ld 4,32(%0)\n"
- "ld 5,40(%0)\n"
- "ld 6,48(%0)\n"
- "ld 7,56(%0)\n"
- "ld 8,64(%0)\n"
- "ld 9,72(%0)\n"
- "ld 10,80(%0)\n"
- "ld 11,88(%0)\n"
- "ld 12,96(%0)\n"
- "ld 13,104(%0)\n"
- "ld 14,112(%0)\n"
- "ld 15,120(%0)\n"
- : : "a" (fpt_save_area));
+ } else
+ asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area));
+
+ asm volatile(
+ " ld 0,0(%0)\n"
+ " ld 1,8(%0)\n"
+ " ld 2,16(%0)\n"
+ " ld 3,24(%0)\n"
+ " ld 4,32(%0)\n"
+ " ld 5,40(%0)\n"
+ " ld 6,48(%0)\n"
+ " ld 7,56(%0)\n"
+ " ld 8,64(%0)\n"
+ " ld 9,72(%0)\n"
+ " ld 10,80(%0)\n"
+ " ld 11,88(%0)\n"
+ " ld 12,96(%0)\n"
+ " ld 13,104(%0)\n"
+ " ld 14,112(%0)\n"
+ " ld 15,120(%0)\n"
+ : : "a" (fpt_save_area));
}
/* Revalidate access registers */
- asm volatile("lam 0,15,0(%0)"
- : : "a" (&S390_lowcore.access_regs_save_area));
+ asm volatile(
+ " lam 0,15,0(%0)"
+ : : "a" (&S390_lowcore.access_regs_save_area));
if (!mci->ar)
/*
* Access registers have unknown contents.
@@ -316,11 +321,13 @@ s390_revalidate_registers(struct mci *mci)
s390_handle_damage("invalid control registers.");
else
#ifdef CONFIG_64BIT
- asm volatile("lctlg 0,15,0(%0)"
- : : "a" (&S390_lowcore.cregs_save_area));
+ asm volatile(
+ " lctlg 0,15,0(%0)"
+ : : "a" (&S390_lowcore.cregs_save_area));
#else
- asm volatile("lctl 0,15,0(%0)"
- : : "a" (&S390_lowcore.cregs_save_area));
+ asm volatile(
+ " lctl 0,15,0(%0)"
+ : : "a" (&S390_lowcore.cregs_save_area));
#endif
/*
@@ -334,20 +341,23 @@ s390_revalidate_registers(struct mci *mci)
* old contents (should be zero) otherwise set it to zero.
*/
if (!mci->pr)
- asm volatile("sr 0,0\n"
- "sckpf"
- : : : "0", "cc");
+ asm volatile(
+ " sr 0,0\n"
+ " sckpf"
+ : : : "0", "cc");
else
asm volatile(
- "l 0,0(%0)\n"
- "sckpf"
- : : "a" (&S390_lowcore.tod_progreg_save_area) : "0", "cc");
+ " l 0,0(%0)\n"
+ " sckpf"
+ : : "a" (&S390_lowcore.tod_progreg_save_area)
+ : "0", "cc");
#endif
/* Revalidate clock comparator register */
- asm volatile ("stck 0(%1)\n"
- "sckc 0(%1)"
- : "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory");
+ asm volatile(
+ " stck 0(%1)\n"
+ " sckc 0(%1)"
+ : "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory");
/* Check if old PSW is valid */
if (!mci->wp)
@@ -380,6 +390,8 @@ s390_do_machine_check(struct pt_regs *regs)
struct mcck_struct *mcck;
int umode;
+ lockdep_off();
+
mci = (struct mci *) &S390_lowcore.mcck_interruption_code;
mcck = &__get_cpu_var(cpu_mcck);
umode = user_mode(regs);
@@ -484,6 +496,7 @@ s390_do_machine_check(struct pt_regs *regs)
mcck->warning = 1;
set_thread_flag(TIF_MCCK_PENDING);
}
+ lockdep_on();
}
/*
@@ -516,7 +529,7 @@ arch_initcall(machine_check_init);
static int __init
machine_check_crw_init (void)
{
- kernel_thread(s390_collect_crw_info, &m_sem, CLONE_FS|CLONE_FILES);
+ kthread_run(s390_collect_crw_info, &m_sem, "kmcheck");
ctl_set_bit(14, 28); /* enable channel report MCH */
return 0;
}
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 9cd789b8acd4..5d39b2df0cc4 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -112,6 +112,109 @@ _zfcp_hex_dump(char *addr, int count)
printk("\n");
}
+
+/****************************************************************/
+/****** Functions to handle the request ID hash table ********/
+/****************************************************************/
+
+#define ZFCP_LOG_AREA ZFCP_LOG_AREA_FSF
+
+static int zfcp_reqlist_init(struct zfcp_adapter *adapter)
+{
+ int i;
+
+ adapter->req_list = kcalloc(REQUEST_LIST_SIZE, sizeof(struct list_head),
+ GFP_KERNEL);
+
+ if (!adapter->req_list)
+ return -ENOMEM;
+
+ for (i=0; i<REQUEST_LIST_SIZE; i++)
+ INIT_LIST_HEAD(&adapter->req_list[i]);
+
+ return 0;
+}
+
+static void zfcp_reqlist_free(struct zfcp_adapter *adapter)
+{
+ struct zfcp_fsf_req *request, *tmp;
+ unsigned int i;
+
+ for (i=0; i<REQUEST_LIST_SIZE; i++) {
+ if (list_empty(&adapter->req_list[i]))
+ continue;
+
+ list_for_each_entry_safe(request, tmp,
+ &adapter->req_list[i], list)
+ list_del(&request->list);
+ }
+
+ kfree(adapter->req_list);
+}
+
+void zfcp_reqlist_add(struct zfcp_adapter *adapter,
+ struct zfcp_fsf_req *fsf_req)
+{
+ unsigned int i;
+
+ i = fsf_req->req_id % REQUEST_LIST_SIZE;
+ list_add_tail(&fsf_req->list, &adapter->req_list[i]);
+}
+
+void zfcp_reqlist_remove(struct zfcp_adapter *adapter, unsigned long req_id)
+{
+ struct zfcp_fsf_req *request, *tmp;
+ unsigned int i, counter;
+ u64 dbg_tmp[2];
+
+ i = req_id % REQUEST_LIST_SIZE;
+ BUG_ON(list_empty(&adapter->req_list[i]));
+
+ counter = 0;
+ list_for_each_entry_safe(request, tmp, &adapter->req_list[i], list) {
+ if (request->req_id == req_id) {
+ dbg_tmp[0] = (u64) atomic_read(&adapter->reqs_active);
+ dbg_tmp[1] = (u64) counter;
+ debug_event(adapter->erp_dbf, 4, (void *) dbg_tmp, 16);
+ list_del(&request->list);
+ break;
+ }
+ counter++;
+ }
+}
+
+struct zfcp_fsf_req *zfcp_reqlist_ismember(struct zfcp_adapter *adapter,
+ unsigned long req_id)
+{
+ struct zfcp_fsf_req *request, *tmp;
+ unsigned int i;
+
+ /* 0 is reserved as an invalid req_id */
+ if (req_id == 0)
+ return NULL;
+
+ i = req_id % REQUEST_LIST_SIZE;
+
+ list_for_each_entry_safe(request, tmp, &adapter->req_list[i], list)
+ if (request->req_id == req_id)
+ return request;
+
+ return NULL;
+}
+
+int zfcp_reqlist_isempty(struct zfcp_adapter *adapter)
+{
+ unsigned int i;
+
+ for (i=0; i<REQUEST_LIST_SIZE; i++)
+ if (!list_empty(&adapter->req_list[i]))
+ return 0;
+
+ return 1;
+}
+
+#undef ZFCP_LOG_AREA
+
/****************************************************************/
/************** Uncategorised Functions *************************/
/****************************************************************/
@@ -200,11 +303,45 @@ zfcp_init_device_configure(void)
return;
}
+static int calc_alignment(int size)
+{
+ int align = 1;
+
+ if (!size)
+ return 0;
+
+ while ((size - align) > 0)
+ align <<= 1;
+
+ return align;
+}
+
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, NULL);
+ if (!zfcp_data.fsf_req_qtcb_cache)
+ goto out;
- int retval = 0;
+ 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, NULL);
+ 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, NULL);
+ if (!zfcp_data.gid_pn_cache)
+ goto out_gid_cache;
atomic_set(&zfcp_data.loglevel, loglevel);
@@ -214,15 +351,16 @@ zfcp_module_init(void)
/* initialize adapters to be removed list head */
INIT_LIST_HEAD(&zfcp_data.adapter_remove_lh);
- zfcp_transport_template = fc_attach_transport(&zfcp_transport_functions);
- if (!zfcp_transport_template)
- return -ENODEV;
+ 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");
- goto out;
+ goto out_misc;
}
ZFCP_LOG_TRACE("major/minor for zfcp_cfdc: %d/%d\n",
@@ -234,9 +372,6 @@ zfcp_module_init(void)
/* initialise configuration rw lock */
rwlock_init(&zfcp_data.config_lock);
- /* save address of data structure managing the driver module */
- zfcp_data.scsi_host_template.module = THIS_MODULE;
-
/* setup dynamic I/O */
retval = zfcp_ccw_register();
if (retval) {
@@ -251,6 +386,14 @@ zfcp_module_init(void)
out_ccw_register:
misc_deregister(&zfcp_cfdc_misc);
+ out_misc:
+ fc_release_transport(zfcp_data.scsi_transport_template);
+ out_transport:
+ kmem_cache_destroy(zfcp_data.gid_pn_cache);
+ out_gid_cache:
+ kmem_cache_destroy(zfcp_data.sr_buffer_cache);
+ out_sr_cache:
+ kmem_cache_destroy(zfcp_data.fsf_req_qtcb_cache);
out:
return retval;
}
@@ -836,20 +979,20 @@ static int
zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
{
adapter->pool.fsf_req_erp =
- mempool_create_kmalloc_pool(ZFCP_POOL_FSF_REQ_ERP_NR,
- sizeof(struct zfcp_fsf_req_pool_element));
+ mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_ERP_NR,
+ zfcp_data.fsf_req_qtcb_cache);
if (!adapter->pool.fsf_req_erp)
return -ENOMEM;
adapter->pool.fsf_req_scsi =
- mempool_create_kmalloc_pool(ZFCP_POOL_FSF_REQ_SCSI_NR,
- sizeof(struct zfcp_fsf_req_pool_element));
+ mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_SCSI_NR,
+ zfcp_data.fsf_req_qtcb_cache);
if (!adapter->pool.fsf_req_scsi)
return -ENOMEM;
adapter->pool.fsf_req_abort =
- mempool_create_kmalloc_pool(ZFCP_POOL_FSF_REQ_ABORT_NR,
- sizeof(struct zfcp_fsf_req_pool_element));
+ mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_ABORT_NR,
+ zfcp_data.fsf_req_qtcb_cache);
if (!adapter->pool.fsf_req_abort)
return -ENOMEM;
@@ -860,14 +1003,14 @@ zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
return -ENOMEM;
adapter->pool.data_status_read =
- mempool_create_kmalloc_pool(ZFCP_POOL_STATUS_READ_NR,
- sizeof(struct fsf_status_read_buffer));
+ mempool_create_slab_pool(ZFCP_POOL_STATUS_READ_NR,
+ zfcp_data.sr_buffer_cache);
if (!adapter->pool.data_status_read)
return -ENOMEM;
adapter->pool.data_gid_pn =
- mempool_create_kmalloc_pool(ZFCP_POOL_DATA_GID_PN_NR,
- sizeof(struct zfcp_gid_pn_data));
+ mempool_create_slab_pool(ZFCP_POOL_DATA_GID_PN_NR,
+ zfcp_data.gid_pn_cache);
if (!adapter->pool.data_gid_pn)
return -ENOMEM;
@@ -961,8 +1104,12 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device)
INIT_LIST_HEAD(&adapter->port_remove_lh);
/* initialize list of fsf requests */
- spin_lock_init(&adapter->fsf_req_list_lock);
- INIT_LIST_HEAD(&adapter->fsf_req_list_head);
+ spin_lock_init(&adapter->req_list_lock);
+ retval = zfcp_reqlist_init(adapter);
+ if (retval) {
+ ZFCP_LOG_INFO("request list initialization failed\n");
+ goto failed_low_mem_buffers;
+ }
/* initialize debug locks */
@@ -988,9 +1135,6 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device)
/* initialize lock of associated request queue */
rwlock_init(&adapter->request_queue.queue_lock);
- /* intitialise SCSI ER timer */
- init_timer(&adapter->scsi_er_timer);
-
/* mark adapter unusable as long as sysfs registration is not complete */
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
@@ -1041,8 +1185,6 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device)
* !0 - struct zfcp_adapter data structure could not be removed
* (e.g. still used)
* locks: adapter list write lock is assumed to be held by caller
- * adapter->fsf_req_list_lock is taken and released within this
- * function and must not be held on entry
*/
void
zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
@@ -1054,14 +1196,14 @@ zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev);
dev_set_drvdata(&adapter->ccw_device->dev, NULL);
/* sanity check: no pending FSF requests */
- spin_lock_irqsave(&adapter->fsf_req_list_lock, flags);
- retval = !list_empty(&adapter->fsf_req_list_head);
- spin_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
- if (retval) {
+ 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->fsf_reqs_active));
+ atomic_read(&adapter->reqs_active));
retval = -EBUSY;
goto out;
}
@@ -1087,6 +1229,7 @@ zfcp_adapter_dequeue(struct zfcp_adapter *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->fc_stats);
kfree(adapter->stats_reset_data);
ZFCP_LOG_TRACE("freeing adapter structure\n");
@@ -1507,7 +1650,6 @@ zfcp_ns_gid_pn_request(struct zfcp_erp_action *erp_action)
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.timer = &erp_action->timer;
gid_pn->port = erp_action->port;
ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp,
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index 57d8e4bfb8d9..81680efa1721 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -164,6 +164,11 @@ zfcp_ccw_set_online(struct ccw_device *ccw_device)
retval = zfcp_adapter_scsi_register(adapter);
if (retval)
goto out_scsi_register;
+
+ /* initialize request counter */
+ BUG_ON(!zfcp_reqlist_isempty(adapter));
+ adapter->req_no = 0;
+
zfcp_erp_modify_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING,
ZFCP_SET);
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED);
@@ -270,19 +275,6 @@ zfcp_ccw_register(void)
}
/**
- * zfcp_ccw_unregister - ccw unregister function
- *
- * Unregisters the driver from common i/o layer. Function will be called at
- * module unload/system shutdown.
- */
-void __exit
-zfcp_ccw_unregister(void)
-{
- zfcp_sysfs_driver_remove_files(&zfcp_ccw_driver.driver);
- ccw_driver_unregister(&zfcp_ccw_driver);
-}
-
-/**
* zfcp_ccw_shutdown - gets called on reboot/shutdown
*
* Makes sure that QDIO queues are down when the system gets stopped.
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index c033145d0f19..0aa3b1ac76af 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -707,7 +707,7 @@ _zfcp_scsi_dbf_event_common(const char *tag, const char *tag2, int level,
struct zfcp_adapter *adapter,
struct scsi_cmnd *scsi_cmnd,
struct zfcp_fsf_req *fsf_req,
- struct zfcp_fsf_req *old_fsf_req)
+ unsigned long old_req_id)
{
struct zfcp_scsi_dbf_record *rec = &adapter->scsi_dbf_buf;
struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)rec;
@@ -768,8 +768,7 @@ _zfcp_scsi_dbf_event_common(const char *tag, const char *tag2, int level,
rec->fsf_seqno = fsf_req->seq_no;
rec->fsf_issued = fsf_req->issued;
}
- rec->type.old_fsf_reqid =
- (unsigned long) old_fsf_req;
+ rec->type.old_fsf_reqid = old_req_id;
} else {
strncpy(dump->tag, "dump", ZFCP_DBF_TAG_SIZE);
dump->total_size = buflen;
@@ -794,17 +793,17 @@ zfcp_scsi_dbf_event_result(const char *tag, int level,
struct zfcp_fsf_req *fsf_req)
{
_zfcp_scsi_dbf_event_common("rslt", tag, level,
- adapter, scsi_cmnd, fsf_req, NULL);
+ adapter, scsi_cmnd, fsf_req, 0);
}
inline void
zfcp_scsi_dbf_event_abort(const char *tag, struct zfcp_adapter *adapter,
struct scsi_cmnd *scsi_cmnd,
struct zfcp_fsf_req *new_fsf_req,
- struct zfcp_fsf_req *old_fsf_req)
+ unsigned long old_req_id)
{
_zfcp_scsi_dbf_event_common("abrt", tag, 1,
- adapter, scsi_cmnd, new_fsf_req, old_fsf_req);
+ adapter, scsi_cmnd, new_fsf_req, old_req_id);
}
inline void
@@ -814,7 +813,7 @@ zfcp_scsi_dbf_event_devreset(const char *tag, u8 flag, struct zfcp_unit *unit,
struct zfcp_adapter *adapter = unit->port->adapter;
_zfcp_scsi_dbf_event_common(flag == FCP_TARGET_RESET ? "trst" : "lrst",
- tag, 1, adapter, scsi_cmnd, NULL, NULL);
+ tag, 1, adapter, scsi_cmnd, NULL, 0);
}
static int
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index 2df512a18e2c..8f882690994d 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -19,7 +19,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-
#ifndef ZFCP_DEF_H
#define ZFCP_DEF_H
@@ -32,6 +31,10 @@
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/mempool.h>
+#include <linux/syscalls.h>
+#include <linux/ioctl.h>
#include <scsi/scsi.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_cmnd.h>
@@ -39,20 +42,17 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
-#include "zfcp_fsf.h"
#include <asm/ccwdev.h>
#include <asm/qdio.h>
#include <asm/debug.h>
#include <asm/ebcdic.h>
-#include <linux/mempool.h>
-#include <linux/syscalls.h>
-#include <linux/ioctl.h>
+#include "zfcp_fsf.h"
/********************* GENERAL DEFINES *********************************/
/* zfcp version number, it consists of major, minor, and patch-level number */
-#define ZFCP_VERSION "4.7.0"
+#define ZFCP_VERSION "4.8.0"
/**
* zfcp_sg_to_address - determine kernel address from struct scatterlist
@@ -80,7 +80,7 @@ zfcp_address_to_sg(void *address, struct scatterlist *list)
#define REQUEST_LIST_SIZE 128
/********************* SCSI SPECIFIC DEFINES *********************************/
-#define ZFCP_SCSI_ER_TIMEOUT (100*HZ)
+#define ZFCP_SCSI_ER_TIMEOUT (10*HZ)
/********************* CIO/QDIO SPECIFIC DEFINES *****************************/
@@ -137,7 +137,7 @@ zfcp_address_to_sg(void *address, struct scatterlist *list)
#define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES 7
/* timeout value for "default timer" for fsf requests */
-#define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ);
+#define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ)
/*************** FIBRE CHANNEL PROTOCOL SPECIFIC DEFINES ********************/
@@ -543,7 +543,7 @@ do { \
} while (0)
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL
-# define ZFCP_LOG_NORMAL(fmt, args...)
+# define ZFCP_LOG_NORMAL(fmt, args...) do { } while (0)
#else
# define ZFCP_LOG_NORMAL(fmt, args...) \
do { \
@@ -553,7 +553,7 @@ do { \
#endif
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO
-# define ZFCP_LOG_INFO(fmt, args...)
+# define ZFCP_LOG_INFO(fmt, args...) do { } while (0)
#else
# define ZFCP_LOG_INFO(fmt, args...) \
do { \
@@ -563,14 +563,14 @@ do { \
#endif
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG
-# define ZFCP_LOG_DEBUG(fmt, args...)
+# 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...)
+# define ZFCP_LOG_TRACE(fmt, args...) do { } while (0)
#else
# define ZFCP_LOG_TRACE(fmt, args...) \
ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, fmt , ##args)
@@ -779,7 +779,6 @@ typedef void (*zfcp_send_ct_handler_t)(unsigned long);
* @handler_data: data passed to handler function
* @pool: pointer to memory pool for ct request structure
* @timeout: FSF timeout for this request
- * @timer: timer (e.g. for request initiated by erp)
* @completion: completion for synchronization purposes
* @status: used to pass error status to calling function
*/
@@ -793,7 +792,6 @@ struct zfcp_send_ct {
unsigned long handler_data;
mempool_t *pool;
int timeout;
- struct timer_list *timer;
struct completion *completion;
int status;
};
@@ -821,7 +819,6 @@ typedef void (*zfcp_send_els_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
- * @timer: timer (e.g. for request initiated by erp)
* @completion: completion for synchronization purposes
* @ls_code: hex code of ELS command
* @status: used to pass error status to calling function
@@ -836,7 +833,6 @@ struct zfcp_send_els {
unsigned int resp_count;
zfcp_send_els_handler_t handler;
unsigned long handler_data;
- struct timer_list *timer;
struct completion *completion;
int ls_code;
int status;
@@ -886,11 +882,10 @@ struct zfcp_adapter {
struct list_head port_remove_lh; /* head of ports to be
removed */
u32 ports; /* number of remote ports */
- struct timer_list scsi_er_timer; /* SCSI err recovery watch */
- struct list_head fsf_req_list_head; /* head of FSF req list */
- spinlock_t fsf_req_list_lock; /* lock for ops on list of
- FSF requests */
- atomic_t fsf_reqs_active; /* # active FSF reqs */
+ 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 */
u32 fsf_req_seq_no; /* FSF cmnd seq number */
wait_queue_head_t request_wq; /* can be used to wait for
@@ -986,6 +981,7 @@ struct zfcp_unit {
/* FSF request */
struct zfcp_fsf_req {
struct list_head list; /* list of FSF requests */
+ unsigned long req_id; /* unique request ID */
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 */
@@ -1002,6 +998,7 @@ struct zfcp_fsf_req {
struct fsf_qtcb *qtcb; /* address of associated QTCB */
u32 seq_no; /* Sequence number of request */
unsigned long 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 */
mempool_t *pool; /* used if request was alloacted
@@ -1015,6 +1012,7 @@ typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*);
/* driver data */
struct zfcp_data {
struct scsi_host_template scsi_host_template;
+ struct scsi_transport_template *scsi_transport_template;
atomic_t status; /* Module status flags */
struct list_head adapter_list_head; /* head of adapter list */
struct list_head adapter_remove_lh; /* head of adapters to be
@@ -1030,6 +1028,9 @@ struct zfcp_data {
wwn_t init_wwpn;
fcp_lun_t init_fcp_lun;
char *driver_version;
+ kmem_cache_t *fsf_req_qtcb_cache;
+ kmem_cache_t *sr_buffer_cache;
+ kmem_cache_t *gid_pn_cache;
};
/**
@@ -1050,7 +1051,7 @@ struct zfcp_sg_list {
#define ZFCP_POOL_DATA_GID_PN_NR 1
/* struct used by memory pools for fsf_requests */
-struct zfcp_fsf_req_pool_element {
+struct zfcp_fsf_req_qtcb {
struct zfcp_fsf_req fsf_req;
struct fsf_qtcb qtcb;
};
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 4682c8b8bd24..862a411a4aa0 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -64,8 +64,6 @@ 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_close_qdio(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_close_fsf(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 *);
@@ -93,10 +91,10 @@ 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 int zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *);
-static int zfcp_erp_action_dismiss_port(struct zfcp_port *);
-static int zfcp_erp_action_dismiss_unit(struct zfcp_unit *);
-static int zfcp_erp_action_dismiss(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 *);
@@ -112,8 +110,62 @@ static inline void zfcp_erp_action_to_ready(struct zfcp_erp_action *);
static inline void zfcp_erp_action_to_running(struct zfcp_erp_action *);
static void zfcp_erp_memwait_handler(unsigned long);
-static void zfcp_erp_timeout_handler(unsigned long);
-static inline void zfcp_erp_timeout_init(struct zfcp_erp_action *);
+
+/**
+ * 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;
+
+ if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status))
+ return;
+
+ /* 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);
+
+ debug_text_event(adapter->erp_dbf, 3, "qdio_down2a");
+ while (qdio_shutdown(adapter->ccw_device,
+ QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS)
+ msleep(1000);
+ debug_text_event(adapter->erp_dbf, 3, "qdio_down2b");
+
+ /* 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);
+}
+
+/**
+ * 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)
+{
+ /* 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,
+ ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
+}
/**
* zfcp_fsf_request_timeout_handler - called if a request timed out
@@ -122,42 +174,20 @@ static inline void zfcp_erp_timeout_init(struct zfcp_erp_action *);
* 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. This should not be used for error recovery, SCSI abort
- * commands and SCSI requests from SCSI mid-layer.
+ * the adapter.
*/
-void
-zfcp_fsf_request_timeout_handler(unsigned long data)
+static void zfcp_fsf_request_timeout_handler(unsigned long data)
{
- struct zfcp_adapter *adapter;
-
- adapter = (struct zfcp_adapter *) data;
-
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
zfcp_erp_adapter_reopen(adapter, 0);
}
-/*
- * function: zfcp_fsf_scsi_er_timeout_handler
- *
- * purpose: This function needs to be called whenever a SCSI error recovery
- * action (abort/reset) does not return.
- * Re-opening the adapter means that the command can be returned
- * by zfcp (it is guarranteed that it does not return via the
- * adapter anymore). The buffer can then be used again.
- *
- * returns: sod all
- */
-void
-zfcp_fsf_scsi_er_timeout_handler(unsigned long data)
+void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req, unsigned long timeout)
{
- struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
-
- ZFCP_LOG_NORMAL("warning: SCSI error recovery timed out. "
- "Restarting all operations on the adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- debug_text_event(adapter->erp_dbf, 1, "eh_lmem_tout");
- zfcp_erp_adapter_reopen(adapter, 0);
-
- return;
+ fsf_req->timer.function = zfcp_fsf_request_timeout_handler;
+ fsf_req->timer.data = (unsigned long) fsf_req->adapter;
+ fsf_req->timer.expires = timeout;
+ add_timer(&fsf_req->timer);
}
/*
@@ -167,7 +197,7 @@ zfcp_fsf_scsi_er_timeout_handler(unsigned long data)
* initiates adapter recovery which is done
* asynchronously
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
int
@@ -203,7 +233,7 @@ zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *adapter, int clear_mask)
* purpose: Wrappper for zfcp_erp_adapter_reopen_internal
* used to ensure the correct locking
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
int
@@ -273,7 +303,6 @@ zfcp_erp_adisc(struct zfcp_port *port)
struct zfcp_ls_adisc *adisc;
void *address = NULL;
int retval = 0;
- struct timer_list *timer;
send_els = kzalloc(sizeof(struct zfcp_send_els), GFP_ATOMIC);
if (send_els == NULL)
@@ -320,22 +349,11 @@ zfcp_erp_adisc(struct zfcp_port *port)
(wwn_t) adisc->wwnn, adisc->hard_nport_id,
adisc->nport_id);
- timer = kmalloc(sizeof(struct timer_list), GFP_ATOMIC);
- if (!timer)
- goto nomem;
-
- init_timer(timer);
- timer->function = zfcp_fsf_request_timeout_handler;
- timer->data = (unsigned long) adapter;
- timer->expires = ZFCP_FSF_REQUEST_TIMEOUT;
- send_els->timer = timer;
-
retval = zfcp_fsf_send_els(send_els);
if (retval != 0) {
ZFCP_LOG_NORMAL("error: initiation of Send ELS failed for port "
"0x%08x on adapter %s\n", send_els->d_id,
zfcp_get_busid_by_adapter(adapter));
- del_timer(send_els->timer);
goto freemem;
}
@@ -347,7 +365,6 @@ zfcp_erp_adisc(struct zfcp_port *port)
if (address != NULL)
__free_pages(send_els->req->page, 0);
if (send_els != NULL) {
- kfree(send_els->timer);
kfree(send_els->req);
kfree(send_els->resp);
kfree(send_els);
@@ -373,9 +390,6 @@ zfcp_erp_adisc_handler(unsigned long data)
struct zfcp_ls_adisc_acc *adisc;
send_els = (struct zfcp_send_els *) data;
-
- del_timer(send_els->timer);
-
adapter = send_els->adapter;
port = send_els->port;
d_id = send_els->d_id;
@@ -424,7 +438,6 @@ zfcp_erp_adisc_handler(unsigned long data)
out:
zfcp_port_put(port);
__free_pages(send_els->req->page, 0);
- kfree(send_els->timer);
kfree(send_els->req);
kfree(send_els->resp);
kfree(send_els);
@@ -469,7 +482,7 @@ zfcp_test_link(struct zfcp_port *port)
* initiates Forced Reopen recovery which is done
* asynchronously
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
static int
@@ -509,7 +522,7 @@ zfcp_erp_port_forced_reopen_internal(struct zfcp_port *port, int clear_mask)
* purpose: Wrappper for zfcp_erp_port_forced_reopen_internal
* used to ensure the correct locking
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
int
@@ -536,7 +549,7 @@ zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear_mask)
* initiates Reopen recovery which is done
* asynchronously
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
static int
@@ -605,7 +618,7 @@ zfcp_erp_port_reopen(struct zfcp_port *port, int clear_mask)
* initiates Reopen recovery which is done
* asynchronously
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
static int
@@ -670,17 +683,10 @@ zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear_mask)
return retval;
}
-/*
- * function:
- *
- * purpose: disable I/O,
- * return any open requests and clean them up,
- * aim: no pending and incoming I/O
- *
- * returns:
+/**
+ * zfcp_erp_adapter_block - mark adapter as blocked, block scsi requests
*/
-static void
-zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int clear_mask)
+static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int clear_mask)
{
debug_text_event(adapter->erp_dbf, 6, "a_bl");
zfcp_erp_modify_adapter_status(adapter,
@@ -688,15 +694,10 @@ zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int clear_mask)
clear_mask, ZFCP_CLEAR);
}
-/*
- * function:
- *
- * purpose: enable I/O
- *
- * returns:
+/**
+ * zfcp_erp_adapter_unblock - mark adapter as unblocked, allow scsi requests
*/
-static void
-zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
+static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
{
debug_text_event(adapter->erp_dbf, 6, "a_ubl");
atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
@@ -848,18 +849,16 @@ zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action)
struct zfcp_adapter *adapter = erp_action->adapter;
if (erp_action->fsf_req) {
- /* take lock to ensure that request is not being deleted meanwhile */
- spin_lock(&adapter->fsf_req_list_lock);
- /* check whether fsf req does still exist */
- list_for_each_entry(fsf_req, &adapter->fsf_req_list_head, list)
- if (fsf_req == erp_action->fsf_req)
- break;
- if (fsf_req && (fsf_req->erp_action == erp_action)) {
+ /* take lock to ensure that request is not deleted meanwhile */
+ spin_lock(&adapter->req_list_lock);
+ if ((!zfcp_reqlist_ismember(adapter,
+ erp_action->fsf_req->req_id)) &&
+ (fsf_req->erp_action == erp_action)) {
/* fsf_req still exists */
debug_text_event(adapter->erp_dbf, 3, "a_ca_req");
debug_event(adapter->erp_dbf, 3, &fsf_req,
sizeof (unsigned long));
- /* dismiss fsf_req of timed out or dismissed erp_action */
+ /* dismiss fsf_req of timed out/dismissed erp_action */
if (erp_action->status & (ZFCP_STATUS_ERP_DISMISSED |
ZFCP_STATUS_ERP_TIMEDOUT)) {
debug_text_event(adapter->erp_dbf, 3,
@@ -892,77 +891,50 @@ zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action)
*/
erp_action->fsf_req = NULL;
}
- spin_unlock(&adapter->fsf_req_list_lock);
+ spin_unlock(&adapter->req_list_lock);
} else
debug_text_event(adapter->erp_dbf, 3, "a_ca_noreq");
return retval;
}
-/*
- * purpose: generic handler for asynchronous events related to erp_action events
- * (normal completion, time-out, dismissing, retry after
- * low memory condition)
- *
- * note: deletion of timer is not required (e.g. in case of a time-out),
- * but a second try does no harm,
- * we leave it in here to allow for greater simplification
+/**
+ * zfcp_erp_async_handler_nolock - complete erp_action
*
- * returns: 0 - there was an action to handle
- * !0 - otherwise
+ * Used for normal completion, time-out, dismissal and failure after
+ * low memory condition.
*/
-static int
-zfcp_erp_async_handler_nolock(struct zfcp_erp_action *erp_action,
- unsigned long set_mask)
+static void zfcp_erp_async_handler_nolock(struct zfcp_erp_action *erp_action,
+ unsigned long set_mask)
{
- int retval;
struct zfcp_adapter *adapter = erp_action->adapter;
if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) {
debug_text_event(adapter->erp_dbf, 2, "a_asyh_ex");
debug_event(adapter->erp_dbf, 2, &erp_action->action,
sizeof (int));
- if (!(set_mask & ZFCP_STATUS_ERP_TIMEDOUT))
- del_timer(&erp_action->timer);
erp_action->status |= set_mask;
zfcp_erp_action_ready(erp_action);
- retval = 0;
} else {
/* action is ready or gone - nothing to do */
debug_text_event(adapter->erp_dbf, 3, "a_asyh_gone");
debug_event(adapter->erp_dbf, 3, &erp_action->action,
sizeof (int));
- retval = 1;
}
-
- return retval;
}
-/*
- * purpose: generic handler for asynchronous events related to erp_action
- * events (normal completion, time-out, dismissing, retry after
- * low memory condition)
- *
- * note: deletion of timer is not required (e.g. in case of a time-out),
- * but a second try does no harm,
- * we leave it in here to allow for greater simplification
- *
- * returns: 0 - there was an action to handle
- * !0 - otherwise
+/**
+ * zfcp_erp_async_handler - wrapper for erp_async_handler_nolock w/ locking
*/
-int
-zfcp_erp_async_handler(struct zfcp_erp_action *erp_action,
- unsigned long set_mask)
+void zfcp_erp_async_handler(struct zfcp_erp_action *erp_action,
+ unsigned long set_mask)
{
struct zfcp_adapter *adapter = erp_action->adapter;
unsigned long flags;
- int retval;
write_lock_irqsave(&adapter->erp_lock, flags);
- retval = zfcp_erp_async_handler_nolock(erp_action, set_mask);
+ zfcp_erp_async_handler_nolock(erp_action, set_mask);
write_unlock_irqrestore(&adapter->erp_lock, flags);
-
- return retval;
}
/*
@@ -987,8 +959,7 @@ zfcp_erp_memwait_handler(unsigned long data)
* action gets an appropriate flag and will be processed
* accordingly
*/
-static void
-zfcp_erp_timeout_handler(unsigned long data)
+void zfcp_erp_timeout_handler(unsigned long data)
{
struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data;
struct zfcp_adapter *adapter = erp_action->adapter;
@@ -999,17 +970,15 @@ zfcp_erp_timeout_handler(unsigned long data)
zfcp_erp_async_handler(erp_action, ZFCP_STATUS_ERP_TIMEDOUT);
}
-/*
- * purpose: is called for an erp_action which needs to be ended
- * though not being done,
- * this is usually required if an higher is generated,
- * action gets an appropriate flag and will be processed
- * accordingly
+/**
+ * zfcp_erp_action_dismiss - dismiss an erp_action
*
- * locks: erp_lock held (thus we need to call another handler variant)
+ * adapter->erp_lock must be held
+ *
+ * Dismissal of an erp_action is usually required if an erp_action of
+ * higher priority is generated.
*/
-static int
-zfcp_erp_action_dismiss(struct zfcp_erp_action *erp_action)
+static void zfcp_erp_action_dismiss(struct zfcp_erp_action *erp_action)
{
struct zfcp_adapter *adapter = erp_action->adapter;
@@ -1017,8 +986,6 @@ zfcp_erp_action_dismiss(struct zfcp_erp_action *erp_action)
debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
zfcp_erp_async_handler_nolock(erp_action, ZFCP_STATUS_ERP_DISMISSED);
-
- return 0;
}
int
@@ -1805,7 +1772,7 @@ zfcp_erp_modify_unit_status(struct zfcp_unit *unit, u32 mask, int set_or_clear)
* purpose: Wrappper for zfcp_erp_port_reopen_all_internal
* used to ensure the correct locking
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
int
@@ -1968,8 +1935,7 @@ zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *erp_action, int close)
&erp_action->adapter->status);
failed_openfcp:
- zfcp_erp_adapter_strategy_close_qdio(erp_action);
- zfcp_erp_adapter_strategy_close_fsf(erp_action);
+ zfcp_close_fsf(erp_action->adapter);
failed_qdio:
out:
return retval;
@@ -2074,69 +2040,6 @@ zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *erp_action)
return retval;
}
-/*
- * function: zfcp_qdio_cleanup
- *
- * purpose: cleans up QDIO operation for the specified adapter
- *
- * returns: 0 - successful cleanup
- * !0 - failed cleanup
- */
-int
-zfcp_erp_adapter_strategy_close_qdio(struct zfcp_erp_action *erp_action)
-{
- int retval = ZFCP_ERP_SUCCEEDED;
- int first_used;
- int used_count;
- struct zfcp_adapter *adapter = erp_action->adapter;
-
- if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
- ZFCP_LOG_DEBUG("error: attempt to shut down inactive QDIO "
- "queues on adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = ZFCP_ERP_FAILED;
- goto out;
- }
-
- /*
- * Get queue_lock and clear QDIOUP flag. Thus it's guaranteed that
- * do_QDIO won't be called while qdio_shutdown is in progress.
- */
-
- write_lock_irq(&adapter->request_queue.queue_lock);
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
- write_unlock_irq(&adapter->request_queue.queue_lock);
-
- debug_text_event(adapter->erp_dbf, 3, "qdio_down2a");
- while (qdio_shutdown(adapter->ccw_device,
- QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS)
- msleep(1000);
- debug_text_event(adapter->erp_dbf, 3, "qdio_down2b");
-
- /*
- * First we had to stop QDIO operation.
- * Now it is safe to take the following actions.
- */
-
- /* Cleanup only necessary when there are unacknowledged buffers */
- if (atomic_read(&adapter->request_queue.free_count)
- < QDIO_MAX_BUFFERS_PER_Q) {
- first_used = (adapter->request_queue.free_index +
- atomic_read(&adapter->request_queue.free_count))
- % QDIO_MAX_BUFFERS_PER_Q;
- used_count = QDIO_MAX_BUFFERS_PER_Q -
- atomic_read(&adapter->request_queue.free_count);
- zfcp_qdio_zero_sbals(adapter->request_queue.buffer,
- first_used, used_count);
- }
- adapter->response_queue.free_index = 0;
- atomic_set(&adapter->response_queue.free_count, 0);
- adapter->request_queue.free_index = 0;
- atomic_set(&adapter->request_queue.free_count, 0);
- adapter->request_queue.distance_from_int = 0;
- out:
- return retval;
-}
static int
zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *erp_action)
@@ -2168,10 +2071,9 @@ zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *erp_action)
atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
&adapter->status);
ZFCP_LOG_DEBUG("Doing exchange config data\n");
- write_lock(&adapter->erp_lock);
+ write_lock_irq(&adapter->erp_lock);
zfcp_erp_action_to_running(erp_action);
- write_unlock(&adapter->erp_lock);
- zfcp_erp_timeout_init(erp_action);
+ write_unlock_irq(&adapter->erp_lock);
if (zfcp_fsf_exchange_config_data(erp_action)) {
retval = ZFCP_ERP_FAILED;
debug_text_event(adapter->erp_dbf, 5, "a_fstx_xf");
@@ -2236,11 +2138,10 @@ zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *erp_action)
adapter = erp_action->adapter;
atomic_clear_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
- write_lock(&adapter->erp_lock);
+ write_lock_irq(&adapter->erp_lock);
zfcp_erp_action_to_running(erp_action);
- write_unlock(&adapter->erp_lock);
+ write_unlock_irq(&adapter->erp_lock);
- zfcp_erp_timeout_init(erp_action);
ret = zfcp_fsf_exchange_port_data(erp_action, adapter, NULL);
if (ret == -EOPNOTSUPP) {
debug_text_event(adapter->erp_dbf, 3, "a_xport_notsupp");
@@ -2258,11 +2159,11 @@ zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *erp_action)
"%s)\n", zfcp_get_busid_by_adapter(adapter));
ret = ZFCP_ERP_FAILED;
}
- if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status)) {
- ZFCP_LOG_INFO("error: exchange port data failed (adapter "
+
+ /* 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));
- ret = ZFCP_ERP_FAILED;
- }
return ret;
}
@@ -2293,35 +2194,6 @@ zfcp_erp_adapter_strategy_open_fsf_statusread(struct zfcp_erp_action
}
/*
- * function: zfcp_fsf_cleanup
- *
- * purpose: cleanup FSF operation for specified adapter
- *
- * returns: 0 - FSF operation successfully cleaned up
- * !0 - failed to cleanup FSF operation for this adapter
- */
-static int
-zfcp_erp_adapter_strategy_close_fsf(struct zfcp_erp_action *erp_action)
-{
- int retval = ZFCP_ERP_SUCCEEDED;
- struct zfcp_adapter *adapter = erp_action->adapter;
-
- /*
- * wake waiting initiators of requests,
- * return SCSI commands (with error status),
- * clean up all requests (synchronously)
- */
- 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,
- ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
-
- return retval;
-}
-
-/*
* function:
*
* purpose: this routine executes the 'Reopen Physical Port' action
@@ -2657,7 +2529,6 @@ zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *erp_action)
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_port *port = erp_action->port;
- zfcp_erp_timeout_init(erp_action);
retval = zfcp_fsf_close_physical_port(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "o_pfstc_nomem");
@@ -2714,7 +2585,6 @@ zfcp_erp_port_strategy_close(struct zfcp_erp_action *erp_action)
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_port *port = erp_action->port;
- zfcp_erp_timeout_init(erp_action);
retval = zfcp_fsf_close_port(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "p_pstc_nomem");
@@ -2752,7 +2622,6 @@ zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *erp_action)
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_port *port = erp_action->port;
- zfcp_erp_timeout_init(erp_action);
retval = zfcp_fsf_open_port(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "p_psto_nomem");
@@ -2790,7 +2659,6 @@ zfcp_erp_port_strategy_open_common_lookup(struct zfcp_erp_action *erp_action)
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_port *port = erp_action->port;
- zfcp_erp_timeout_init(erp_action);
retval = zfcp_ns_gid_pn_request(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "p_pstn_nomem");
@@ -2916,7 +2784,6 @@ zfcp_erp_unit_strategy_close(struct zfcp_erp_action *erp_action)
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_unit *unit = erp_action->unit;
- zfcp_erp_timeout_init(erp_action);
retval = zfcp_fsf_close_unit(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "u_ustc_nomem");
@@ -2957,7 +2824,6 @@ zfcp_erp_unit_strategy_open(struct zfcp_erp_action *erp_action)
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_unit *unit = erp_action->unit;
- zfcp_erp_timeout_init(erp_action);
retval = zfcp_fsf_open_unit(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "u_usto_nomem");
@@ -2982,14 +2848,13 @@ zfcp_erp_unit_strategy_open(struct zfcp_erp_action *erp_action)
return retval;
}
-static inline void
-zfcp_erp_timeout_init(struct zfcp_erp_action *erp_action)
+void zfcp_erp_start_timer(struct zfcp_fsf_req *fsf_req)
{
- init_timer(&erp_action->timer);
- erp_action->timer.function = zfcp_erp_timeout_handler;
- erp_action->timer.data = (unsigned long) erp_action;
- /* jiffies will be added in zfcp_fsf_req_send */
- erp_action->timer.expires = ZFCP_ERP_FSFREQ_TIMEOUT;
+ 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);
}
/*
@@ -3293,10 +3158,8 @@ zfcp_erp_action_cleanup(int action, struct zfcp_adapter *adapter,
}
-static int
-zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
+static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
{
- int retval = 0;
struct zfcp_port *port;
debug_text_event(adapter->erp_dbf, 5, "a_actab");
@@ -3305,14 +3168,10 @@ zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
else
list_for_each_entry(port, &adapter->port_list_head, list)
zfcp_erp_action_dismiss_port(port);
-
- return retval;
}
-static int
-zfcp_erp_action_dismiss_port(struct zfcp_port *port)
+static void zfcp_erp_action_dismiss_port(struct zfcp_port *port)
{
- int retval = 0;
struct zfcp_unit *unit;
struct zfcp_adapter *adapter = port->adapter;
@@ -3323,22 +3182,16 @@ zfcp_erp_action_dismiss_port(struct zfcp_port *port)
else
list_for_each_entry(unit, &port->unit_list_head, list)
zfcp_erp_action_dismiss_unit(unit);
-
- return retval;
}
-static int
-zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit)
+static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit)
{
- int retval = 0;
struct zfcp_adapter *adapter = unit->port->adapter;
debug_text_event(adapter->erp_dbf, 5, "u_actab");
debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof (fcp_lun_t));
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status))
zfcp_erp_action_dismiss(&unit->erp_action);
-
- return retval;
}
static inline void
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index d02366004cdd..b8794d77285d 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -55,7 +55,6 @@ extern void zfcp_unit_dequeue(struct zfcp_unit *);
/******************************* S/390 IO ************************************/
extern int zfcp_ccw_register(void);
-extern void zfcp_ccw_unregister(void);
extern void zfcp_qdio_zero_sbals(struct qdio_buffer **, int, int);
extern int zfcp_qdio_allocate(struct zfcp_adapter *);
@@ -63,7 +62,6 @@ 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 int zfcp_qdio_reqid_check(struct zfcp_adapter *, void *);
extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_req
(struct zfcp_fsf_req *, int, int);
@@ -89,8 +87,8 @@ extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *,
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_request_timeout_handler(unsigned long);
-extern void zfcp_fsf_scsi_er_timeout_handler(unsigned long);
+extern void zfcp_fsf_start_timer(struct zfcp_fsf_req *, unsigned long);
+extern void zfcp_erp_start_timer(struct zfcp_fsf_req *);
extern int 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 *,
@@ -100,8 +98,7 @@ extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
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 *,
- struct timer_list*, int);
+ 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 *);
@@ -125,14 +122,11 @@ extern char *zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *);
extern void set_host_byte(u32 *, char);
extern void set_driver_byte(u32 *, char);
extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
-extern void zfcp_fsf_start_scsi_er_timer(struct zfcp_adapter *);
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 *, struct timer_list *);
-extern int zfcp_scsi_command_sync(struct zfcp_unit *, struct scsi_cmnd *,
- struct timer_list *);
-extern struct scsi_transport_template *zfcp_transport_template;
+ 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 ****************************************/
@@ -156,7 +150,7 @@ extern void zfcp_erp_unit_failed(struct zfcp_unit *);
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 int zfcp_erp_async_handler(struct zfcp_erp_action *, unsigned long);
+extern void zfcp_erp_async_handler(struct zfcp_erp_action *, unsigned long);
extern int zfcp_test_link(struct zfcp_port *);
@@ -187,8 +181,13 @@ extern void zfcp_scsi_dbf_event_result(const char *, int, struct zfcp_adapter *,
struct zfcp_fsf_req *);
extern void zfcp_scsi_dbf_event_abort(const char *, struct zfcp_adapter *,
struct scsi_cmnd *, struct zfcp_fsf_req *,
- struct zfcp_fsf_req *);
+ unsigned long);
extern void zfcp_scsi_dbf_event_devreset(const char *, u8, struct zfcp_unit *,
struct scsi_cmnd *);
+extern void zfcp_reqlist_add(struct zfcp_adapter *, struct zfcp_fsf_req *);
+extern void zfcp_reqlist_remove(struct zfcp_adapter *, unsigned long);
+extern struct zfcp_fsf_req *zfcp_reqlist_ismember(struct zfcp_adapter *,
+ unsigned long);
+extern int zfcp_reqlist_isempty(struct zfcp_adapter *);
#endif /* ZFCP_EXT_H */
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 6335f9229184..277826cdd0c8 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -42,14 +42,13 @@ static inline int zfcp_fsf_req_sbal_check(
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 *, struct timer_list *);
+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_adapter *,
struct fsf_link_down_info *);
static int zfcp_fsf_req_dispatch(struct zfcp_fsf_req *);
-static void zfcp_fsf_req_dismiss(struct zfcp_fsf_req *);
/* association between FSF command and FSF QTCB type */
static u32 fsf_qtcb_type[] = {
@@ -101,14 +100,19 @@ zfcp_fsf_req_alloc(mempool_t *pool, int req_flags)
if (req_flags & ZFCP_REQ_NO_QTCB)
size = sizeof(struct zfcp_fsf_req);
else
- size = sizeof(struct zfcp_fsf_req_pool_element);
+ size = sizeof(struct zfcp_fsf_req_qtcb);
- if (likely(pool != NULL))
+ if (likely(pool))
ptr = mempool_alloc(pool, GFP_ATOMIC);
- else
- ptr = kmalloc(size, 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,
+ SLAB_ATOMIC);
+ }
- if (unlikely(NULL == ptr))
+ if (unlikely(!ptr))
goto out;
memset(ptr, 0, size);
@@ -116,9 +120,8 @@ zfcp_fsf_req_alloc(mempool_t *pool, int req_flags)
if (req_flags & ZFCP_REQ_NO_QTCB) {
fsf_req = (struct zfcp_fsf_req *) ptr;
} else {
- fsf_req = &((struct zfcp_fsf_req_pool_element *) ptr)->fsf_req;
- fsf_req->qtcb =
- &((struct zfcp_fsf_req_pool_element *) ptr)->qtcb;
+ fsf_req = &((struct zfcp_fsf_req_qtcb *) ptr)->fsf_req;
+ fsf_req->qtcb = &((struct zfcp_fsf_req_qtcb *) ptr)->qtcb;
}
fsf_req->pool = pool;
@@ -140,55 +143,63 @@ zfcp_fsf_req_alloc(mempool_t *pool, int req_flags)
void
zfcp_fsf_req_free(struct zfcp_fsf_req *fsf_req)
{
- if (likely(fsf_req->pool != NULL))
+ if (likely(fsf_req->pool)) {
mempool_free(fsf_req, fsf_req->pool);
- else
- kfree(fsf_req);
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:
- *
- * note: qdio queues shall be down (no ongoing inbound processing)
- */
-int
-zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter)
-{
- struct zfcp_fsf_req *fsf_req, *tmp;
- unsigned long flags;
- LIST_HEAD(remove_queue);
-
- spin_lock_irqsave(&adapter->fsf_req_list_lock, flags);
- list_splice_init(&adapter->fsf_req_list_head, &remove_queue);
- atomic_set(&adapter->fsf_reqs_active, 0);
- spin_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
+ return;
+ }
- list_for_each_entry_safe(fsf_req, tmp, &remove_queue, list) {
- list_del(&fsf_req->list);
- zfcp_fsf_req_dismiss(fsf_req);
+ if (fsf_req->qtcb) {
+ kmem_cache_free(zfcp_data.fsf_req_qtcb_cache, fsf_req);
+ return;
}
- return 0;
+ kfree(fsf_req);
}
-/*
- * function:
- *
- * purpose:
- *
- * returns:
+/**
+ * zfcp_fsf_req_dismiss - dismiss a single fsf request
*/
-static void
-zfcp_fsf_req_dismiss(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_req_dismiss(struct zfcp_adapter *adapter,
+ struct zfcp_fsf_req *fsf_req,
+ unsigned int counter)
{
+ u64 dbg_tmp[2];
+
+ dbg_tmp[0] = (u64) atomic_read(&adapter->reqs_active);
+ dbg_tmp[1] = (u64) counter;
+ debug_event(adapter->erp_dbf, 4, (void *) dbg_tmp, 16);
+ list_del(&fsf_req->list);
fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
zfcp_fsf_req_complete(fsf_req);
}
+/**
+ * zfcp_fsf_req_dismiss_all - dismiss all remaining fsf requests
+ */
+int zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter)
+{
+ struct zfcp_fsf_req *request, *tmp;
+ unsigned long flags;
+ unsigned int i, counter;
+
+ spin_lock_irqsave(&adapter->req_list_lock, flags);
+ atomic_set(&adapter->reqs_active, 0);
+ for (i=0; i<REQUEST_LIST_SIZE; i++) {
+ if (list_empty(&adapter->req_list[i]))
+ continue;
+
+ counter = 0;
+ list_for_each_entry_safe(request, tmp,
+ &adapter->req_list[i], list) {
+ zfcp_fsf_req_dismiss(adapter, request, counter);
+ counter++;
+ }
+ }
+ spin_unlock_irqrestore(&adapter->req_list_lock, flags);
+
+ return 0;
+}
+
/*
* function: zfcp_fsf_req_complete
*
@@ -214,8 +225,10 @@ zfcp_fsf_req_complete(struct zfcp_fsf_req *fsf_req)
*/
zfcp_fsf_status_read_handler(fsf_req);
goto out;
- } else
+ } else {
+ del_timer(&fsf_req->timer);
zfcp_fsf_protstatus_eval(fsf_req);
+ }
/*
* fsf_req may be deleted due to waking up functions, so
@@ -774,8 +787,7 @@ zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags)
sbale->addr = (void *) status_buffer;
sbale->length = sizeof(struct fsf_status_read_buffer);
- /* start QDIO request for this FSF request */
- retval = zfcp_fsf_req_send(fsf_req, NULL);
+ retval = zfcp_fsf_req_send(fsf_req);
if (retval) {
ZFCP_LOG_DEBUG("error: Could not set-up unsolicited status "
"environment.\n");
@@ -1101,8 +1113,8 @@ zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
struct zfcp_unit *unit, int req_flags)
{
volatile struct qdio_buffer_element *sbale;
- unsigned long lock_flags;
struct zfcp_fsf_req *fsf_req = NULL;
+ unsigned long lock_flags;
int retval = 0;
/* setup new FSF request */
@@ -1132,12 +1144,9 @@ zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
/* set handle of request which should be aborted */
fsf_req->qtcb->bottom.support.req_handle = (u64) old_req_id;
- /* start QDIO request for this FSF request */
-
- zfcp_fsf_start_scsi_er_timer(adapter);
- retval = zfcp_fsf_req_send(fsf_req, NULL);
+ zfcp_fsf_start_timer(fsf_req, ZFCP_SCSI_ER_TIMEOUT);
+ retval = zfcp_fsf_req_send(fsf_req);
if (retval) {
- del_timer(&adapter->scsi_er_timer);
ZFCP_LOG_INFO("error: Failed to send abort command request "
"on adapter %s, port 0x%016Lx, unit 0x%016Lx\n",
zfcp_get_busid_by_adapter(adapter),
@@ -1173,8 +1182,6 @@ zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *new_fsf_req)
unsigned char status_qual =
new_fsf_req->qtcb->header.fsf_status_qual.word[0];
- del_timer(&new_fsf_req->adapter->scsi_er_timer);
-
if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
/* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */
goto skip_fsfstatus;
@@ -1380,11 +1387,6 @@ zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
goto failed_req;
}
- if (erp_action != NULL) {
- erp_action->fsf_req = fsf_req;
- fsf_req->erp_action = erp_action;
- }
-
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)){
@@ -1451,8 +1453,14 @@ zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
zfcp_san_dbf_event_ct_request(fsf_req);
- /* start QDIO request for this FSF request */
- ret = zfcp_fsf_req_send(fsf_req, ct->timer);
+ 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);
+
+ ret = zfcp_fsf_req_send(fsf_req);
if (ret) {
ZFCP_LOG_DEBUG("error: initiation of CT request failed "
"(adapter %s, port 0x%016Lx)\n",
@@ -1749,8 +1757,8 @@ zfcp_fsf_send_els(struct zfcp_send_els *els)
zfcp_san_dbf_event_els_request(fsf_req);
- /* start QDIO request for this FSF request */
- ret = zfcp_fsf_req_send(fsf_req, els->timer);
+ 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%08x)\n",
@@ -1947,6 +1955,7 @@ int
zfcp_fsf_exchange_config_data(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;
@@ -1955,7 +1964,7 @@ zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
FSF_QTCB_EXCHANGE_CONFIG_DATA,
ZFCP_REQ_AUTO_CLEANUP,
erp_action->adapter->pool.fsf_req_erp,
- &lock_flags, &(erp_action->fsf_req));
+ &lock_flags, &fsf_req);
if (retval < 0) {
ZFCP_LOG_INFO("error: Could not create exchange configuration "
"data request for adapter %s.\n",
@@ -1963,26 +1972,26 @@ zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
goto out;
}
- sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
- erp_action->fsf_req->sbal_curr, 0);
+ 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;
- erp_action->fsf_req->erp_action = erp_action;
- erp_action->fsf_req->qtcb->bottom.config.feature_selection =
+ fsf_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;
- /* start QDIO request for this FSF request */
- retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+ zfcp_erp_start_timer(fsf_req);
+ retval = zfcp_fsf_req_send(fsf_req);
if (retval) {
ZFCP_LOG_INFO
("error: Could not send exchange configuration data "
"command on the adapter %s\n",
zfcp_get_busid_by_adapter(erp_action->adapter));
- zfcp_fsf_req_free(erp_action->fsf_req);
+ zfcp_fsf_req_free(fsf_req);
erp_action->fsf_req = NULL;
goto out;
}
@@ -2212,10 +2221,9 @@ zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action,
struct fsf_qtcb_bottom_port *data)
{
volatile struct qdio_buffer_element *sbale;
- int retval = 0;
- unsigned long lock_flags;
struct zfcp_fsf_req *fsf_req;
- struct timer_list *timer;
+ unsigned long lock_flags;
+ int retval = 0;
if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT)) {
ZFCP_LOG_INFO("error: exchange port data "
@@ -2227,7 +2235,7 @@ zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action,
/* setup new FSF request */
retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
erp_action ? ZFCP_REQ_AUTO_CLEANUP : 0,
- 0, &lock_flags, &fsf_req);
+ NULL, &lock_flags, &fsf_req);
if (retval < 0) {
ZFCP_LOG_INFO("error: Out of resources. Could not create an "
"exchange port data request for"
@@ -2248,22 +2256,11 @@ zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action,
if (erp_action) {
erp_action->fsf_req = fsf_req;
fsf_req->erp_action = erp_action;
- timer = &erp_action->timer;
- } else {
- timer = kmalloc(sizeof(struct timer_list), GFP_ATOMIC);
- if (!timer) {
- write_unlock_irqrestore(&adapter->request_queue.queue_lock,
- lock_flags);
- zfcp_fsf_req_free(fsf_req);
- return -ENOMEM;
- }
- init_timer(timer);
- timer->function = zfcp_fsf_request_timeout_handler;
- timer->data = (unsigned long) adapter;
- timer->expires = ZFCP_FSF_REQUEST_TIMEOUT;
- }
+ zfcp_erp_start_timer(fsf_req);
+ } else
+ zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
- retval = zfcp_fsf_req_send(fsf_req, timer);
+ retval = zfcp_fsf_req_send(fsf_req);
if (retval) {
ZFCP_LOG_INFO("error: Could not send an exchange port data "
"command on the adapter %s\n",
@@ -2271,8 +2268,6 @@ zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action,
zfcp_fsf_req_free(fsf_req);
if (erp_action)
erp_action->fsf_req = NULL;
- else
- kfree(timer);
write_unlock_irqrestore(&adapter->request_queue.queue_lock,
lock_flags);
return retval;
@@ -2283,9 +2278,7 @@ zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action,
if (!erp_action) {
wait_event(fsf_req->completion_wq,
fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
- del_timer_sync(timer);
zfcp_fsf_req_free(fsf_req);
- kfree(timer);
}
return retval;
}
@@ -2367,6 +2360,7 @@ 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;
@@ -2375,7 +2369,7 @@ zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
FSF_QTCB_OPEN_PORT_WITH_DID,
ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
erp_action->adapter->pool.fsf_req_erp,
- &lock_flags, &(erp_action->fsf_req));
+ &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",
@@ -2384,24 +2378,24 @@ zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
goto out;
}
- sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
- erp_action->fsf_req->sbal_curr, 0);
+ 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;
- erp_action->fsf_req->qtcb->bottom.support.d_id = erp_action->port->d_id;
+ fsf_req->qtcb->bottom.support.d_id = erp_action->port->d_id;
atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status);
- erp_action->fsf_req->data = (unsigned long) erp_action->port;
- erp_action->fsf_req->erp_action = erp_action;
+ fsf_req->data = (unsigned long) erp_action->port;
+ fsf_req->erp_action = erp_action;
+ erp_action->fsf_req = fsf_req;
- /* start QDIO request for this FSF request */
- retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+ 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(erp_action->fsf_req);
+ zfcp_fsf_req_free(fsf_req);
erp_action->fsf_req = NULL;
goto out;
}
@@ -2623,6 +2617,7 @@ 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;
@@ -2631,7 +2626,7 @@ zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
FSF_QTCB_CLOSE_PORT,
ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
erp_action->adapter->pool.fsf_req_erp,
- &lock_flags, &(erp_action->fsf_req));
+ &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",
@@ -2640,25 +2635,25 @@ zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
goto out;
}
- sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
- erp_action->fsf_req->sbal_curr, 0);
+ 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;
atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status);
- erp_action->fsf_req->data = (unsigned long) erp_action->port;
- erp_action->fsf_req->erp_action = erp_action;
- erp_action->fsf_req->qtcb->header.port_handle =
- erp_action->port->handle;
-
- /* start QDIO request for this FSF request */
- retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+ 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);
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(erp_action->fsf_req);
+ zfcp_fsf_req_free(fsf_req);
erp_action->fsf_req = NULL;
goto out;
}
@@ -2755,16 +2750,17 @@ zfcp_fsf_close_port_handler(struct zfcp_fsf_req *fsf_req)
int
zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
{
- int retval = 0;
- unsigned long lock_flags;
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, &erp_action->fsf_req);
+ &lock_flags, &fsf_req);
if (retval < 0) {
ZFCP_LOG_INFO("error: Could not create close physical port "
"request (adapter %s, port 0x%016Lx)\n",
@@ -2774,8 +2770,7 @@ zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
goto out;
}
- sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
- erp_action->fsf_req->sbal_curr, 0);
+ 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;
@@ -2783,20 +2778,19 @@ zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING,
&erp_action->port->status);
/* save a pointer to this port */
- erp_action->fsf_req->data = (unsigned long) erp_action->port;
- /* port to be closed */
- erp_action->fsf_req->qtcb->header.port_handle =
- erp_action->port->handle;
- erp_action->fsf_req->erp_action = erp_action;
-
- /* start QDIO request for this FSF request */
- retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+ 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);
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(erp_action->fsf_req);
+ zfcp_fsf_req_free(fsf_req);
erp_action->fsf_req = NULL;
goto out;
}
@@ -2961,6 +2955,7 @@ 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;
@@ -2969,7 +2964,7 @@ zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
FSF_QTCB_OPEN_LUN,
ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
erp_action->adapter->pool.fsf_req_erp,
- &lock_flags, &(erp_action->fsf_req));
+ &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",
@@ -2979,24 +2974,22 @@ zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
goto out;
}
- sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
- erp_action->fsf_req->sbal_curr, 0);
+ 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;
- erp_action->fsf_req->qtcb->header.port_handle =
- erp_action->port->handle;
- erp_action->fsf_req->qtcb->bottom.support.fcp_lun =
- erp_action->unit->fcp_lun;
+ 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))
- erp_action->fsf_req->qtcb->bottom.support.option =
+ fsf_req->qtcb->bottom.support.option =
FSF_OPEN_LUN_SUPPRESS_BOXING;
atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status);
- erp_action->fsf_req->data = (unsigned long) erp_action->unit;
- erp_action->fsf_req->erp_action = erp_action;
+ fsf_req->data = (unsigned long) erp_action->unit;
+ fsf_req->erp_action = erp_action;
+ erp_action->fsf_req = fsf_req;
- /* start QDIO request for this FSF request */
- retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+ zfcp_erp_start_timer(fsf_req);
+ retval = zfcp_fsf_req_send(erp_action->fsf_req);
if (retval) {
ZFCP_LOG_INFO("error: Could not send an open unit request "
"on the adapter %s, port 0x%016Lx for "
@@ -3004,7 +2997,7 @@ zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
zfcp_get_busid_by_adapter(erp_action->adapter),
erp_action->port->wwpn,
erp_action->unit->fcp_lun);
- zfcp_fsf_req_free(erp_action->fsf_req);
+ zfcp_fsf_req_free(fsf_req);
erp_action->fsf_req = NULL;
goto out;
}
@@ -3297,6 +3290,7 @@ int
zfcp_fsf_close_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;
@@ -3305,7 +3299,7 @@ zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
FSF_QTCB_CLOSE_LUN,
ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
erp_action->adapter->pool.fsf_req_erp,
- &lock_flags, &(erp_action->fsf_req));
+ &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",
@@ -3315,27 +3309,26 @@ zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
goto out;
}
- sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
- erp_action->fsf_req->sbal_curr, 0);
+ 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;
- erp_action->fsf_req->qtcb->header.port_handle =
- erp_action->port->handle;
- erp_action->fsf_req->qtcb->header.lun_handle = erp_action->unit->handle;
+ 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);
- erp_action->fsf_req->data = (unsigned long) erp_action->unit;
- erp_action->fsf_req->erp_action = erp_action;
+ fsf_req->data = (unsigned long) erp_action->unit;
+ fsf_req->erp_action = erp_action;
+ erp_action->fsf_req = fsf_req;
- /* start QDIO request for this FSF request */
- retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
+ zfcp_erp_start_timer(fsf_req);
+ retval = zfcp_fsf_req_send(erp_action->fsf_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(erp_action->fsf_req);
+ zfcp_fsf_req_free(fsf_req);
erp_action->fsf_req = NULL;
goto out;
}
@@ -3488,7 +3481,7 @@ int
zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
struct zfcp_unit *unit,
struct scsi_cmnd * scsi_cmnd,
- struct timer_list *timer, int req_flags)
+ int use_timer, int req_flags)
{
struct zfcp_fsf_req *fsf_req = NULL;
struct fcp_cmnd_iu *fcp_cmnd_iu;
@@ -3516,7 +3509,7 @@ zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
fsf_req->unit = unit;
/* associate FSF request with SCSI request (for look up on abort) */
- scsi_cmnd->host_scribble = (char *) fsf_req;
+ scsi_cmnd->host_scribble = (unsigned char *) fsf_req->req_id;
/* associate SCSI command with FSF request */
fsf_req->data = (unsigned long) scsi_cmnd;
@@ -3629,11 +3622,10 @@ zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
(char *) scsi_cmnd->cmnd, scsi_cmnd->cmd_len);
- /*
- * start QDIO request for this FSF request
- * covered by an SBALE)
- */
- retval = zfcp_fsf_req_send(fsf_req, timer);
+ 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",
@@ -3718,11 +3710,9 @@ zfcp_fsf_send_fcp_command_task_management(struct zfcp_adapter *adapter,
fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
fcp_cmnd_iu->task_management_flags = tm_flags;
- /* start QDIO request for this FSF request */
- zfcp_fsf_start_scsi_er_timer(adapter);
- retval = zfcp_fsf_req_send(fsf_req, NULL);
+ zfcp_fsf_start_timer(fsf_req, ZFCP_SCSI_ER_TIMEOUT);
+ retval = zfcp_fsf_req_send(fsf_req);
if (retval) {
- del_timer(&adapter->scsi_er_timer);
ZFCP_LOG_INFO("error: Could not send an FCP-command (task "
"management) on adapter %s, port 0x%016Lx for "
"unit LUN 0x%016Lx\n",
@@ -4226,7 +4216,6 @@ zfcp_fsf_send_fcp_command_task_management_handler(struct zfcp_fsf_req *fsf_req)
char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu);
struct zfcp_unit *unit = (struct zfcp_unit *) fsf_req->data;
- del_timer(&fsf_req->adapter->scsi_er_timer);
if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
goto skip_fsfstatus;
@@ -4295,7 +4284,6 @@ zfcp_fsf_control_file(struct zfcp_adapter *adapter,
struct zfcp_fsf_req *fsf_req;
struct fsf_qtcb_bottom_support *bottom;
volatile struct qdio_buffer_element *sbale;
- struct timer_list *timer;
unsigned long lock_flags;
int req_flags = 0;
int direction;
@@ -4327,12 +4315,6 @@ zfcp_fsf_control_file(struct zfcp_adapter *adapter,
goto out;
}
- timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
- if (!timer) {
- retval = -ENOMEM;
- goto out;
- }
-
retval = zfcp_fsf_req_create(adapter, fsf_command, req_flags,
NULL, &lock_flags, &fsf_req);
if (retval < 0) {
@@ -4367,12 +4349,8 @@ zfcp_fsf_control_file(struct zfcp_adapter *adapter,
} else
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
- init_timer(timer);
- timer->function = zfcp_fsf_request_timeout_handler;
- timer->data = (unsigned long) adapter;
- timer->expires = ZFCP_FSF_REQUEST_TIMEOUT;
-
- retval = zfcp_fsf_req_send(fsf_req, timer);
+ 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",
@@ -4392,15 +4370,12 @@ zfcp_fsf_control_file(struct zfcp_adapter *adapter,
fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
*fsf_req_ptr = fsf_req;
- del_timer_sync(timer);
- goto free_timer;
+ goto out;
free_fsf_req:
zfcp_fsf_req_free(fsf_req);
unlock_queue_lock:
write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
- free_timer:
- kfree(timer);
out:
return retval;
}
@@ -4592,12 +4567,14 @@ static inline 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 = (unsigned long)fsf_req;
+ 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_type =
+ fsf_qtcb_type[fsf_req->fsf_command];
fsf_req->qtcb->prefix.qtcb_version = ZFCP_QTCB_VERSION;
- fsf_req->qtcb->header.req_handle = (unsigned long)fsf_req;
+ fsf_req->qtcb->header.req_handle = fsf_req->req_id;
fsf_req->qtcb->header.fsf_command = fsf_req->fsf_command;
}
}
@@ -4668,8 +4645,15 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags,
fsf_req->adapter = adapter;
fsf_req->fsf_command = fsf_cmd;
+ INIT_LIST_HEAD(&fsf_req->list);
+
+ /* 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_fsf_req_qtcb_init(fsf_req);
+ init_timer(&fsf_req->timer);
+ zfcp_fsf_req_qtcb_init(fsf_req);
/* initialize waitqueue which may be used to wait on
this request completion */
@@ -4707,7 +4691,7 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags,
sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
/* setup common SBALE fields */
- sbale[0].addr = fsf_req;
+ 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;
@@ -4739,15 +4723,14 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags,
* returns: 0 - request transfer succesfully started
* !0 - start of request transfer failed
*/
-static int
-zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer)
+static int zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req)
{
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;
- unsigned long flags;
+ u64 dbg_tmp[2];
int retval = 0;
adapter = fsf_req->adapter;
@@ -4761,19 +4744,13 @@ zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer)
ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) sbale[1].addr,
sbale[1].length);
- /* put allocated FSF request at list tail */
- spin_lock_irqsave(&adapter->fsf_req_list_lock, flags);
- list_add_tail(&fsf_req->list, &adapter->fsf_req_list_head);
- spin_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
+ /* 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);
inc_seq_no = (fsf_req->qtcb != NULL);
- /* figure out expiration time of timeout and start timeout */
- if (unlikely(timer)) {
- timer->expires += jiffies;
- add_timer(timer);
- }
-
ZFCP_LOG_TRACE("request queue of adapter %s: "
"next free SBAL is %i, %i free SBALs\n",
zfcp_get_busid_by_adapter(adapter),
@@ -4803,31 +4780,25 @@ zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer)
QDIO_FLAG_SYNC_OUTPUT,
0, fsf_req->sbal_first, fsf_req->sbal_number, NULL);
+ dbg_tmp[0] = (unsigned long) sbale[0].addr;
+ dbg_tmp[1] = (u64) retval;
+ debug_event(adapter->erp_dbf, 4, (void *) dbg_tmp, 16);
+
if (unlikely(retval)) {
/* Queues are down..... */
retval = -EIO;
- /*
- * FIXME(potential race):
- * timer might be expired (absolutely unlikely)
- */
- if (timer)
- del_timer(timer);
- spin_lock_irqsave(&adapter->fsf_req_list_lock, flags);
- list_del(&fsf_req->list);
- spin_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
- /*
- * adjust the number of free SBALs in request queue as well as
- * position of first one
- */
+ del_timer(&fsf_req->timer);
+ spin_lock(&adapter->req_list_lock);
+ zfcp_reqlist_remove(adapter, fsf_req->req_id);
+ 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; /* increase */
+ 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_LOG_DEBUG
- ("error: do_QDIO failed. Buffers could not be enqueued "
- "to request queue.\n");
+ zfcp_erp_adapter_reopen(adapter, 0);
} else {
req_queue->distance_from_int = new_distance_from_int;
/*
@@ -4843,7 +4814,7 @@ zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer)
adapter->fsf_req_seq_no++;
/* count FSF requests pending */
- atomic_inc(&adapter->fsf_reqs_active);
+ atomic_inc(&adapter->reqs_active);
}
return retval;
}
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
index 345a191926a4..dbd9f48e863e 100644
--- a/drivers/s390/scsi/zfcp_qdio.c
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -282,6 +282,37 @@ zfcp_qdio_request_handler(struct ccw_device *ccw_device,
return;
}
+/**
+ * zfcp_qdio_reqid_check - checks for valid reqids or unsolicited status
+ */
+static int zfcp_qdio_reqid_check(struct zfcp_adapter *adapter,
+ unsigned long req_id)
+{
+ struct zfcp_fsf_req *fsf_req;
+ unsigned long flags;
+
+ debug_long_event(adapter->erp_dbf, 4, req_id);
+
+ spin_lock_irqsave(&adapter->req_list_lock, flags);
+ fsf_req = zfcp_reqlist_ismember(adapter, req_id);
+
+ if (!fsf_req) {
+ spin_unlock_irqrestore(&adapter->req_list_lock, flags);
+ ZFCP_LOG_NORMAL("error: unknown request id (%ld).\n", req_id);
+ zfcp_erp_adapter_reopen(adapter, 0);
+ return -EINVAL;
+ }
+
+ zfcp_reqlist_remove(adapter, req_id);
+ atomic_dec(&adapter->reqs_active);
+ spin_unlock_irqrestore(&adapter->req_list_lock, flags);
+
+ /* finish the FSF request */
+ zfcp_fsf_req_complete(fsf_req);
+
+ return 0;
+}
+
/*
* function: zfcp_qdio_response_handler
*
@@ -344,7 +375,7 @@ zfcp_qdio_response_handler(struct ccw_device *ccw_device,
/* look for QDIO request identifiers in SB */
buffere = &buffer->element[buffere_index];
retval = zfcp_qdio_reqid_check(adapter,
- (void *) buffere->addr);
+ (unsigned long) buffere->addr);
if (retval) {
ZFCP_LOG_NORMAL("bug: unexpected inbound "
@@ -415,51 +446,6 @@ zfcp_qdio_response_handler(struct ccw_device *ccw_device,
return;
}
-/*
- * function: zfcp_qdio_reqid_check
- *
- * purpose: checks for valid reqids or unsolicited status
- *
- * returns: 0 - valid request id or unsolicited status
- * !0 - otherwise
- */
-int
-zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, void *sbale_addr)
-{
- struct zfcp_fsf_req *fsf_req;
-
- /* invalid (per convention used in this driver) */
- if (unlikely(!sbale_addr)) {
- ZFCP_LOG_NORMAL("bug: invalid reqid\n");
- return -EINVAL;
- }
-
- /* valid request id and thus (hopefully :) valid fsf_req address */
- fsf_req = (struct zfcp_fsf_req *) sbale_addr;
-
- /* serialize with zfcp_fsf_req_dismiss_all */
- spin_lock(&adapter->fsf_req_list_lock);
- if (list_empty(&adapter->fsf_req_list_head)) {
- spin_unlock(&adapter->fsf_req_list_lock);
- return 0;
- }
- list_del(&fsf_req->list);
- atomic_dec(&adapter->fsf_reqs_active);
- spin_unlock(&adapter->fsf_req_list_lock);
-
- if (unlikely(adapter != fsf_req->adapter)) {
- ZFCP_LOG_NORMAL("bug: invalid reqid (fsf_req=%p, "
- "fsf_req->adapter=%p, adapter=%p)\n",
- fsf_req, fsf_req->adapter, adapter);
- return -EINVAL;
- }
-
- /* finish the FSF request */
- zfcp_fsf_req_complete(fsf_req);
-
- return 0;
-}
-
/**
* zfcp_qdio_sbale_get - return pointer to SBALE of qdio_queue
* @queue: queue from which SBALE should be returned
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 46e14f22ec18..7cafa34e4c7f 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -30,7 +30,6 @@ 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_bus_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 *);
@@ -40,37 +39,27 @@ static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int,
static struct device_attribute *zfcp_sysfs_sdev_attrs[];
-struct scsi_transport_template *zfcp_transport_template;
-
struct zfcp_data zfcp_data = {
.scsi_host_template = {
- name: ZFCP_NAME,
- proc_name: "zfcp",
- proc_info: NULL,
- detect: NULL,
- 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_bus_reset_handler: zfcp_scsi_eh_bus_reset_handler,
- eh_host_reset_handler: zfcp_scsi_eh_host_reset_handler,
- /* FIXME(openfcp): Tune */
- can_queue: 4096,
- this_id: -1,
- /*
- * FIXME:
- * one less? can zfcp_create_sbale cope with it?
- */
- sg_tablesize: ZFCP_MAX_SBALES_PER_REQ,
- cmd_per_lun: 1,
- unchecked_isa_dma: 0,
- use_clustering: 1,
- sdev_attrs: zfcp_sysfs_sdev_attrs,
+ .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_bus_reset_handler = zfcp_scsi_eh_host_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,
},
.driver_version = ZFCP_VERSION,
- /* rest initialised with zeros */
};
/* Find start of Response Information in FCP response unit*/
@@ -177,8 +166,14 @@ zfcp_scsi_slave_alloc(struct scsi_device *sdp)
return retval;
}
-static void
-zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
+/**
+ * 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;
@@ -186,6 +181,7 @@ zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status);
sdpnt->hostdata = NULL;
unit->device = NULL;
+ zfcp_erp_unit_failed(unit);
zfcp_unit_put(unit);
} else {
ZFCP_LOG_NORMAL("bug: no unit associated with SCSI device at "
@@ -235,7 +231,7 @@ zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result)
*/
int
zfcp_scsi_command_async(struct zfcp_adapter *adapter, struct zfcp_unit *unit,
- struct scsi_cmnd *scpnt, struct timer_list *timer)
+ struct scsi_cmnd *scpnt, int use_timer)
{
int tmp;
int retval;
@@ -271,7 +267,7 @@ zfcp_scsi_command_async(struct zfcp_adapter *adapter, struct zfcp_unit *unit,
goto out;
}
- tmp = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, timer,
+ tmp = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, use_timer,
ZFCP_REQ_AUTO_CLEANUP);
if (unlikely(tmp < 0)) {
@@ -295,21 +291,22 @@ zfcp_scsi_command_sync_handler(struct scsi_cmnd *scpnt)
* 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
- * @timer: timer to be started if request is successfully initiated
+ * @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,
- struct timer_list *timer)
+ int use_timer)
{
int ret;
DECLARE_COMPLETION(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, timer);
+ ret = zfcp_scsi_command_async(unit->port->adapter, unit, scpnt,
+ use_timer);
if (ret == 0)
wait_for_completion(&wait);
@@ -345,7 +342,7 @@ zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
unit = (struct zfcp_unit *) scpnt->device->hostdata;
- return zfcp_scsi_command_async(adapter, unit, scpnt, NULL);
+ return zfcp_scsi_command_async(adapter, unit, scpnt, 0);
}
static struct zfcp_unit *
@@ -382,16 +379,15 @@ zfcp_unit_lookup(struct zfcp_adapter *adapter, int channel, unsigned int id,
* will handle late commands. (Usually, the normal completion of late
* commands is ignored with respect to the running abort operation.)
*/
-int
-zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
+int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
{
struct Scsi_Host *scsi_host;
struct zfcp_adapter *adapter;
struct zfcp_unit *unit;
- int retval = SUCCESS;
- struct zfcp_fsf_req *new_fsf_req = NULL;
- struct zfcp_fsf_req *old_fsf_req;
+ struct zfcp_fsf_req *fsf_req;
unsigned long flags;
+ unsigned long old_req_id;
+ int retval = SUCCESS;
scsi_host = scpnt->device->host;
adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
@@ -403,55 +399,47 @@ zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
/* avoid race condition between late normal completion and abort */
write_lock_irqsave(&adapter->abort_lock, flags);
- /*
- * Check whether command has just completed and can not be aborted.
- * Even if the command has just been completed late, we can access
- * scpnt since the SCSI stack does not release it at least until
- * this routine returns. (scpnt is parameter passed to this routine
- * and must not disappear during abort even on late completion.)
- */
- old_fsf_req = (struct zfcp_fsf_req *) scpnt->host_scribble;
- if (!old_fsf_req) {
+ /* Check whether corresponding fsf_req is still pending */
+ spin_lock(&adapter->req_list_lock);
+ fsf_req = zfcp_reqlist_ismember(adapter, (unsigned long)
+ scpnt->host_scribble);
+ 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, NULL);
+ zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, NULL, 0);
retval = SUCCESS;
goto out;
}
- old_fsf_req->data = 0;
- old_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTING;
+ fsf_req->data = 0;
+ 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 */
+ /* don't access old fsf_req after releasing the abort_lock */
write_unlock_irqrestore(&adapter->abort_lock, flags);
- /* call FSF routine which does the abort */
- new_fsf_req = zfcp_fsf_abort_fcp_command((unsigned long) old_fsf_req,
- adapter, unit, 0);
- if (!new_fsf_req) {
+
+ 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_fsf_req);
+ old_req_id);
retval = FAILED;
goto out;
}
- /* wait for completion of abort */
- __wait_event(new_fsf_req->completion_wq,
- new_fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ __wait_event(fsf_req->completion_wq,
+ fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
- /* status should be valid since signals were not permitted */
- if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) {
- zfcp_scsi_dbf_event_abort("okay", adapter, scpnt, new_fsf_req,
- NULL);
+ if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) {
+ zfcp_scsi_dbf_event_abort("okay", adapter, scpnt, fsf_req, 0);
retval = SUCCESS;
- } else if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) {
- zfcp_scsi_dbf_event_abort("lte2", adapter, scpnt, new_fsf_req,
- NULL);
+ } 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, new_fsf_req,
- NULL);
+ zfcp_scsi_dbf_event_abort("fail", adapter, scpnt, fsf_req, 0);
retval = FAILED;
}
- zfcp_fsf_req_free(new_fsf_req);
+ zfcp_fsf_req_free(fsf_req);
out:
return retval;
}
@@ -550,33 +538,19 @@ zfcp_task_management_function(struct zfcp_unit *unit, u8 tm_flags,
}
/**
- * zfcp_scsi_eh_bus_reset_handler - reset bus (reopen adapter)
+ * zfcp_scsi_eh_host_reset_handler - handler for host and bus reset
*/
-int
-zfcp_scsi_eh_bus_reset_handler(struct scsi_cmnd *scpnt)
+int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
{
- struct zfcp_unit *unit = (struct zfcp_unit*) scpnt->device->hostdata;
- struct zfcp_adapter *adapter = unit->port->adapter;
-
- ZFCP_LOG_NORMAL("bus reset because of problems with "
- "unit 0x%016Lx\n", unit->fcp_lun);
- zfcp_erp_adapter_reopen(adapter, 0);
- zfcp_erp_wait(adapter);
-
- return SUCCESS;
-}
+ struct zfcp_unit *unit;
+ struct zfcp_adapter *adapter;
-/**
- * zfcp_scsi_eh_host_reset_handler - reset host (reopen adapter)
- */
-int
-zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
-{
- struct zfcp_unit *unit = (struct zfcp_unit*) scpnt->device->hostdata;
- struct zfcp_adapter *adapter = unit->port->adapter;
+ unit = (struct zfcp_unit*) scpnt->device->hostdata;
+ adapter = unit->port->adapter;
- ZFCP_LOG_NORMAL("host reset because of problems with "
+ ZFCP_LOG_NORMAL("host/bus reset because of problems with "
"unit 0x%016Lx\n", unit->fcp_lun);
+
zfcp_erp_adapter_reopen(adapter, 0);
zfcp_erp_wait(adapter);
@@ -607,7 +581,7 @@ zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
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->transportt = zfcp_transport_template;
+ adapter->scsi_host->transportt = zfcp_data.scsi_transport_template;
/*
* save a pointer to our own adapter data structure within
@@ -648,16 +622,6 @@ zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
return;
}
-
-void
-zfcp_fsf_start_scsi_er_timer(struct zfcp_adapter *adapter)
-{
- adapter->scsi_er_timer.function = zfcp_fsf_scsi_er_timeout_handler;
- adapter->scsi_er_timer.data = (unsigned long) adapter;
- adapter->scsi_er_timer.expires = jiffies + ZFCP_SCSI_ER_TIMEOUT;
- add_timer(&adapter->scsi_er_timer);
-}
-
/*
* Support functions for FC transport class
*/
diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c
index 66da840c9316..1e788e815ce7 100644
--- a/drivers/s390/sysinfo.c
+++ b/drivers/s390/sysinfo.c
@@ -5,26 +5,24 @@
* Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com)
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <asm/ebcdic.h>
-struct sysinfo_1_1_1
-{
+struct sysinfo_1_1_1 {
char reserved_0[32];
char manufacturer[16];
char type[4];
char reserved_1[12];
- char model[16];
+ char model_capacity[16];
char sequence[16];
char plant[4];
+ char model[16];
};
-struct sysinfo_1_2_1
-{
+struct sysinfo_1_2_1 {
char reserved_0[80];
char sequence[16];
char plant[4];
@@ -32,9 +30,12 @@ struct sysinfo_1_2_1
unsigned short cpu_address;
};
-struct sysinfo_1_2_2
-{
- char reserved_0[32];
+struct sysinfo_1_2_2 {
+ char format;
+ char reserved_0[1];
+ unsigned short acc_offset;
+ char reserved_1[24];
+ unsigned int secondary_capability;
unsigned int capability;
unsigned short cpus_total;
unsigned short cpus_configured;
@@ -43,8 +44,12 @@ struct sysinfo_1_2_2
unsigned short adjustment[0];
};
-struct sysinfo_2_2_1
-{
+struct sysinfo_1_2_2_extension {
+ unsigned int alt_capability;
+ unsigned short alt_adjustment[0];
+};
+
+struct sysinfo_2_2_1 {
char reserved_0[80];
char sequence[16];
char plant[4];
@@ -52,15 +57,11 @@ struct sysinfo_2_2_1
unsigned short cpu_address;
};
-struct sysinfo_2_2_2
-{
+struct sysinfo_2_2_2 {
char reserved_0[32];
unsigned short lpar_number;
char reserved_1;
unsigned char characteristics;
- #define LPAR_CHAR_DEDICATED (1 << 7)
- #define LPAR_CHAR_SHARED (1 << 6)
- #define LPAR_CHAR_LIMITED (1 << 5)
unsigned short cpus_total;
unsigned short cpus_configured;
unsigned short cpus_standby;
@@ -72,12 +73,14 @@ struct sysinfo_2_2_2
unsigned short cpus_shared;
};
-struct sysinfo_3_2_2
-{
+#define LPAR_CHAR_DEDICATED (1 << 7)
+#define LPAR_CHAR_SHARED (1 << 6)
+#define LPAR_CHAR_LIMITED (1 << 5)
+
+struct sysinfo_3_2_2 {
char reserved_0[31];
unsigned char count;
- struct
- {
+ struct {
char reserved_0[4];
unsigned short cpus_total;
unsigned short cpus_configured;
@@ -91,136 +94,223 @@ struct sysinfo_3_2_2
} vm[8];
};
-union s390_sysinfo
+static inline int stsi(void *sysinfo, int fc, int sel1, int sel2)
{
- struct sysinfo_1_1_1 sysinfo_1_1_1;
- struct sysinfo_1_2_1 sysinfo_1_2_1;
- struct sysinfo_1_2_2 sysinfo_1_2_2;
- struct sysinfo_2_2_1 sysinfo_2_2_1;
- struct sysinfo_2_2_2 sysinfo_2_2_2;
- struct sysinfo_3_2_2 sysinfo_3_2_2;
-};
-
-static inline int stsi (void *sysinfo,
- int fc, int sel1, int sel2)
-{
- int cc, retv;
-
-#ifndef CONFIG_64BIT
- __asm__ __volatile__ ( "lr\t0,%2\n"
- "\tlr\t1,%3\n"
- "\tstsi\t0(%4)\n"
- "0:\tipm\t%0\n"
- "\tsrl\t%0,28\n"
- "1:lr\t%1,0\n"
- ".section .fixup,\"ax\"\n"
- "2:\tlhi\t%0,3\n"
- "\tbras\t1,3f\n"
- "\t.long 1b\n"
- "3:\tl\t1,0(1)\n"
- "\tbr\t1\n"
- ".previous\n"
- ".section __ex_table,\"a\"\n"
- "\t.align 4\n"
- "\t.long 0b,2b\n"
- ".previous\n"
- : "=d" (cc), "=d" (retv)
- : "d" ((fc << 28) | sel1), "d" (sel2), "a" (sysinfo)
- : "cc", "memory", "0", "1" );
-#else
- __asm__ __volatile__ ( "lr\t0,%2\n"
- "lr\t1,%3\n"
- "\tstsi\t0(%4)\n"
- "0:\tipm\t%0\n"
- "\tsrl\t%0,28\n"
- "1:lr\t%1,0\n"
- ".section .fixup,\"ax\"\n"
- "2:\tlhi\t%0,3\n"
- "\tjg\t1b\n"
- ".previous\n"
- ".section __ex_table,\"a\"\n"
- "\t.align 8\n"
- "\t.quad 0b,2b\n"
- ".previous\n"
- : "=d" (cc), "=d" (retv)
- : "d" ((fc << 28) | sel1), "d" (sel2), "a" (sysinfo)
- : "cc", "memory", "0", "1" );
-#endif
-
- return cc? -1 : retv;
+ register int r0 asm("0") = (fc << 28) | sel1;
+ register int r1 asm("1") = sel2;
+
+ asm volatile(
+ " stsi 0(%2)\n"
+ "0: jz 2f\n"
+ "1: lhi %0,%3\n"
+ "2:\n"
+ EX_TABLE(0b,1b)
+ : "+d" (r0) : "d" (r1), "a" (sysinfo), "K" (-ENOSYS)
+ : "cc", "memory" );
+ return r0;
}
-static inline int stsi_0 (void)
+static inline int stsi_0(void)
{
int rc = stsi (NULL, 0, 0, 0);
- return rc == -1 ? rc : (((unsigned int)rc) >> 28);
+ return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
}
-static inline int stsi_1_1_1 (struct sysinfo_1_1_1 *info)
+static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len)
{
- int rc = stsi (info, 1, 1, 1);
- if (rc != -1)
- {
- EBCASC (info->manufacturer, sizeof(info->manufacturer));
- EBCASC (info->type, sizeof(info->type));
- EBCASC (info->model, sizeof(info->model));
- EBCASC (info->sequence, sizeof(info->sequence));
- EBCASC (info->plant, sizeof(info->plant));
- }
- return rc == -1 ? rc : 0;
+ if (stsi(info, 1, 1, 1) == -ENOSYS)
+ return len;
+
+ EBCASC(info->manufacturer, sizeof(info->manufacturer));
+ EBCASC(info->type, sizeof(info->type));
+ EBCASC(info->model, sizeof(info->model));
+ EBCASC(info->sequence, sizeof(info->sequence));
+ EBCASC(info->plant, sizeof(info->plant));
+ EBCASC(info->model_capacity, sizeof(info->model_capacity));
+ len += sprintf(page + len, "Manufacturer: %-16.16s\n",
+ info->manufacturer);
+ len += sprintf(page + len, "Type: %-4.4s\n",
+ info->type);
+ if (info->model[0] != '\0')
+ /*
+ * Sigh: the model field has been renamed with System z9
+ * to model_capacity and a new model field has been added
+ * after the plant field. To avoid confusing older programs
+ * the "Model:" prints "model_capacity model" or just
+ * "model_capacity" if the model string is empty .
+ */
+ len += sprintf(page + len,
+ "Model: %-16.16s %-16.16s\n",
+ info->model_capacity, info->model);
+ else
+ len += sprintf(page + len, "Model: %-16.16s\n",
+ info->model_capacity);
+ len += sprintf(page + len, "Sequence Code: %-16.16s\n",
+ info->sequence);
+ len += sprintf(page + len, "Plant: %-4.4s\n",
+ info->plant);
+ len += sprintf(page + len, "Model Capacity: %-16.16s\n",
+ info->model_capacity);
+ return len;
}
-static inline int stsi_1_2_1 (struct sysinfo_1_2_1 *info)
+#if 0 /* Currently unused */
+static int stsi_1_2_1(struct sysinfo_1_2_1 *info, char *page, int len)
{
- int rc = stsi (info, 1, 2, 1);
- if (rc != -1)
- {
- EBCASC (info->sequence, sizeof(info->sequence));
- EBCASC (info->plant, sizeof(info->plant));
- }
- return rc == -1 ? rc : 0;
+ if (stsi(info, 1, 2, 1) == -ENOSYS)
+ return len;
+
+ len += sprintf(page + len, "\n");
+ EBCASC(info->sequence, sizeof(info->sequence));
+ EBCASC(info->plant, sizeof(info->plant));
+ len += sprintf(page + len, "Sequence Code of CPU: %-16.16s\n",
+ info->sequence);
+ len += sprintf(page + len, "Plant of CPU: %-16.16s\n",
+ info->plant);
+ return len;
}
+#endif
-static inline int stsi_1_2_2 (struct sysinfo_1_2_2 *info)
+static int stsi_1_2_2(struct sysinfo_1_2_2 *info, char *page, int len)
{
- int rc = stsi (info, 1, 2, 2);
- return rc == -1 ? rc : 0;
+ struct sysinfo_1_2_2_extension *ext;
+ int i;
+
+ if (stsi(info, 1, 2, 2) == -ENOSYS)
+ return len;
+ ext = (struct sysinfo_1_2_2_extension *)
+ ((unsigned long) info + info->acc_offset);
+
+ len += sprintf(page + len, "\n");
+ len += sprintf(page + len, "CPUs Total: %d\n",
+ info->cpus_total);
+ len += sprintf(page + len, "CPUs Configured: %d\n",
+ info->cpus_configured);
+ len += sprintf(page + len, "CPUs Standby: %d\n",
+ info->cpus_standby);
+ len += sprintf(page + len, "CPUs Reserved: %d\n",
+ info->cpus_reserved);
+
+ if (info->format == 1) {
+ /*
+ * Sigh 2. According to the specification the alternate
+ * capability field is a 32 bit floating point number
+ * if the higher order 8 bits are not zero. Printing
+ * a floating point number in the kernel is a no-no,
+ * always print the number as 32 bit unsigned integer.
+ * The user-space needs to know about the stange
+ * encoding of the alternate cpu capability.
+ */
+ len += sprintf(page + len, "Capability: %u %u\n",
+ info->capability, ext->alt_capability);
+ for (i = 2; i <= info->cpus_total; i++)
+ len += sprintf(page + len,
+ "Adjustment %02d-way: %u %u\n",
+ i, info->adjustment[i-2],
+ ext->alt_adjustment[i-2]);
+
+ } else {
+ len += sprintf(page + len, "Capability: %u\n",
+ info->capability);
+ for (i = 2; i <= info->cpus_total; i++)
+ len += sprintf(page + len,
+ "Adjustment %02d-way: %u\n",
+ i, info->adjustment[i-2]);
+ }
+
+ if (info->secondary_capability != 0)
+ len += sprintf(page + len, "Secondary Capability: %d\n",
+ info->secondary_capability);
+
+ return len;
}
-static inline int stsi_2_2_1 (struct sysinfo_2_2_1 *info)
+#if 0 /* Currently unused */
+static int stsi_2_2_1(struct sysinfo_2_2_1 *info, char *page, int len)
{
- int rc = stsi (info, 2, 2, 1);
- if (rc != -1)
- {
- EBCASC (info->sequence, sizeof(info->sequence));
- EBCASC (info->plant, sizeof(info->plant));
- }
- return rc == -1 ? rc : 0;
+ if (stsi(info, 2, 2, 1) == -ENOSYS)
+ return len;
+
+ len += sprintf(page + len, "\n");
+ EBCASC (info->sequence, sizeof(info->sequence));
+ EBCASC (info->plant, sizeof(info->plant));
+ len += sprintf(page + len, "Sequence Code of logical CPU: %-16.16s\n",
+ info->sequence);
+ len += sprintf(page + len, "Plant of logical CPU: %-16.16s\n",
+ info->plant);
+ return len;
}
+#endif
-static inline int stsi_2_2_2 (struct sysinfo_2_2_2 *info)
+static int stsi_2_2_2(struct sysinfo_2_2_2 *info, char *page, int len)
{
- int rc = stsi (info, 2, 2, 2);
- if (rc != -1)
- {
- EBCASC (info->name, sizeof(info->name));
- }
- return rc == -1 ? rc : 0;
+ if (stsi(info, 2, 2, 2) == -ENOSYS)
+ return len;
+
+ EBCASC (info->name, sizeof(info->name));
+
+ len += sprintf(page + len, "\n");
+ len += sprintf(page + len, "LPAR Number: %d\n",
+ info->lpar_number);
+
+ len += sprintf(page + len, "LPAR Characteristics: ");
+ if (info->characteristics & LPAR_CHAR_DEDICATED)
+ len += sprintf(page + len, "Dedicated ");
+ if (info->characteristics & LPAR_CHAR_SHARED)
+ len += sprintf(page + len, "Shared ");
+ if (info->characteristics & LPAR_CHAR_LIMITED)
+ len += sprintf(page + len, "Limited ");
+ len += sprintf(page + len, "\n");
+
+ len += sprintf(page + len, "LPAR Name: %-8.8s\n",
+ info->name);
+
+ len += sprintf(page + len, "LPAR Adjustment: %d\n",
+ info->caf);
+
+ len += sprintf(page + len, "LPAR CPUs Total: %d\n",
+ info->cpus_total);
+ len += sprintf(page + len, "LPAR CPUs Configured: %d\n",
+ info->cpus_configured);
+ len += sprintf(page + len, "LPAR CPUs Standby: %d\n",
+ info->cpus_standby);
+ len += sprintf(page + len, "LPAR CPUs Reserved: %d\n",
+ info->cpus_reserved);
+ len += sprintf(page + len, "LPAR CPUs Dedicated: %d\n",
+ info->cpus_dedicated);
+ len += sprintf(page + len, "LPAR CPUs Shared: %d\n",
+ info->cpus_shared);
+ return len;
}
-static inline int stsi_3_2_2 (struct sysinfo_3_2_2 *info)
+static int stsi_3_2_2(struct sysinfo_3_2_2 *info, char *page, int len)
{
- int rc = stsi (info, 3, 2, 2);
- if (rc != -1)
- {
- int i;
- for (i = 0; i < info->count; i++)
- {
- EBCASC (info->vm[i].name, sizeof(info->vm[i].name));
- EBCASC (info->vm[i].cpi, sizeof(info->vm[i].cpi));
- }
+ int i;
+
+ if (stsi(info, 3, 2, 2) == -ENOSYS)
+ return len;
+ for (i = 0; i < info->count; i++) {
+ EBCASC (info->vm[i].name, sizeof(info->vm[i].name));
+ EBCASC (info->vm[i].cpi, sizeof(info->vm[i].cpi));
+ len += sprintf(page + len, "\n");
+ len += sprintf(page + len, "VM%02d Name: %-8.8s\n",
+ i, info->vm[i].name);
+ len += sprintf(page + len, "VM%02d Control Program: %-16.16s\n",
+ i, info->vm[i].cpi);
+
+ len += sprintf(page + len, "VM%02d Adjustment: %d\n",
+ i, info->vm[i].caf);
+
+ len += sprintf(page + len, "VM%02d CPUs Total: %d\n",
+ i, info->vm[i].cpus_total);
+ len += sprintf(page + len, "VM%02d CPUs Configured: %d\n",
+ i, info->vm[i].cpus_configured);
+ len += sprintf(page + len, "VM%02d CPUs Standby: %d\n",
+ i, info->vm[i].cpus_standby);
+ len += sprintf(page + len, "VM%02d CPUs Reserved: %d\n",
+ i, info->vm[i].cpus_reserved);
}
- return rc == -1 ? rc : 0;
+ return len;
}
@@ -228,118 +318,34 @@ static int proc_read_sysinfo(char *page, char **start,
off_t off, int count,
int *eof, void *data)
{
- unsigned long info_page = get_zeroed_page (GFP_KERNEL);
- union s390_sysinfo *info = (union s390_sysinfo *) info_page;
- int len = 0;
- int level;
- int i;
+ unsigned long info = get_zeroed_page (GFP_KERNEL);
+ int level, len;
if (!info)
return 0;
- level = stsi_0 ();
-
- if (level >= 1 && stsi_1_1_1 (&info->sysinfo_1_1_1) == 0)
- {
- len += sprintf (page+len, "Manufacturer: %-16.16s\n",
- info->sysinfo_1_1_1.manufacturer);
- len += sprintf (page+len, "Type: %-4.4s\n",
- info->sysinfo_1_1_1.type);
- len += sprintf (page+len, "Model: %-16.16s\n",
- info->sysinfo_1_1_1.model);
- len += sprintf (page+len, "Sequence Code: %-16.16s\n",
- info->sysinfo_1_1_1.sequence);
- len += sprintf (page+len, "Plant: %-4.4s\n",
- info->sysinfo_1_1_1.plant);
- }
-
- if (level >= 1 && stsi_1_2_2 (&info->sysinfo_1_2_2) == 0)
- {
- len += sprintf (page+len, "\n");
- len += sprintf (page+len, "CPUs Total: %d\n",
- info->sysinfo_1_2_2.cpus_total);
- len += sprintf (page+len, "CPUs Configured: %d\n",
- info->sysinfo_1_2_2.cpus_configured);
- len += sprintf (page+len, "CPUs Standby: %d\n",
- info->sysinfo_1_2_2.cpus_standby);
- len += sprintf (page+len, "CPUs Reserved: %d\n",
- info->sysinfo_1_2_2.cpus_reserved);
-
- len += sprintf (page+len, "Capability: %d\n",
- info->sysinfo_1_2_2.capability);
+ len = 0;
+ level = stsi_0();
+ if (level >= 1)
+ len = stsi_1_1_1((struct sysinfo_1_1_1 *) info, page, len);
- for (i = 2; i <= info->sysinfo_1_2_2.cpus_total; i++)
- len += sprintf (page+len, "Adjustment %02d-way: %d\n",
- i, info->sysinfo_1_2_2.adjustment[i-2]);
- }
+ if (level >= 1)
+ len = stsi_1_2_2((struct sysinfo_1_2_2 *) info, page, len);
- if (level >= 2 && stsi_2_2_2 (&info->sysinfo_2_2_2) == 0)
- {
- len += sprintf (page+len, "\n");
- len += sprintf (page+len, "LPAR Number: %d\n",
- info->sysinfo_2_2_2.lpar_number);
-
- len += sprintf (page+len, "LPAR Characteristics: ");
- if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_DEDICATED)
- len += sprintf (page+len, "Dedicated ");
- if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_SHARED)
- len += sprintf (page+len, "Shared ");
- if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_LIMITED)
- len += sprintf (page+len, "Limited ");
- len += sprintf (page+len, "\n");
-
- len += sprintf (page+len, "LPAR Name: %-8.8s\n",
- info->sysinfo_2_2_2.name);
-
- len += sprintf (page+len, "LPAR Adjustment: %d\n",
- info->sysinfo_2_2_2.caf);
-
- len += sprintf (page+len, "LPAR CPUs Total: %d\n",
- info->sysinfo_2_2_2.cpus_total);
- len += sprintf (page+len, "LPAR CPUs Configured: %d\n",
- info->sysinfo_2_2_2.cpus_configured);
- len += sprintf (page+len, "LPAR CPUs Standby: %d\n",
- info->sysinfo_2_2_2.cpus_standby);
- len += sprintf (page+len, "LPAR CPUs Reserved: %d\n",
- info->sysinfo_2_2_2.cpus_reserved);
- len += sprintf (page+len, "LPAR CPUs Dedicated: %d\n",
- info->sysinfo_2_2_2.cpus_dedicated);
- len += sprintf (page+len, "LPAR CPUs Shared: %d\n",
- info->sysinfo_2_2_2.cpus_shared);
- }
+ if (level >= 2)
+ len = stsi_2_2_2((struct sysinfo_2_2_2 *) info, page, len);
- if (level >= 3 && stsi_3_2_2 (&info->sysinfo_3_2_2) == 0)
- {
- for (i = 0; i < info->sysinfo_3_2_2.count; i++)
- {
- len += sprintf (page+len, "\n");
- len += sprintf (page+len, "VM%02d Name: %-8.8s\n",
- i, info->sysinfo_3_2_2.vm[i].name);
- len += sprintf (page+len, "VM%02d Control Program: %-16.16s\n",
- i, info->sysinfo_3_2_2.vm[i].cpi);
-
- len += sprintf (page+len, "VM%02d Adjustment: %d\n",
- i, info->sysinfo_3_2_2.vm[i].caf);
-
- len += sprintf (page+len, "VM%02d CPUs Total: %d\n",
- i, info->sysinfo_3_2_2.vm[i].cpus_total);
- len += sprintf (page+len, "VM%02d CPUs Configured: %d\n",
- i, info->sysinfo_3_2_2.vm[i].cpus_configured);
- len += sprintf (page+len, "VM%02d CPUs Standby: %d\n",
- i, info->sysinfo_3_2_2.vm[i].cpus_standby);
- len += sprintf (page+len, "VM%02d CPUs Reserved: %d\n",
- i, info->sysinfo_3_2_2.vm[i].cpus_reserved);
- }
- }
+ if (level >= 3)
+ len = stsi_3_2_2((struct sysinfo_3_2_2 *) info, page, len);
- free_page (info_page);
+ free_page (info);
return len;
}
static __init int create_proc_sysinfo(void)
{
- create_proc_read_entry ("sysinfo", 0444, NULL,
- proc_read_sysinfo, NULL);
+ create_proc_read_entry("sysinfo", 0444, NULL,
+ proc_read_sysinfo, NULL);
return 0;
}