summaryrefslogtreecommitdiff
path: root/drivers/s390
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2017-01-24 12:37:43 -0800
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2017-01-24 12:37:43 -0800
commit0cce284537fb42d9c28b9b31038ffc9b464555f5 (patch)
treee7af3b5fe2d04ef6527b5da50851d498ee13184e /drivers/s390
parentcefb26d4b37c329f624eb235a1bdc7b6b8dd9430 (diff)
parent7a308bb3016f57e5be11a677d15b821536419d36 (diff)
downloadlinux-next-0cce284537fb42d9c28b9b31038ffc9b464555f5.tar.gz
Merge tag 'v4.10-rc5' into next
Sync up with mainline to bring up improvements in various subsystems.
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/block/dasd.c340
-rw-r--r--drivers/s390/block/dasd_3990_erp.c58
-rw-r--r--drivers/s390/block/dasd_devmap.c329
-rw-r--r--drivers/s390/block/dasd_eckd.c329
-rw-r--r--drivers/s390/block/dasd_eckd.h5
-rw-r--r--drivers/s390/block/dasd_eer.c31
-rw-r--r--drivers/s390/block/dasd_erp.c8
-rw-r--r--drivers/s390/block/dasd_fba.c2
-rw-r--r--drivers/s390/block/dasd_genhd.c2
-rw-r--r--drivers/s390/block/dasd_int.h450
-rw-r--r--drivers/s390/block/dasd_ioctl.c2
-rw-r--r--drivers/s390/block/dasd_proc.c2
-rw-r--r--drivers/s390/block/xpram.c2
-rw-r--r--drivers/s390/char/con3215.c14
-rw-r--r--drivers/s390/char/con3270.c11
-rw-r--r--drivers/s390/char/keyboard.c2
-rw-r--r--drivers/s390/char/monreader.c2
-rw-r--r--drivers/s390/char/monwriter.c2
-rw-r--r--drivers/s390/char/sclp.h23
-rw-r--r--drivers/s390/char/sclp_cmd.c25
-rw-r--r--drivers/s390/char/sclp_ctl.c21
-rw-r--r--drivers/s390/char/sclp_early.c31
-rw-r--r--drivers/s390/char/sclp_quiesce.c4
-rw-r--r--drivers/s390/char/sclp_rw.c2
-rw-r--r--drivers/s390/char/sclp_tty.c5
-rw-r--r--drivers/s390/char/sclp_vt220.c2
-rw-r--r--drivers/s390/char/tape_3590.c11
-rw-r--r--drivers/s390/char/tape_char.c2
-rw-r--r--drivers/s390/char/tty3270.c2
-rw-r--r--drivers/s390/char/vmcp.c2
-rw-r--r--drivers/s390/char/vmlogrdr.c4
-rw-r--r--drivers/s390/char/vmur.c11
-rw-r--r--drivers/s390/char/zcore.c24
-rw-r--r--drivers/s390/cio/blacklist.c2
-rw-r--r--drivers/s390/cio/chp.c6
-rw-r--r--drivers/s390/cio/chsc.c20
-rw-r--r--drivers/s390/cio/cio.h1
-rw-r--r--drivers/s390/cio/cmf.c10
-rw-r--r--drivers/s390/cio/css.c6
-rw-r--r--drivers/s390/cio/device.c6
-rw-r--r--drivers/s390/cio/device.h1
-rw-r--r--drivers/s390/cio/device_fsm.c6
-rw-r--r--drivers/s390/cio/device_ops.c5
-rw-r--r--drivers/s390/crypto/Makefile13
-rw-r--r--drivers/s390/crypto/ap_asm.h191
-rw-r--r--drivers/s390/crypto/ap_bus.c1335
-rw-r--r--drivers/s390/crypto/ap_bus.h98
-rw-r--r--drivers/s390/crypto/ap_card.c170
-rw-r--r--drivers/s390/crypto/ap_debug.h28
-rw-r--r--drivers/s390/crypto/ap_queue.c701
-rw-r--r--drivers/s390/crypto/zcrypt_api.c1139
-rw-r--r--drivers/s390/crypto/zcrypt_api.h99
-rw-r--r--drivers/s390/crypto/zcrypt_card.c187
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c216
-rw-r--r--drivers/s390/crypto/zcrypt_cex4.c319
-rw-r--r--drivers/s390/crypto/zcrypt_debug.h50
-rw-r--r--drivers/s390/crypto/zcrypt_error.h105
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.c135
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.h5
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.c660
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.h23
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c378
-rw-r--r--drivers/s390/crypto/zcrypt_queue.c226
-rw-r--r--drivers/s390/net/ctcm_main.c5
-rw-r--r--drivers/s390/net/lcs.c2
-rw-r--r--drivers/s390/net/netiucv.c43
-rw-r--r--drivers/s390/net/qeth_core_main.c4
-rw-r--r--drivers/s390/net/qeth_l2_main.c2
-rw-r--r--drivers/s390/net/qeth_l3_main.c2
-rw-r--r--drivers/s390/scsi/zfcp_aux.c2
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c177
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h53
-rw-r--r--drivers/s390/scsi/zfcp_erp.c69
-rw-r--r--drivers/s390/scsi/zfcp_ext.h14
-rw-r--r--drivers/s390/scsi/zfcp_fc.c36
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c22
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h5
-rw-r--r--drivers/s390/scsi/zfcp_reqlist.h30
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c67
-rw-r--r--drivers/s390/virtio/virtio_ccw.c54
80 files changed, 5466 insertions, 3022 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index fb1b56a71475..0e3fdfdbd098 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -69,6 +69,7 @@ static void dasd_block_tasklet(struct dasd_block *);
static void do_kick_device(struct work_struct *);
static void do_restore_device(struct work_struct *);
static void do_reload_device(struct work_struct *);
+static void do_requeue_requests(struct work_struct *);
static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
static void dasd_device_timeout(unsigned long);
static void dasd_block_timeout(unsigned long);
@@ -125,6 +126,7 @@ struct dasd_device *dasd_alloc_device(void)
INIT_WORK(&device->kick_work, do_kick_device);
INIT_WORK(&device->restore_device, do_restore_device);
INIT_WORK(&device->reload_device, do_reload_device);
+ INIT_WORK(&device->requeue_requests, do_requeue_requests);
device->state = DASD_STATE_NEW;
device->target = DASD_STATE_NEW;
mutex_init(&device->state_mutex);
@@ -212,16 +214,6 @@ static int dasd_state_known_to_new(struct dasd_device *device)
{
/* Disable extended error reporting for this device. */
dasd_eer_disable(device);
- /* Forget the discipline information. */
- if (device->discipline) {
- if (device->discipline->uncheck_device)
- device->discipline->uncheck_device(device);
- module_put(device->discipline->owner);
- }
- device->discipline = NULL;
- if (device->base_discipline)
- module_put(device->base_discipline->owner);
- device->base_discipline = NULL;
device->state = DASD_STATE_NEW;
if (device->block)
@@ -336,6 +328,7 @@ static int dasd_state_basic_to_ready(struct dasd_device *device)
{
int rc;
struct dasd_block *block;
+ struct gendisk *disk;
rc = 0;
block = device->block;
@@ -346,6 +339,9 @@ static int dasd_state_basic_to_ready(struct dasd_device *device)
if (rc) {
if (rc != -EAGAIN) {
device->state = DASD_STATE_UNFMT;
+ disk = device->block->gdp;
+ kobject_uevent(&disk_to_dev(disk)->kobj,
+ KOBJ_CHANGE);
goto out;
}
return rc;
@@ -1454,9 +1450,9 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
cqr->starttime = jiffies;
cqr->retries--;
if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) {
- cqr->lpm &= device->path_data.opm;
+ cqr->lpm &= dasd_path_get_opm(device);
if (!cqr->lpm)
- cqr->lpm = device->path_data.opm;
+ cqr->lpm = dasd_path_get_opm(device);
}
if (cqr->cpmode == 1) {
rc = ccw_device_tm_start(device->cdev, cqr->cpaddr,
@@ -1489,8 +1485,8 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
DBF_DEV_EVENT(DBF_WARNING, device,
"start_IO: selected paths gone (%x)",
cqr->lpm);
- } else if (cqr->lpm != device->path_data.opm) {
- cqr->lpm = device->path_data.opm;
+ } else if (cqr->lpm != dasd_path_get_opm(device)) {
+ cqr->lpm = dasd_path_get_opm(device);
DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
"start_IO: selected paths gone,"
" retry on all paths");
@@ -1499,11 +1495,10 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
"start_IO: all paths in opm gone,"
" do path verification");
dasd_generic_last_path_gone(device);
- device->path_data.opm = 0;
- device->path_data.ppm = 0;
- device->path_data.npm = 0;
- device->path_data.tbvpm =
- ccw_device_get_path_mask(device->cdev);
+ dasd_path_no_path(device);
+ dasd_path_set_tbvpm(device,
+ ccw_device_get_path_mask(
+ device->cdev));
}
break;
case -ENODEV:
@@ -1629,6 +1624,13 @@ void dasd_generic_handle_state_change(struct dasd_device *device)
}
EXPORT_SYMBOL_GPL(dasd_generic_handle_state_change);
+static int dasd_check_hpf_error(struct irb *irb)
+{
+ return (scsw_tm_is_valid_schxs(&irb->scsw) &&
+ (irb->scsw.tm.sesq == SCSW_SESQ_DEV_NOFCX ||
+ irb->scsw.tm.sesq == SCSW_SESQ_PATH_NOFCX));
+}
+
/*
* Interrupt handler for "normal" ssch-io based dasd devices.
*/
@@ -1648,7 +1650,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
switch (PTR_ERR(irb)) {
case -EIO:
if (cqr && cqr->status == DASD_CQR_CLEAR_PENDING) {
- device = (struct dasd_device *) cqr->startdev;
+ device = cqr->startdev;
cqr->status = DASD_CQR_CLEARED;
dasd_device_clear_timer(device);
wake_up(&dasd_flush_wq);
@@ -1755,19 +1757,26 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
struct dasd_ccw_req, devlist);
}
} else { /* error */
+ /* check for HPF error
+ * call discipline function to requeue all requests
+ * and disable HPF accordingly
+ */
+ if (cqr->cpmode && dasd_check_hpf_error(irb) &&
+ device->discipline->handle_hpf_error)
+ device->discipline->handle_hpf_error(device, irb);
/*
* If we don't want complex ERP for this request, then just
* reset this and retry it in the fastpath
*/
if (!test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags) &&
cqr->retries > 0) {
- if (cqr->lpm == device->path_data.opm)
+ if (cqr->lpm == dasd_path_get_opm(device))
DBF_DEV_EVENT(DBF_DEBUG, device,
"default ERP in fastpath "
"(%i retries left)",
cqr->retries);
if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))
- cqr->lpm = device->path_data.opm;
+ cqr->lpm = dasd_path_get_opm(device);
cqr->status = DASD_CQR_QUEUED;
next = cqr;
} else
@@ -2008,17 +2017,18 @@ static void __dasd_device_check_path_events(struct dasd_device *device)
{
int rc;
- if (device->path_data.tbvpm) {
- if (device->stopped & ~(DASD_STOPPED_DC_WAIT |
- DASD_UNRESUMED_PM))
- return;
- rc = device->discipline->verify_path(
- device, device->path_data.tbvpm);
- if (rc)
- dasd_device_set_timer(device, 50);
- else
- device->path_data.tbvpm = 0;
- }
+ if (!dasd_path_get_tbvpm(device))
+ return;
+
+ if (device->stopped &
+ ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM))
+ return;
+ rc = device->discipline->verify_path(device,
+ dasd_path_get_tbvpm(device));
+ if (rc)
+ dasd_device_set_timer(device, 50);
+ else
+ dasd_path_clear_all_verify(device);
};
/*
@@ -2274,6 +2284,15 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
continue;
}
/*
+ * Don't try to start requests if device is in
+ * offline processing, it might wait forever
+ */
+ if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
+ cqr->status = DASD_CQR_FAILED;
+ cqr->intrc = -ENODEV;
+ continue;
+ }
+ /*
* Don't try to start requests if device is stopped
* except path verification requests
*/
@@ -2921,10 +2940,10 @@ static int _dasd_requeue_request(struct dasd_ccw_req *cqr)
if (!block)
return -EINVAL;
- spin_lock_irqsave(&block->queue_lock, flags);
+ spin_lock_irqsave(&block->request_queue_lock, flags);
req = (struct request *) cqr->callback_data;
blk_requeue_request(block->request_queue, req);
- spin_unlock_irqrestore(&block->queue_lock, flags);
+ spin_unlock_irqrestore(&block->request_queue_lock, flags);
return 0;
}
@@ -3118,6 +3137,7 @@ static int dasd_alloc_queue(struct dasd_block *block)
*/
static void dasd_setup_queue(struct dasd_block *block)
{
+ struct request_queue *q = block->request_queue;
int max;
if (block->base->features & DASD_FEATURE_USERAW) {
@@ -3132,17 +3152,16 @@ static void dasd_setup_queue(struct dasd_block *block)
} else {
max = block->base->discipline->max_blocks << block->s2b_shift;
}
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, block->request_queue);
- block->request_queue->limits.max_dev_sectors = max;
- blk_queue_logical_block_size(block->request_queue,
- block->bp_block);
- blk_queue_max_hw_sectors(block->request_queue, max);
- blk_queue_max_segments(block->request_queue, -1L);
+ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
+ q->limits.max_dev_sectors = max;
+ blk_queue_logical_block_size(q, block->bp_block);
+ blk_queue_max_hw_sectors(q, max);
+ blk_queue_max_segments(q, USHRT_MAX);
/* with page sized segments we can translate each segement into
* one idaw/tidaw
*/
- blk_queue_max_segment_size(block->request_queue, PAGE_SIZE);
- blk_queue_segment_boundary(block->request_queue, PAGE_SIZE - 1);
+ blk_queue_max_segment_size(q, PAGE_SIZE);
+ blk_queue_segment_boundary(q, PAGE_SIZE - 1);
}
/*
@@ -3364,6 +3383,22 @@ int dasd_generic_probe(struct ccw_device *cdev,
}
EXPORT_SYMBOL_GPL(dasd_generic_probe);
+void dasd_generic_free_discipline(struct dasd_device *device)
+{
+ /* Forget the discipline information. */
+ if (device->discipline) {
+ if (device->discipline->uncheck_device)
+ device->discipline->uncheck_device(device);
+ module_put(device->discipline->owner);
+ device->discipline = NULL;
+ }
+ if (device->base_discipline) {
+ module_put(device->base_discipline->owner);
+ device->base_discipline = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(dasd_generic_free_discipline);
+
/*
* This will one day be called from a global not_oper handler.
* It is also used by driver_unregister during module unload.
@@ -3498,11 +3533,15 @@ int dasd_generic_set_offline(struct ccw_device *cdev)
struct dasd_device *device;
struct dasd_block *block;
int max_count, open_count, rc;
+ unsigned long flags;
rc = 0;
- device = dasd_device_from_cdev(cdev);
- if (IS_ERR(device))
+ spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+ device = dasd_device_from_cdev_locked(cdev);
+ if (IS_ERR(device)) {
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
return PTR_ERR(device);
+ }
/*
* We must make sure that this device is currently not in use.
@@ -3521,8 +3560,7 @@ int dasd_generic_set_offline(struct ccw_device *cdev)
pr_warn("%s: The DASD cannot be set offline while it is in use\n",
dev_name(&cdev->dev));
clear_bit(DASD_FLAG_OFFLINE, &device->flags);
- dasd_put_device(device);
- return -EBUSY;
+ goto out_busy;
}
}
@@ -3532,19 +3570,19 @@ int dasd_generic_set_offline(struct ccw_device *cdev)
* could only be called by normal offline so safe_offline flag
* needs to be removed to run normal offline and kill all I/O
*/
- if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) {
+ if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags))
/* Already doing normal offline processing */
- dasd_put_device(device);
- return -EBUSY;
- } else
+ goto out_busy;
+ else
clear_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags);
-
- } else
- if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
+ } else {
+ if (test_bit(DASD_FLAG_OFFLINE, &device->flags))
/* Already doing offline processing */
- dasd_put_device(device);
- return -EBUSY;
- }
+ goto out_busy;
+ }
+
+ set_bit(DASD_FLAG_OFFLINE, &device->flags);
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
/*
* if safe_offline called set safe_offline_running flag and
@@ -3572,7 +3610,6 @@ int dasd_generic_set_offline(struct ccw_device *cdev)
goto interrupted;
}
- set_bit(DASD_FLAG_OFFLINE, &device->flags);
dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */
block = device->block;
@@ -3591,7 +3628,14 @@ interrupted:
clear_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags);
clear_bit(DASD_FLAG_OFFLINE, &device->flags);
dasd_put_device(device);
+
return rc;
+
+out_busy:
+ dasd_put_device(device);
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+
+ return -EBUSY;
}
EXPORT_SYMBOL_GPL(dasd_generic_set_offline);
@@ -3656,14 +3700,12 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
case CIO_GONE:
case CIO_BOXED:
case CIO_NO_PATH:
- device->path_data.opm = 0;
- device->path_data.ppm = 0;
- device->path_data.npm = 0;
+ dasd_path_no_path(device);
ret = dasd_generic_last_path_gone(device);
break;
case CIO_OPER:
ret = 1;
- if (device->path_data.opm)
+ if (dasd_path_get_opm(device))
ret = dasd_generic_path_operational(device);
break;
}
@@ -3674,48 +3716,32 @@ EXPORT_SYMBOL_GPL(dasd_generic_notify);
void dasd_generic_path_event(struct ccw_device *cdev, int *path_event)
{
- int chp;
- __u8 oldopm, eventlpm;
struct dasd_device *device;
+ int chp, oldopm, hpfpm, ifccpm;
device = dasd_device_from_cdev_locked(cdev);
if (IS_ERR(device))
return;
+
+ oldopm = dasd_path_get_opm(device);
for (chp = 0; chp < 8; chp++) {
- eventlpm = 0x80 >> chp;
if (path_event[chp] & PE_PATH_GONE) {
- oldopm = device->path_data.opm;
- device->path_data.opm &= ~eventlpm;
- device->path_data.ppm &= ~eventlpm;
- device->path_data.npm &= ~eventlpm;
- if (oldopm && !device->path_data.opm) {
- dev_warn(&device->cdev->dev,
- "No verified channel paths remain "
- "for the device\n");
- DBF_DEV_EVENT(DBF_WARNING, device,
- "%s", "last verified path gone");
- dasd_eer_write(device, NULL, DASD_EER_NOPATH);
- dasd_device_set_stop_bits(device,
- DASD_STOPPED_DC_WAIT);
- }
+ dasd_path_notoper(device, chp);
}
if (path_event[chp] & PE_PATH_AVAILABLE) {
- device->path_data.opm &= ~eventlpm;
- device->path_data.ppm &= ~eventlpm;
- device->path_data.npm &= ~eventlpm;
- device->path_data.tbvpm |= eventlpm;
+ dasd_path_available(device, chp);
dasd_schedule_device_bh(device);
}
if (path_event[chp] & PE_PATHGROUP_ESTABLISHED) {
- if (!(device->path_data.opm & eventlpm) &&
- !(device->path_data.tbvpm & eventlpm)) {
+ if (!dasd_path_is_operational(device, chp) &&
+ !dasd_path_need_verify(device, chp)) {
/*
* we can not establish a pathgroup on an
* unavailable path, so trigger a path
* verification first
*/
- device->path_data.tbvpm |= eventlpm;
- dasd_schedule_device_bh(device);
+ dasd_path_available(device, chp);
+ dasd_schedule_device_bh(device);
}
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"Pathgroup re-established\n");
@@ -3723,45 +3749,65 @@ void dasd_generic_path_event(struct ccw_device *cdev, int *path_event)
device->discipline->kick_validate(device);
}
}
+ hpfpm = dasd_path_get_hpfpm(device);
+ ifccpm = dasd_path_get_ifccpm(device);
+ if (!dasd_path_get_opm(device) && hpfpm) {
+ /*
+ * device has no operational paths but at least one path is
+ * disabled due to HPF errors
+ * disable HPF at all and use the path(s) again
+ */
+ if (device->discipline->disable_hpf)
+ device->discipline->disable_hpf(device);
+ dasd_device_set_stop_bits(device, DASD_STOPPED_NOT_ACC);
+ dasd_path_set_tbvpm(device, hpfpm);
+ dasd_schedule_device_bh(device);
+ dasd_schedule_requeue(device);
+ } else if (!dasd_path_get_opm(device) && ifccpm) {
+ /*
+ * device has no operational paths but at least one path is
+ * disabled due to IFCC errors
+ * trigger path verification on paths with IFCC errors
+ */
+ dasd_path_set_tbvpm(device, ifccpm);
+ dasd_schedule_device_bh(device);
+ }
+ if (oldopm && !dasd_path_get_opm(device) && !hpfpm && !ifccpm) {
+ dev_warn(&device->cdev->dev,
+ "No verified channel paths remain for the device\n");
+ DBF_DEV_EVENT(DBF_WARNING, device,
+ "%s", "last verified path gone");
+ dasd_eer_write(device, NULL, DASD_EER_NOPATH);
+ dasd_device_set_stop_bits(device,
+ DASD_STOPPED_DC_WAIT);
+ }
dasd_put_device(device);
}
EXPORT_SYMBOL_GPL(dasd_generic_path_event);
int dasd_generic_verify_path(struct dasd_device *device, __u8 lpm)
{
- if (!device->path_data.opm && lpm) {
- device->path_data.opm = lpm;
+ if (!dasd_path_get_opm(device) && lpm) {
+ dasd_path_set_opm(device, lpm);
dasd_generic_path_operational(device);
} else
- device->path_data.opm |= lpm;
+ dasd_path_add_opm(device, lpm);
return 0;
}
EXPORT_SYMBOL_GPL(dasd_generic_verify_path);
-
-int dasd_generic_pm_freeze(struct ccw_device *cdev)
+/*
+ * clear active requests and requeue them to block layer if possible
+ */
+static int dasd_generic_requeue_all_requests(struct dasd_device *device)
{
- struct dasd_device *device = dasd_device_from_cdev(cdev);
- struct list_head freeze_queue;
+ struct list_head requeue_queue;
struct dasd_ccw_req *cqr, *n;
struct dasd_ccw_req *refers;
int rc;
- if (IS_ERR(device))
- return PTR_ERR(device);
-
- /* mark device as suspended */
- set_bit(DASD_FLAG_SUSPENDED, &device->flags);
-
- if (device->discipline->freeze)
- rc = device->discipline->freeze(device);
-
- /* disallow new I/O */
- dasd_device_set_stop_bits(device, DASD_STOPPED_PM);
-
- /* clear active requests and requeue them to block layer if possible */
- INIT_LIST_HEAD(&freeze_queue);
- spin_lock_irq(get_ccwdev_lock(cdev));
+ INIT_LIST_HEAD(&requeue_queue);
+ spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = 0;
list_for_each_entry_safe(cqr, n, &device->ccw_queue, devlist) {
/* Check status and move request to flush_queue */
@@ -3772,25 +3818,22 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev)
dev_err(&device->cdev->dev,
"Unable to terminate request %p "
"on suspend\n", cqr);
- spin_unlock_irq(get_ccwdev_lock(cdev));
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
dasd_put_device(device);
return rc;
}
}
- list_move_tail(&cqr->devlist, &freeze_queue);
+ list_move_tail(&cqr->devlist, &requeue_queue);
}
- spin_unlock_irq(get_ccwdev_lock(cdev));
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
- list_for_each_entry_safe(cqr, n, &freeze_queue, devlist) {
+ list_for_each_entry_safe(cqr, n, &requeue_queue, devlist) {
wait_event(dasd_flush_wq,
(cqr->status != DASD_CQR_CLEAR_PENDING));
- if (cqr->status == DASD_CQR_CLEARED)
- cqr->status = DASD_CQR_QUEUED;
- /* requeue requests to blocklayer will only work for
- block device requests */
- if (_dasd_requeue_request(cqr))
- continue;
+ /* mark sleepon requests as ended */
+ if (cqr->callback_data == DASD_SLEEPON_START_TAG)
+ cqr->callback_data = DASD_SLEEPON_END_TAG;
/* remove requests from device and block queue */
list_del_init(&cqr->devlist);
@@ -3802,6 +3845,14 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev)
dasd_free_erp_request(cqr, cqr->memdev);
cqr = refers;
}
+
+ /*
+ * requeue requests to blocklayer will only work
+ * for block device requests
+ */
+ if (_dasd_requeue_request(cqr))
+ continue;
+
if (cqr->block)
list_del_init(&cqr->blocklist);
cqr->block->base->discipline->free_cp(
@@ -3812,15 +3863,56 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev)
* if requests remain then they are internal request
* and go back to the device queue
*/
- if (!list_empty(&freeze_queue)) {
+ if (!list_empty(&requeue_queue)) {
/* move freeze_queue to start of the ccw_queue */
- spin_lock_irq(get_ccwdev_lock(cdev));
- list_splice_tail(&freeze_queue, &device->ccw_queue);
- spin_unlock_irq(get_ccwdev_lock(cdev));
+ spin_lock_irq(get_ccwdev_lock(device->cdev));
+ list_splice_tail(&requeue_queue, &device->ccw_queue);
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
}
- dasd_put_device(device);
+ /* wake up generic waitqueue for eventually ended sleepon requests */
+ wake_up(&generic_waitq);
return rc;
}
+
+static void do_requeue_requests(struct work_struct *work)
+{
+ struct dasd_device *device = container_of(work, struct dasd_device,
+ requeue_requests);
+ dasd_generic_requeue_all_requests(device);
+ dasd_device_remove_stop_bits(device, DASD_STOPPED_NOT_ACC);
+ if (device->block)
+ dasd_schedule_block_bh(device->block);
+ dasd_put_device(device);
+}
+
+void dasd_schedule_requeue(struct dasd_device *device)
+{
+ dasd_get_device(device);
+ /* queue call to dasd_reload_device to the kernel event daemon. */
+ if (!schedule_work(&device->requeue_requests))
+ dasd_put_device(device);
+}
+EXPORT_SYMBOL(dasd_schedule_requeue);
+
+int dasd_generic_pm_freeze(struct ccw_device *cdev)
+{
+ struct dasd_device *device = dasd_device_from_cdev(cdev);
+ int rc;
+
+ if (IS_ERR(device))
+ return PTR_ERR(device);
+
+ /* mark device as suspended */
+ set_bit(DASD_FLAG_SUSPENDED, &device->flags);
+
+ if (device->discipline->freeze)
+ rc = device->discipline->freeze(device);
+
+ /* disallow new I/O */
+ dasd_device_set_stop_bits(device, DASD_STOPPED_PM);
+
+ return dasd_generic_requeue_all_requests(device);
+}
EXPORT_SYMBOL_GPL(dasd_generic_pm_freeze);
int dasd_generic_restore_device(struct ccw_device *cdev)
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index 8305ab688d57..774da20ceb58 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -152,7 +152,7 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
opm = ccw_device_get_path_mask(device->cdev);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
if (erp->lpm == 0)
- erp->lpm = device->path_data.opm &
+ erp->lpm = dasd_path_get_opm(device) &
~(erp->irb.esw.esw0.sublog.lpum);
else
erp->lpm &= ~(erp->irb.esw.esw0.sublog.lpum);
@@ -273,7 +273,7 @@ static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp)
!test_bit(DASD_CQR_VERIFY_PATH, &erp->flags)) {
erp->status = DASD_CQR_FILLED;
erp->retries = 10;
- erp->lpm = erp->startdev->path_data.opm;
+ erp->lpm = dasd_path_get_opm(erp->startdev);
erp->function = dasd_3990_erp_action_1_sec;
}
return erp;
@@ -674,7 +674,7 @@ dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense)
break;
case 0x0D:
dev_warn(&device->cdev->dev,
- "FORMAT 4 - No syn byte in count "
+ "FORMAT 4 - No sync byte in count "
"address area; offset active\n");
break;
case 0x0E:
@@ -684,7 +684,7 @@ dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense)
break;
case 0x0F:
dev_warn(&device->cdev->dev,
- "FORMAT 4 - No syn byte in data area; "
+ "FORMAT 4 - No sync byte in data area; "
"offset active\n");
break;
default:
@@ -999,7 +999,7 @@ dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense)
break;
default:
dev_warn(&device->cdev->dev,
- "FORMAT D - Reserved\n");
+ "FORMAT F - Reserved\n");
}
break;
@@ -1926,7 +1926,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense)
!test_bit(DASD_CQR_VERIFY_PATH, &erp->flags)) {
/* reset the lpm and the status to be able to
* try further actions. */
- erp->lpm = erp->startdev->path_data.opm;
+ erp->lpm = dasd_path_get_opm(erp->startdev);
erp->status = DASD_CQR_NEED_ERP;
}
}
@@ -2208,6 +2208,51 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_inspect_32 */
+static void dasd_3990_erp_disable_path(struct dasd_device *device, __u8 lpum)
+{
+ int pos = pathmask_to_pos(lpum);
+
+ /* no remaining path, cannot disable */
+ if (!(dasd_path_get_opm(device) & ~lpum))
+ return;
+
+ dev_err(&device->cdev->dev,
+ "Path %x.%02x (pathmask %02x) is disabled - IFCC threshold exceeded\n",
+ device->path[pos].cssid, device->path[pos].chpid, lpum);
+ dasd_path_remove_opm(device, lpum);
+ dasd_path_add_ifccpm(device, lpum);
+ device->path[pos].errorclk = 0;
+ atomic_set(&device->path[pos].error_count, 0);
+}
+
+static void dasd_3990_erp_account_error(struct dasd_ccw_req *erp)
+{
+ struct dasd_device *device = erp->startdev;
+ __u8 lpum = erp->refers->irb.esw.esw1.lpum;
+ int pos = pathmask_to_pos(lpum);
+ unsigned long long clk;
+
+ if (!device->path_thrhld)
+ return;
+
+ clk = get_tod_clock();
+ /*
+ * check if the last error is longer ago than the timeout,
+ * if so reset error state
+ */
+ if ((tod_to_ns(clk - device->path[pos].errorclk) / NSEC_PER_SEC)
+ >= device->path_interval) {
+ atomic_set(&device->path[pos].error_count, 0);
+ device->path[pos].errorclk = 0;
+ }
+ atomic_inc(&device->path[pos].error_count);
+ device->path[pos].errorclk = clk;
+ /* threshold exceeded disable path if possible */
+ if (atomic_read(&device->path[pos].error_count) >=
+ device->path_thrhld)
+ dasd_3990_erp_disable_path(device, lpum);
+}
+
/*
*****************************************************************************
* main ERP control functions (24 and 32 byte sense)
@@ -2237,6 +2282,7 @@ dasd_3990_erp_control_check(struct dasd_ccw_req *erp)
| SCHN_STAT_CHN_CTRL_CHK)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"channel or interface control check");
+ dasd_3990_erp_account_error(erp);
erp = dasd_3990_erp_action_4(erp, NULL);
}
return erp;
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 3cdbce45e464..dd46e96a3034 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -20,7 +20,7 @@
#include <linux/slab.h>
#include <asm/debug.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/ipl.h>
/* This is ugly... */
@@ -617,6 +617,7 @@ dasd_delete_device(struct dasd_device *device)
/* Wait for reference counter to drop to zero. */
wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
+ dasd_generic_free_discipline(device);
/* Disconnect dasd_device structure from ccw_device structure. */
cdev = device->cdev;
device->cdev = NULL;
@@ -724,27 +725,15 @@ static ssize_t dasd_ff_show(struct device *dev, struct device_attribute *attr,
static ssize_t dasd_ff_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct dasd_devmap *devmap;
- int val;
- char *endp;
-
- devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
- if (IS_ERR(devmap))
- return PTR_ERR(devmap);
+ unsigned int val;
+ int rc;
- val = simple_strtoul(buf, &endp, 0);
- if (((endp + 1) < (buf + count)) || (val > 1))
+ if (kstrtouint(buf, 0, &val) || val > 1)
return -EINVAL;
- spin_lock(&dasd_devmap_lock);
- if (val)
- devmap->features |= DASD_FEATURE_FAILFAST;
- else
- devmap->features &= ~DASD_FEATURE_FAILFAST;
- if (devmap->device)
- devmap->device->features = devmap->features;
- spin_unlock(&dasd_devmap_lock);
- return count;
+ rc = dasd_set_feature(to_ccwdev(dev), DASD_FEATURE_FAILFAST, val);
+
+ return rc ? : count;
}
static DEVICE_ATTR(failfast, 0644, dasd_ff_show, dasd_ff_store);
@@ -770,32 +759,41 @@ static ssize_t
dasd_ro_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct dasd_devmap *devmap;
+ struct ccw_device *cdev = to_ccwdev(dev);
struct dasd_device *device;
- int val;
- char *endp;
-
- devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
- if (IS_ERR(devmap))
- return PTR_ERR(devmap);
+ unsigned long flags;
+ unsigned int val;
+ int rc;
- val = simple_strtoul(buf, &endp, 0);
- if (((endp + 1) < (buf + count)) || (val > 1))
+ if (kstrtouint(buf, 0, &val) || val > 1)
return -EINVAL;
- spin_lock(&dasd_devmap_lock);
- if (val)
- devmap->features |= DASD_FEATURE_READONLY;
- else
- devmap->features &= ~DASD_FEATURE_READONLY;
- device = devmap->device;
- if (device) {
- device->features = devmap->features;
- val = val || test_bit(DASD_FLAG_DEVICE_RO, &device->flags);
+ rc = dasd_set_feature(cdev, DASD_FEATURE_READONLY, val);
+ if (rc)
+ return rc;
+
+ device = dasd_device_from_cdev(cdev);
+ if (IS_ERR(device))
+ return PTR_ERR(device);
+
+ spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+ val = val || test_bit(DASD_FLAG_DEVICE_RO, &device->flags);
+
+ if (!device->block || !device->block->gdp ||
+ test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+ goto out;
}
- spin_unlock(&dasd_devmap_lock);
- if (device && device->block && device->block->gdp)
- set_disk_ro(device->block->gdp, val);
+ /* Increase open_count to avoid losing the block device */
+ atomic_inc(&device->block->open_count);
+ spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+
+ set_disk_ro(device->block->gdp, val);
+ atomic_dec(&device->block->open_count);
+
+out:
+ dasd_put_device(device);
+
return count;
}
@@ -822,27 +820,15 @@ static ssize_t
dasd_erplog_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct dasd_devmap *devmap;
- int val;
- char *endp;
-
- devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
- if (IS_ERR(devmap))
- return PTR_ERR(devmap);
+ unsigned int val;
+ int rc;
- val = simple_strtoul(buf, &endp, 0);
- if (((endp + 1) < (buf + count)) || (val > 1))
+ if (kstrtouint(buf, 0, &val) || val > 1)
return -EINVAL;
- spin_lock(&dasd_devmap_lock);
- if (val)
- devmap->features |= DASD_FEATURE_ERPLOG;
- else
- devmap->features &= ~DASD_FEATURE_ERPLOG;
- if (devmap->device)
- devmap->device->features = devmap->features;
- spin_unlock(&dasd_devmap_lock);
- return count;
+ rc = dasd_set_feature(to_ccwdev(dev), DASD_FEATURE_ERPLOG, val);
+
+ return rc ? : count;
}
static DEVICE_ATTR(erplog, 0644, dasd_erplog_show, dasd_erplog_store);
@@ -870,16 +856,14 @@ dasd_use_diag_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct dasd_devmap *devmap;
+ unsigned int val;
ssize_t rc;
- int val;
- char *endp;
devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
if (IS_ERR(devmap))
return PTR_ERR(devmap);
- val = simple_strtoul(buf, &endp, 0);
- if (((endp + 1) < (buf + count)) || (val > 1))
+ if (kstrtouint(buf, 0, &val) || val > 1)
return -EINVAL;
spin_lock(&dasd_devmap_lock);
@@ -993,10 +977,12 @@ dasd_access_show(struct device *dev, struct device_attribute *attr,
if (IS_ERR(device))
return PTR_ERR(device);
- if (device->discipline->host_access_count)
- count = device->discipline->host_access_count(device);
- else
+ if (!device->discipline)
+ count = -ENODEV;
+ else if (!device->discipline->host_access_count)
count = -EOPNOTSUPP;
+ else
+ count = device->discipline->host_access_count(device);
dasd_put_device(device);
if (count < 0)
@@ -1196,27 +1182,25 @@ static ssize_t
dasd_eer_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct dasd_devmap *devmap;
- int val, rc;
- char *endp;
+ struct dasd_device *device;
+ unsigned int val;
+ int rc = 0;
- devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
- if (IS_ERR(devmap))
- return PTR_ERR(devmap);
- if (!devmap->device)
- return -ENODEV;
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return PTR_ERR(device);
- val = simple_strtoul(buf, &endp, 0);
- if (((endp + 1) < (buf + count)) || (val > 1))
+ if (kstrtouint(buf, 0, &val) || val > 1)
return -EINVAL;
- if (val) {
- rc = dasd_eer_enable(devmap->device);
- if (rc)
- return rc;
- } else
- dasd_eer_disable(devmap->device);
- return count;
+ if (val)
+ rc = dasd_eer_enable(device);
+ else
+ dasd_eer_disable(device);
+
+ dasd_put_device(device);
+
+ return rc ? : count;
}
static DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store);
@@ -1359,6 +1343,50 @@ dasd_timeout_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(timeout, 0644,
dasd_timeout_show, dasd_timeout_store);
+
+static ssize_t
+dasd_path_reset_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dasd_device *device;
+ unsigned int val;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+
+ if ((kstrtouint(buf, 16, &val) != 0) || val > 0xff)
+ val = 0;
+
+ if (device->discipline && device->discipline->reset_path)
+ device->discipline->reset_path(device, (__u8) val);
+
+ dasd_put_device(device);
+ return count;
+}
+
+static DEVICE_ATTR(path_reset, 0200, NULL, dasd_path_reset_store);
+
+static ssize_t dasd_hpf_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dasd_device *device;
+ int hpf;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+ if (!device->discipline || !device->discipline->hpf_enabled) {
+ dasd_put_device(device);
+ return snprintf(buf, PAGE_SIZE, "%d\n", dasd_nofcx);
+ }
+ hpf = device->discipline->hpf_enabled(device);
+ dasd_put_device(device);
+ return snprintf(buf, PAGE_SIZE, "%d\n", hpf);
+}
+
+static DEVICE_ATTR(hpf, 0444, dasd_hpf_show, NULL);
+
static ssize_t dasd_reservation_policy_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1384,27 +1412,17 @@ static ssize_t dasd_reservation_policy_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct dasd_devmap *devmap;
+ struct ccw_device *cdev = to_ccwdev(dev);
int rc;
- devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
- if (IS_ERR(devmap))
- return PTR_ERR(devmap);
- rc = 0;
- spin_lock(&dasd_devmap_lock);
if (sysfs_streq("ignore", buf))
- devmap->features &= ~DASD_FEATURE_FAILONSLCK;
+ rc = dasd_set_feature(cdev, DASD_FEATURE_FAILONSLCK, 0);
else if (sysfs_streq("fail", buf))
- devmap->features |= DASD_FEATURE_FAILONSLCK;
+ rc = dasd_set_feature(cdev, DASD_FEATURE_FAILONSLCK, 1);
else
rc = -EINVAL;
- if (devmap->device)
- devmap->device->features = devmap->features;
- spin_unlock(&dasd_devmap_lock);
- if (rc)
- return rc;
- else
- return count;
+
+ return rc ? : count;
}
static DEVICE_ATTR(reservation_policy, 0644,
@@ -1460,25 +1478,120 @@ static ssize_t dasd_pm_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dasd_device *device;
- u8 opm, nppm, cablepm, cuirpm, hpfpm;
+ u8 opm, nppm, cablepm, cuirpm, hpfpm, ifccpm;
device = dasd_device_from_cdev(to_ccwdev(dev));
if (IS_ERR(device))
return sprintf(buf, "0\n");
- opm = device->path_data.opm;
- nppm = device->path_data.npm;
- cablepm = device->path_data.cablepm;
- cuirpm = device->path_data.cuirpm;
- hpfpm = device->path_data.hpfpm;
+ opm = dasd_path_get_opm(device);
+ nppm = dasd_path_get_nppm(device);
+ cablepm = dasd_path_get_cablepm(device);
+ cuirpm = dasd_path_get_cuirpm(device);
+ hpfpm = dasd_path_get_hpfpm(device);
+ ifccpm = dasd_path_get_ifccpm(device);
dasd_put_device(device);
- return sprintf(buf, "%02x %02x %02x %02x %02x\n", opm, nppm,
- cablepm, cuirpm, hpfpm);
+ return sprintf(buf, "%02x %02x %02x %02x %02x %02x\n", opm, nppm,
+ cablepm, cuirpm, hpfpm, ifccpm);
}
static DEVICE_ATTR(path_masks, 0444, dasd_pm_show, NULL);
+/*
+ * threshold value for IFCC/CCC errors
+ */
+static ssize_t
+dasd_path_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dasd_device *device;
+ int len;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+ len = snprintf(buf, PAGE_SIZE, "%lu\n", device->path_thrhld);
+ dasd_put_device(device);
+ return len;
+}
+
+static ssize_t
+dasd_path_threshold_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dasd_device *device;
+ unsigned long flags;
+ unsigned long val;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+
+ if ((kstrtoul(buf, 10, &val) != 0) ||
+ (val > DASD_THRHLD_MAX) || val == 0) {
+ dasd_put_device(device);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(get_ccwdev_lock(to_ccwdev(dev)), flags);
+ if (val)
+ device->path_thrhld = val;
+ spin_unlock_irqrestore(get_ccwdev_lock(to_ccwdev(dev)), flags);
+ dasd_put_device(device);
+ return count;
+}
+
+static DEVICE_ATTR(path_threshold, 0644, dasd_path_threshold_show,
+ dasd_path_threshold_store);
+/*
+ * interval for IFCC/CCC checks
+ * meaning time with no IFCC/CCC error before the error counter
+ * gets reset
+ */
+static ssize_t
+dasd_path_interval_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dasd_device *device;
+ int len;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+ len = snprintf(buf, PAGE_SIZE, "%lu\n", device->path_interval);
+ dasd_put_device(device);
+ return len;
+}
+
+static ssize_t
+dasd_path_interval_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dasd_device *device;
+ unsigned long flags;
+ unsigned long val;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+
+ if ((kstrtoul(buf, 10, &val) != 0) ||
+ (val > DASD_INTERVAL_MAX) || val == 0) {
+ dasd_put_device(device);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(get_ccwdev_lock(to_ccwdev(dev)), flags);
+ if (val)
+ device->path_interval = val;
+ spin_unlock_irqrestore(get_ccwdev_lock(to_ccwdev(dev)), flags);
+ dasd_put_device(device);
+ return count;
+}
+
+static DEVICE_ATTR(path_interval, 0644, dasd_path_interval_show,
+ dasd_path_interval_store);
+
+
static struct attribute * dasd_attrs[] = {
&dev_attr_readonly.attr,
&dev_attr_discipline.attr,
@@ -1499,6 +1612,10 @@ static struct attribute * dasd_attrs[] = {
&dev_attr_safe_offline.attr,
&dev_attr_host_access_count.attr,
&dev_attr_path_masks.attr,
+ &dev_attr_path_threshold.attr,
+ &dev_attr_path_interval.attr,
+ &dev_attr_path_reset.attr,
+ &dev_attr_hpf.attr,
NULL,
};
@@ -1530,7 +1647,7 @@ dasd_set_feature(struct ccw_device *cdev, int feature, int flag)
{
struct dasd_devmap *devmap;
- devmap = dasd_find_busid(dev_name(&cdev->dev));
+ devmap = dasd_devmap_from_cdev(cdev);
if (IS_ERR(devmap))
return PTR_ERR(devmap);
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 98bbec44bcd0..ade04216c970 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -26,7 +26,7 @@
#include <asm/idals.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/cio.h>
#include <asm/ccwdev.h>
#include <asm/itcw.h>
@@ -1042,8 +1042,11 @@ static void dasd_eckd_clear_conf_data(struct dasd_device *device)
private->conf_data = NULL;
private->conf_len = 0;
for (i = 0; i < 8; i++) {
- kfree(private->path_conf_data[i]);
- private->path_conf_data[i] = NULL;
+ kfree(device->path[i].conf_data);
+ device->path[i].conf_data = NULL;
+ device->path[i].cssid = 0;
+ device->path[i].ssid = 0;
+ device->path[i].chpid = 0;
}
}
@@ -1055,13 +1058,14 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
int rc, path_err, pos;
__u8 lpm, opm;
struct dasd_eckd_private *private, path_private;
- struct dasd_path *path_data;
struct dasd_uid *uid;
char print_path_uid[60], print_device_uid[60];
+ struct channel_path_desc *chp_desc;
+ struct subchannel_id sch_id;
private = device->private;
- path_data = &device->path_data;
opm = ccw_device_get_path_mask(device->cdev);
+ ccw_device_get_schid(device->cdev, &sch_id);
conf_data_saved = 0;
path_err = 0;
/* get configuration data per operational path */
@@ -1081,7 +1085,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
"No configuration data "
"retrieved");
/* no further analysis possible */
- path_data->opm |= lpm;
+ dasd_path_add_opm(device, opm);
continue; /* no error */
}
/* save first valid configuration data */
@@ -1098,8 +1102,13 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
}
pos = pathmask_to_pos(lpm);
/* store per path conf_data */
- private->path_conf_data[pos] =
- (struct dasd_conf_data *) conf_data;
+ device->path[pos].conf_data = conf_data;
+ device->path[pos].cssid = sch_id.cssid;
+ device->path[pos].ssid = sch_id.ssid;
+ chp_desc = ccw_device_get_chp_desc(device->cdev, pos);
+ if (chp_desc)
+ device->path[pos].chpid = chp_desc->chpid;
+ kfree(chp_desc);
/*
* build device UID that other path data
* can be compared to it
@@ -1154,42 +1163,66 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
"device %s instead of %s\n", lpm,
print_path_uid, print_device_uid);
path_err = -EINVAL;
- path_data->cablepm |= lpm;
+ dasd_path_add_cablepm(device, lpm);
continue;
}
pos = pathmask_to_pos(lpm);
/* store per path conf_data */
- private->path_conf_data[pos] =
- (struct dasd_conf_data *) conf_data;
+ device->path[pos].conf_data = conf_data;
+ device->path[pos].cssid = sch_id.cssid;
+ device->path[pos].ssid = sch_id.ssid;
+ chp_desc = ccw_device_get_chp_desc(device->cdev, pos);
+ if (chp_desc)
+ device->path[pos].chpid = chp_desc->chpid;
+ kfree(chp_desc);
path_private.conf_data = NULL;
path_private.conf_len = 0;
}
switch (dasd_eckd_path_access(conf_data, conf_len)) {
case 0x02:
- path_data->npm |= lpm;
+ dasd_path_add_nppm(device, lpm);
break;
case 0x03:
- path_data->ppm |= lpm;
+ dasd_path_add_ppm(device, lpm);
break;
}
- if (!path_data->opm) {
- path_data->opm = lpm;
+ if (!dasd_path_get_opm(device)) {
+ dasd_path_set_opm(device, lpm);
dasd_generic_path_operational(device);
} else {
- path_data->opm |= lpm;
+ dasd_path_add_opm(device, lpm);
}
- /*
- * if the path is used
- * it should not be in one of the negative lists
- */
- path_data->cablepm &= ~lpm;
- path_data->hpfpm &= ~lpm;
- path_data->cuirpm &= ~lpm;
}
return path_err;
}
+static u32 get_fcx_max_data(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+ int fcx_in_css, fcx_in_gneq, fcx_in_features;
+ int tpm, mdc;
+
+ if (dasd_nofcx)
+ return 0;
+ /* is transport mode supported? */
+ fcx_in_css = css_general_characteristics.fcx;
+ fcx_in_gneq = private->gneq->reserved2[7] & 0x04;
+ fcx_in_features = private->features.feature[40] & 0x80;
+ tpm = fcx_in_css && fcx_in_gneq && fcx_in_features;
+
+ if (!tpm)
+ return 0;
+
+ mdc = ccw_device_get_mdc(device->cdev, 0);
+ if (mdc < 0) {
+ dev_warn(&device->cdev->dev, "Detecting the maximum supported data size for zHPF requests failed\n");
+ return 0;
+ } else {
+ return (u32)mdc * FCX_MAX_DATA_FACTOR;
+ }
+}
+
static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm)
{
struct dasd_eckd_private *private = device->private;
@@ -1205,7 +1238,7 @@ static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm)
mdc, lpm);
return mdc;
}
- fcx_max_data = mdc * FCX_MAX_DATA_FACTOR;
+ fcx_max_data = (u32)mdc * FCX_MAX_DATA_FACTOR;
if (fcx_max_data < private->fcx_max_data) {
dev_warn(&device->cdev->dev,
"The maximum data size for zHPF requests %u "
@@ -1222,8 +1255,7 @@ static int rebuild_device_uid(struct dasd_device *device,
struct path_verification_work_data *data)
{
struct dasd_eckd_private *private = device->private;
- struct dasd_path *path_data = &device->path_data;
- __u8 lpm, opm = path_data->opm;
+ __u8 lpm, opm = dasd_path_get_opm(device);
int rc = -ENODEV;
for (lpm = 0x80; lpm; lpm >>= 1) {
@@ -1356,7 +1388,7 @@ static void do_path_verification_work(struct work_struct *work)
* in other case the device UID may have changed and
* the first working path UID will be used as device UID
*/
- if (device->path_data.opm &&
+ if (dasd_path_get_opm(device) &&
dasd_eckd_compare_path_uid(device, &path_private)) {
/*
* the comparison was not successful
@@ -1406,23 +1438,17 @@ static void do_path_verification_work(struct work_struct *work)
* situation in dasd_start_IO.
*/
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- if (!device->path_data.opm && opm) {
- device->path_data.opm = opm;
- device->path_data.cablepm &= ~opm;
- device->path_data.cuirpm &= ~opm;
- device->path_data.hpfpm &= ~opm;
+ if (!dasd_path_get_opm(device) && opm) {
+ dasd_path_set_opm(device, opm);
dasd_generic_path_operational(device);
} else {
- device->path_data.opm |= opm;
- device->path_data.cablepm &= ~opm;
- device->path_data.cuirpm &= ~opm;
- device->path_data.hpfpm &= ~opm;
+ dasd_path_add_opm(device, opm);
}
- device->path_data.npm |= npm;
- device->path_data.ppm |= ppm;
- device->path_data.tbvpm |= epm;
- device->path_data.cablepm |= cablepm;
- device->path_data.hpfpm |= hpfpm;
+ dasd_path_add_nppm(device, npm);
+ dasd_path_add_ppm(device, ppm);
+ dasd_path_add_tbvpm(device, epm);
+ dasd_path_add_cablepm(device, cablepm);
+ dasd_path_add_nohpfpm(device, hpfpm);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
}
clear_bit(DASD_FLAG_PATH_VERIFY, &device->flags);
@@ -1456,6 +1482,19 @@ static int dasd_eckd_verify_path(struct dasd_device *device, __u8 lpm)
return 0;
}
+static void dasd_eckd_reset_path(struct dasd_device *device, __u8 pm)
+{
+ struct dasd_eckd_private *private = device->private;
+ unsigned long flags;
+
+ if (!private->fcx_max_data)
+ private->fcx_max_data = get_fcx_max_data(device);
+ spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+ dasd_path_set_tbvpm(device, pm ? : dasd_path_get_notoperpm(device));
+ dasd_schedule_device_bh(device);
+ spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+}
+
static int dasd_eckd_read_features(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
@@ -1652,32 +1691,6 @@ static void dasd_eckd_kick_validate_server(struct dasd_device *device)
dasd_put_device(device);
}
-static u32 get_fcx_max_data(struct dasd_device *device)
-{
- struct dasd_eckd_private *private = device->private;
- int fcx_in_css, fcx_in_gneq, fcx_in_features;
- int tpm, mdc;
-
- if (dasd_nofcx)
- return 0;
- /* is transport mode supported? */
- fcx_in_css = css_general_characteristics.fcx;
- fcx_in_gneq = private->gneq->reserved2[7] & 0x04;
- fcx_in_features = private->features.feature[40] & 0x80;
- tpm = fcx_in_css && fcx_in_gneq && fcx_in_features;
-
- if (!tpm)
- return 0;
-
- mdc = ccw_device_get_mdc(device->cdev, 0);
- if (mdc < 0) {
- dev_warn(&device->cdev->dev, "Detecting the maximum supported"
- " data size for zHPF requests failed\n");
- return 0;
- } else
- return mdc * FCX_MAX_DATA_FACTOR;
-}
-
/*
* Check device characteristics.
* If the device is accessible using ECKD discipline, the device is enabled.
@@ -1729,10 +1742,11 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
if (rc)
goto out_err1;
- /* set default timeout */
+ /* set some default values */
device->default_expires = DASD_EXPIRES;
- /* set default retry count */
device->default_retries = DASD_RETRIES;
+ device->path_thrhld = DASD_ECKD_PATH_THRHLD;
+ device->path_interval = DASD_ECKD_PATH_INTERVAL;
if (private->gneq) {
value = 1;
@@ -1839,13 +1853,16 @@ static void dasd_eckd_uncheck_device(struct dasd_device *device)
private->gneq = NULL;
private->conf_len = 0;
for (i = 0; i < 8; i++) {
- kfree(private->path_conf_data[i]);
- if ((__u8 *)private->path_conf_data[i] ==
+ kfree(device->path[i].conf_data);
+ if ((__u8 *)device->path[i].conf_data ==
private->conf_data) {
private->conf_data = NULL;
private->conf_len = 0;
}
- private->path_conf_data[i] = NULL;
+ device->path[i].conf_data = NULL;
+ device->path[i].cssid = 0;
+ device->path[i].ssid = 0;
+ device->path[i].chpid = 0;
}
kfree(private->conf_data);
private->conf_data = NULL;
@@ -2966,7 +2983,7 @@ static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr)
if (cqr->block && (cqr->startdev != cqr->block->base)) {
dasd_eckd_reset_ccw_to_base_io(cqr);
cqr->startdev = cqr->block->base;
- cqr->lpm = cqr->block->base->path_data.opm;
+ cqr->lpm = dasd_path_get_opm(cqr->block->base);
}
};
@@ -3251,7 +3268,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single(
cqr->memdev = startdev;
cqr->block = block;
cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
- cqr->lpm = startdev->path_data.ppm;
+ cqr->lpm = dasd_path_get_ppm(startdev);
cqr->retries = startdev->default_retries;
cqr->buildclk = get_tod_clock();
cqr->status = DASD_CQR_FILLED;
@@ -3426,7 +3443,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
cqr->memdev = startdev;
cqr->block = block;
cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
- cqr->lpm = startdev->path_data.ppm;
+ cqr->lpm = dasd_path_get_ppm(startdev);
cqr->retries = startdev->default_retries;
cqr->buildclk = get_tod_clock();
cqr->status = DASD_CQR_FILLED;
@@ -3735,7 +3752,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
cqr->memdev = startdev;
cqr->block = block;
cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
- cqr->lpm = startdev->path_data.ppm;
+ cqr->lpm = dasd_path_get_ppm(startdev);
cqr->retries = startdev->default_retries;
cqr->buildclk = get_tod_clock();
cqr->status = DASD_CQR_FILLED;
@@ -3962,7 +3979,7 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev,
cqr->memdev = startdev;
cqr->block = block;
cqr->expires = startdev->default_expires * HZ;
- cqr->lpm = startdev->path_data.ppm;
+ cqr->lpm = dasd_path_get_ppm(startdev);
cqr->retries = startdev->default_retries;
cqr->buildclk = get_tod_clock();
cqr->status = DASD_CQR_FILLED;
@@ -4783,7 +4800,8 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw),
scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw),
scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw),
- irb->scsw.tm.fcxs, irb->scsw.tm.schxs,
+ irb->scsw.tm.fcxs,
+ (irb->scsw.tm.ifob << 7) | irb->scsw.tm.sesq,
req ? req->intrc : 0);
len += sprintf(page + len, PRINTK_HEADER
" device %s: Failing TCW: %p\n",
@@ -5201,7 +5219,7 @@ static int dasd_eckd_query_host_access(struct dasd_device *device,
cqr->buildclk = get_tod_clock();
cqr->status = DASD_CQR_FILLED;
- rc = dasd_sleep_on(cqr);
+ rc = dasd_sleep_on_interruptible(cqr);
if (rc == 0) {
*data = *host_access;
} else {
@@ -5306,11 +5324,10 @@ static int dasd_hosts_print(struct dasd_device *device, struct seq_file *m)
*/
static int
dasd_eckd_psf_cuir_response(struct dasd_device *device, int response,
- __u32 message_id,
- struct channel_path_desc *desc,
- struct subchannel_id sch_id)
+ __u32 message_id, __u8 lpum)
{
struct dasd_psf_cuir_response *psf_cuir;
+ int pos = pathmask_to_pos(lpum);
struct dasd_ccw_req *cqr;
struct ccw1 *ccw;
int rc;
@@ -5328,11 +5345,10 @@ dasd_eckd_psf_cuir_response(struct dasd_device *device, int response,
psf_cuir = (struct dasd_psf_cuir_response *)cqr->data;
psf_cuir->order = PSF_ORDER_CUIR_RESPONSE;
psf_cuir->cc = response;
- if (desc)
- psf_cuir->chpid = desc->chpid;
+ psf_cuir->chpid = device->path[pos].chpid;
psf_cuir->message_id = message_id;
- psf_cuir->cssid = sch_id.cssid;
- psf_cuir->ssid = sch_id.ssid;
+ psf_cuir->cssid = device->path[pos].cssid;
+ psf_cuir->ssid = device->path[pos].ssid;
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_PSF;
ccw->cda = (__u32)(addr_t)psf_cuir;
@@ -5363,20 +5379,19 @@ static struct dasd_conf_data *dasd_eckd_get_ref_conf(struct dasd_device *device,
__u8 lpum,
struct dasd_cuir_message *cuir)
{
- struct dasd_eckd_private *private = device->private;
struct dasd_conf_data *conf_data;
int path, pos;
if (cuir->record_selector == 0)
goto out;
for (path = 0x80, pos = 0; path; path >>= 1, pos++) {
- conf_data = private->path_conf_data[pos];
+ conf_data = device->path[pos].conf_data;
if (conf_data->gneq.record_selector ==
cuir->record_selector)
return conf_data;
}
out:
- return private->path_conf_data[pathmask_to_pos(lpum)];
+ return device->path[pathmask_to_pos(lpum)].conf_data;
}
/*
@@ -5391,7 +5406,6 @@ out:
static int dasd_eckd_cuir_scope(struct dasd_device *device, __u8 lpum,
struct dasd_cuir_message *cuir)
{
- struct dasd_eckd_private *private = device->private;
struct dasd_conf_data *ref_conf_data;
unsigned long bitmask = 0, mask = 0;
struct dasd_conf_data *conf_data;
@@ -5417,11 +5431,10 @@ static int dasd_eckd_cuir_scope(struct dasd_device *device, __u8 lpum,
mask |= cuir->neq_map[1] << 8;
mask |= cuir->neq_map[0] << 16;
- for (path = 0x80; path; path >>= 1) {
+ for (path = 0; path < 8; path++) {
/* initialise data per path */
bitmask = mask;
- pos = pathmask_to_pos(path);
- conf_data = private->path_conf_data[pos];
+ conf_data = device->path[path].conf_data;
pos = 8 - ffs(cuir->ned_map);
ned = (char *) &conf_data->neds[pos];
/* compare reference ned and per path ned */
@@ -5442,33 +5455,29 @@ static int dasd_eckd_cuir_scope(struct dasd_device *device, __u8 lpum,
continue;
/* device and path match the reference values
add path to CUIR scope */
- tbcpm |= path;
+ tbcpm |= 0x80 >> path;
}
return tbcpm;
}
static void dasd_eckd_cuir_notify_user(struct dasd_device *device,
- unsigned long paths,
- struct subchannel_id sch_id, int action)
+ unsigned long paths, int action)
{
- struct channel_path_desc *desc;
int pos;
while (paths) {
/* get position of bit in mask */
- pos = ffs(paths) - 1;
+ pos = 8 - ffs(paths);
/* get channel path descriptor from this position */
- desc = ccw_device_get_chp_desc(device->cdev, 7 - pos);
if (action == CUIR_QUIESCE)
- pr_warn("Service on the storage server caused path "
- "%x.%02x to go offline", sch_id.cssid,
- desc ? desc->chpid : 0);
+ pr_warn("Service on the storage server caused path %x.%02x to go offline",
+ device->path[pos].cssid,
+ device->path[pos].chpid);
else if (action == CUIR_RESUME)
- pr_info("Path %x.%02x is back online after service "
- "on the storage server", sch_id.cssid,
- desc ? desc->chpid : 0);
- kfree(desc);
- clear_bit(pos, &paths);
+ pr_info("Path %x.%02x is back online after service on the storage server",
+ device->path[pos].cssid,
+ device->path[pos].chpid);
+ clear_bit(7 - pos, &paths);
}
}
@@ -5479,16 +5488,16 @@ static int dasd_eckd_cuir_remove_path(struct dasd_device *device, __u8 lpum,
tbcpm = dasd_eckd_cuir_scope(device, lpum, cuir);
/* nothing to do if path is not in use */
- if (!(device->path_data.opm & tbcpm))
+ if (!(dasd_path_get_opm(device) & tbcpm))
return 0;
- if (!(device->path_data.opm & ~tbcpm)) {
+ if (!(dasd_path_get_opm(device) & ~tbcpm)) {
/* no path would be left if the CUIR action is taken
return error */
return -EINVAL;
}
/* remove device from operational path mask */
- device->path_data.opm &= ~tbcpm;
- device->path_data.cuirpm |= tbcpm;
+ dasd_path_remove_opm(device, tbcpm);
+ dasd_path_add_cuirpm(device, tbcpm);
return tbcpm;
}
@@ -5501,7 +5510,6 @@ static int dasd_eckd_cuir_remove_path(struct dasd_device *device, __u8 lpum,
* notify the already set offline devices again
*/
static int dasd_eckd_cuir_quiesce(struct dasd_device *device, __u8 lpum,
- struct subchannel_id sch_id,
struct dasd_cuir_message *cuir)
{
struct dasd_eckd_private *private = device->private;
@@ -5556,14 +5564,13 @@ static int dasd_eckd_cuir_quiesce(struct dasd_device *device, __u8 lpum,
}
}
/* notify user about all paths affected by CUIR action */
- dasd_eckd_cuir_notify_user(device, paths, sch_id, CUIR_QUIESCE);
+ dasd_eckd_cuir_notify_user(device, paths, CUIR_QUIESCE);
return 0;
out_err:
return tbcpm;
}
static int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum,
- struct subchannel_id sch_id,
struct dasd_cuir_message *cuir)
{
struct dasd_eckd_private *private = device->private;
@@ -5581,8 +5588,8 @@ static int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum,
alias_list) {
tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
paths |= tbcpm;
- if (!(dev->path_data.opm & tbcpm)) {
- dev->path_data.tbvpm |= tbcpm;
+ if (!(dasd_path_get_opm(dev) & tbcpm)) {
+ dasd_path_add_tbvpm(dev, tbcpm);
dasd_schedule_device_bh(dev);
}
}
@@ -5591,8 +5598,8 @@ static int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum,
alias_list) {
tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
paths |= tbcpm;
- if (!(dev->path_data.opm & tbcpm)) {
- dev->path_data.tbvpm |= tbcpm;
+ if (!(dasd_path_get_opm(dev) & tbcpm)) {
+ dasd_path_add_tbvpm(dev, tbcpm);
dasd_schedule_device_bh(dev);
}
}
@@ -5605,8 +5612,8 @@ static int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum,
alias_list) {
tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
paths |= tbcpm;
- if (!(dev->path_data.opm & tbcpm)) {
- dev->path_data.tbvpm |= tbcpm;
+ if (!(dasd_path_get_opm(dev) & tbcpm)) {
+ dasd_path_add_tbvpm(dev, tbcpm);
dasd_schedule_device_bh(dev);
}
}
@@ -5615,14 +5622,14 @@ static int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum,
alias_list) {
tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
paths |= tbcpm;
- if (!(dev->path_data.opm & tbcpm)) {
- dev->path_data.tbvpm |= tbcpm;
+ if (!(dasd_path_get_opm(dev) & tbcpm)) {
+ dasd_path_add_tbvpm(dev, tbcpm);
dasd_schedule_device_bh(dev);
}
}
}
/* notify user about all paths affected by CUIR action */
- dasd_eckd_cuir_notify_user(device, paths, sch_id, CUIR_RESUME);
+ dasd_eckd_cuir_notify_user(device, paths, CUIR_RESUME);
return 0;
}
@@ -5630,38 +5637,31 @@ static void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages,
__u8 lpum)
{
struct dasd_cuir_message *cuir = messages;
- struct channel_path_desc *desc;
- struct subchannel_id sch_id;
- int pos, response;
+ int response;
DBF_DEV_EVENT(DBF_WARNING, device,
"CUIR request: %016llx %016llx %016llx %08x",
((u64 *)cuir)[0], ((u64 *)cuir)[1], ((u64 *)cuir)[2],
((u32 *)cuir)[3]);
- ccw_device_get_schid(device->cdev, &sch_id);
- pos = pathmask_to_pos(lpum);
- desc = ccw_device_get_chp_desc(device->cdev, pos);
if (cuir->code == CUIR_QUIESCE) {
/* quiesce */
- if (dasd_eckd_cuir_quiesce(device, lpum, sch_id, cuir))
+ if (dasd_eckd_cuir_quiesce(device, lpum, cuir))
response = PSF_CUIR_LAST_PATH;
else
response = PSF_CUIR_COMPLETED;
} else if (cuir->code == CUIR_RESUME) {
/* resume */
- dasd_eckd_cuir_resume(device, lpum, sch_id, cuir);
+ dasd_eckd_cuir_resume(device, lpum, cuir);
response = PSF_CUIR_COMPLETED;
} else
response = PSF_CUIR_NOT_SUPPORTED;
dasd_eckd_psf_cuir_response(device, response,
- cuir->message_id, desc, sch_id);
+ cuir->message_id, lpum);
DBF_DEV_EVENT(DBF_WARNING, device,
"CUIR response: %d on message ID %08x", response,
cuir->message_id);
- /* free descriptor copy */
- kfree(desc);
/* to make sure there is no attention left schedule work again */
device->discipline->check_attention(device, lpum);
}
@@ -5708,6 +5708,63 @@ static int dasd_eckd_check_attention(struct dasd_device *device, __u8 lpum)
return 0;
}
+static int dasd_eckd_disable_hpf_path(struct dasd_device *device, __u8 lpum)
+{
+ if (~lpum & dasd_path_get_opm(device)) {
+ dasd_path_add_nohpfpm(device, lpum);
+ dasd_path_remove_opm(device, lpum);
+ dev_err(&device->cdev->dev,
+ "Channel path %02X lost HPF functionality and is disabled\n",
+ lpum);
+ return 1;
+ }
+ return 0;
+}
+
+static void dasd_eckd_disable_hpf_device(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+
+ dev_err(&device->cdev->dev,
+ "High Performance FICON disabled\n");
+ private->fcx_max_data = 0;
+}
+
+static int dasd_eckd_hpf_enabled(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+
+ return private->fcx_max_data ? 1 : 0;
+}
+
+static void dasd_eckd_handle_hpf_error(struct dasd_device *device,
+ struct irb *irb)
+{
+ struct dasd_eckd_private *private = device->private;
+
+ if (!private->fcx_max_data) {
+ /* sanity check for no HPF, the error makes no sense */
+ DBF_DEV_EVENT(DBF_WARNING, device, "%s",
+ "Trying to disable HPF for a non HPF device");
+ return;
+ }
+ if (irb->scsw.tm.sesq == SCSW_SESQ_DEV_NOFCX) {
+ dasd_eckd_disable_hpf_device(device);
+ } else if (irb->scsw.tm.sesq == SCSW_SESQ_PATH_NOFCX) {
+ if (dasd_eckd_disable_hpf_path(device, irb->esw.esw1.lpum))
+ return;
+ dasd_eckd_disable_hpf_device(device);
+ dasd_path_set_tbvpm(device,
+ dasd_path_get_hpfpm(device));
+ }
+ /*
+ * prevent that any new I/O ist started on the device and schedule a
+ * requeue of existing requests
+ */
+ dasd_device_set_stop_bits(device, DASD_STOPPED_NOT_ACC);
+ dasd_schedule_requeue(device);
+}
+
static struct ccw_driver dasd_eckd_driver = {
.driver = {
.name = "dasd-eckd",
@@ -5776,6 +5833,10 @@ static struct dasd_discipline dasd_eckd_discipline = {
.check_attention = dasd_eckd_check_attention,
.host_access_count = dasd_eckd_host_access_count,
.hosts_print = dasd_hosts_print,
+ .handle_hpf_error = dasd_eckd_handle_hpf_error,
+ .disable_hpf = dasd_eckd_disable_hpf_device,
+ .hpf_enabled = dasd_eckd_hpf_enabled,
+ .reset_path = dasd_eckd_reset_path,
};
static int __init
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index 59803626ea36..e2a710c250a5 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -94,6 +94,8 @@
#define FCX_MAX_DATA_FACTOR 65536
#define DASD_ECKD_RCD_DATA_SIZE 256
+#define DASD_ECKD_PATH_THRHLD 256
+#define DASD_ECKD_PATH_INTERVAL 300
/*****************************************************************************
* SECTION: Type Definitions
@@ -535,8 +537,7 @@ struct dasd_eckd_private {
struct dasd_eckd_characteristics rdc_data;
u8 *conf_data;
int conf_len;
- /* per path configuration data */
- struct dasd_conf_data *path_conf_data[8];
+
/* pointers to specific parts in the conf_data */
struct dasd_ned *ned;
struct dasd_sneq *sneq;
diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c
index 21ef63cf0960..8713fefd794b 100644
--- a/drivers/s390/block/dasd_eer.c
+++ b/drivers/s390/block/dasd_eer.c
@@ -20,7 +20,7 @@
#include <linux/err.h>
#include <linux/slab.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/atomic.h>
#include <asm/ebcdic.h>
@@ -454,20 +454,30 @@ static void dasd_eer_snss_cb(struct dasd_ccw_req *cqr, void *data)
*/
int dasd_eer_enable(struct dasd_device *device)
{
- struct dasd_ccw_req *cqr;
+ struct dasd_ccw_req *cqr = NULL;
unsigned long flags;
struct ccw1 *ccw;
+ int rc = 0;
+ spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
if (device->eer_cqr)
- return 0;
+ goto out;
+ else if (!device->discipline ||
+ strcmp(device->discipline->name, "ECKD"))
+ rc = -EMEDIUMTYPE;
+ else if (test_bit(DASD_FLAG_OFFLINE, &device->flags))
+ rc = -EBUSY;
- if (!device->discipline || strcmp(device->discipline->name, "ECKD"))
- return -EPERM; /* FIXME: -EMEDIUMTYPE ? */
+ if (rc)
+ goto out;
cqr = dasd_kmalloc_request(DASD_ECKD_MAGIC, 1 /* SNSS */,
SNSS_DATA_SIZE, device);
- if (IS_ERR(cqr))
- return -ENOMEM;
+ if (IS_ERR(cqr)) {
+ rc = -ENOMEM;
+ cqr = NULL;
+ goto out;
+ }
cqr->startdev = device;
cqr->retries = 255;
@@ -485,15 +495,18 @@ int dasd_eer_enable(struct dasd_device *device)
cqr->status = DASD_CQR_FILLED;
cqr->callback = dasd_eer_snss_cb;
- spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
if (!device->eer_cqr) {
device->eer_cqr = cqr;
cqr = NULL;
}
+
+out:
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+
if (cqr)
dasd_kfree_request(cqr, device);
- return 0;
+
+ return rc;
}
/*
diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c
index e1e88486b2b4..9e3419124264 100644
--- a/drivers/s390/block/dasd_erp.c
+++ b/drivers/s390/block/dasd_erp.c
@@ -15,7 +15,7 @@
#include <asm/debug.h>
#include <asm/ebcdic.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
/* This is ugly... */
#define PRINTK_HEADER "dasd_erp:"
@@ -96,7 +96,7 @@ dasd_default_erp_action(struct dasd_ccw_req *cqr)
"default ERP called (%i retries left)",
cqr->retries);
if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))
- cqr->lpm = device->path_data.opm;
+ cqr->lpm = dasd_path_get_opm(device);
cqr->status = DASD_CQR_FILLED;
} else {
pr_err("%s: default ERP has run out of retries and failed\n",
@@ -169,12 +169,12 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb)
device = cqr->startdev;
if (cqr->intrc == -ETIMEDOUT) {
dev_err(&device->cdev->dev,
- "A timeout error occurred for cqr %p", cqr);
+ "A timeout error occurred for cqr %p\n", cqr);
return;
}
if (cqr->intrc == -ENOLINK) {
dev_err(&device->cdev->dev,
- "A transport error occurred for cqr %p", cqr);
+ "A transport error occurred for cqr %p\n", cqr);
return;
}
/* dump sense data */
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index d7b5b550364b..462cab5d4302 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -168,7 +168,7 @@ dasd_fba_check_characteristics(struct dasd_device *device)
device->default_expires = DASD_EXPIRES;
device->default_retries = FBA_DEFAULT_RETRIES;
- device->path_data.opm = LPM_ANYPATH;
+ dasd_path_set_opm(device, LPM_ANYPATH);
readonly = dasd_device_is_ro(device);
if (readonly)
diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c
index e2fa759bf2ad..8b1341fb2e0d 100644
--- a/drivers/s390/block/dasd_genhd.c
+++ b/drivers/s390/block/dasd_genhd.c
@@ -16,7 +16,7 @@
#include <linux/fs.h>
#include <linux/blkpg.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
/* This is ugly... */
#define PRINTK_HEADER "dasd_gendisk:"
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index ac7027e6d52b..24be210c10e5 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -55,6 +55,7 @@
#include <asm/debug.h>
#include <asm/dasd.h>
#include <asm/idals.h>
+#include <linux/bitops.h>
/* DASD discipline magic */
#define DASD_ECKD_MAGIC 0xC5C3D2C4
@@ -377,6 +378,10 @@ struct dasd_discipline {
int (*check_attention)(struct dasd_device *, __u8);
int (*host_access_count)(struct dasd_device *);
int (*hosts_print)(struct dasd_device *, struct seq_file *);
+ void (*handle_hpf_error)(struct dasd_device *, struct irb *);
+ void (*disable_hpf)(struct dasd_device *);
+ int (*hpf_enabled)(struct dasd_device *);
+ void (*reset_path)(struct dasd_device *, __u8);
};
extern struct dasd_discipline *dasd_diag_discipline_pointer;
@@ -397,17 +402,31 @@ extern struct dasd_discipline *dasd_diag_discipline_pointer;
#define DASD_EER_STATECHANGE 3
#define DASD_EER_PPRCSUSPEND 4
+/* DASD path handling */
+
+#define DASD_PATH_OPERATIONAL 1
+#define DASD_PATH_TBV 2
+#define DASD_PATH_PP 3
+#define DASD_PATH_NPP 4
+#define DASD_PATH_MISCABLED 5
+#define DASD_PATH_NOHPF 6
+#define DASD_PATH_CUIR 7
+#define DASD_PATH_IFCC 8
+
+#define DASD_THRHLD_MAX 4294967295U
+#define DASD_INTERVAL_MAX 4294967295U
+
struct dasd_path {
- __u8 opm;
- __u8 tbvpm;
- __u8 ppm;
- __u8 npm;
- /* paths that are not used because of a special condition */
- __u8 cablepm; /* miss-cabled */
- __u8 hpfpm; /* the HPF requirements of the other paths are not met */
- __u8 cuirpm; /* CUIR varied offline */
+ unsigned long flags;
+ u8 cssid;
+ u8 ssid;
+ u8 chpid;
+ struct dasd_conf_data *conf_data;
+ atomic_t error_count;
+ unsigned long long errorclk;
};
+
struct dasd_profile_info {
/* legacy part of profile data, as in dasd_profile_info_t */
unsigned int dasd_io_reqs; /* number of requests processed */
@@ -458,7 +477,8 @@ struct dasd_device {
struct dasd_discipline *discipline;
struct dasd_discipline *base_discipline;
void *private;
- struct dasd_path path_data;
+ struct dasd_path path[8];
+ __u8 opm;
/* Device state and target state. */
int state, target;
@@ -483,6 +503,7 @@ struct dasd_device {
struct work_struct reload_device;
struct work_struct kick_validate;
struct work_struct suc_work;
+ struct work_struct requeue_requests;
struct timer_list timer;
debug_info_t *debug_area;
@@ -498,6 +519,9 @@ struct dasd_device {
unsigned long blk_timeout;
+ unsigned long path_thrhld;
+ unsigned long path_interval;
+
struct dentry *debugfs_dentry;
struct dentry *hosts_dentry;
struct dasd_profile profile;
@@ -707,6 +731,7 @@ void dasd_set_target_state(struct dasd_device *, int);
void dasd_kick_device(struct dasd_device *);
void dasd_restore_device(struct dasd_device *);
void dasd_reload_device(struct dasd_device *);
+void dasd_schedule_requeue(struct dasd_device *);
void dasd_add_request_head(struct dasd_ccw_req *);
void dasd_add_request_tail(struct dasd_ccw_req *);
@@ -725,6 +750,7 @@ void dasd_block_clear_timer(struct dasd_block *);
int dasd_cancel_req(struct dasd_ccw_req *);
int dasd_flush_device_queue(struct dasd_device *);
int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *);
+void dasd_generic_free_discipline(struct dasd_device *);
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);
@@ -834,4 +860,410 @@ static inline int dasd_eer_enabled(struct dasd_device *device)
#define dasd_eer_enabled(d) (0)
#endif /* CONFIG_DASD_ERR */
+
+/* DASD path handling functions */
+
+/*
+ * helper functions to modify bit masks for a given channel path for a device
+ */
+static inline int dasd_path_is_operational(struct dasd_device *device, int chp)
+{
+ return test_bit(DASD_PATH_OPERATIONAL, &device->path[chp].flags);
+}
+
+static inline int dasd_path_need_verify(struct dasd_device *device, int chp)
+{
+ return test_bit(DASD_PATH_TBV, &device->path[chp].flags);
+}
+
+static inline void dasd_path_verify(struct dasd_device *device, int chp)
+{
+ __set_bit(DASD_PATH_TBV, &device->path[chp].flags);
+}
+
+static inline void dasd_path_clear_verify(struct dasd_device *device, int chp)
+{
+ __clear_bit(DASD_PATH_TBV, &device->path[chp].flags);
+}
+
+static inline void dasd_path_clear_all_verify(struct dasd_device *device)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++)
+ dasd_path_clear_verify(device, chp);
+}
+
+static inline void dasd_path_operational(struct dasd_device *device, int chp)
+{
+ __set_bit(DASD_PATH_OPERATIONAL, &device->path[chp].flags);
+ device->opm |= (0x80 >> chp);
+}
+
+static inline void dasd_path_nonpreferred(struct dasd_device *device, int chp)
+{
+ __set_bit(DASD_PATH_NPP, &device->path[chp].flags);
+}
+
+static inline int dasd_path_is_nonpreferred(struct dasd_device *device, int chp)
+{
+ return test_bit(DASD_PATH_NPP, &device->path[chp].flags);
+}
+
+static inline void dasd_path_clear_nonpreferred(struct dasd_device *device,
+ int chp)
+{
+ __clear_bit(DASD_PATH_NPP, &device->path[chp].flags);
+}
+
+static inline void dasd_path_preferred(struct dasd_device *device, int chp)
+{
+ __set_bit(DASD_PATH_PP, &device->path[chp].flags);
+}
+
+static inline int dasd_path_is_preferred(struct dasd_device *device, int chp)
+{
+ return test_bit(DASD_PATH_PP, &device->path[chp].flags);
+}
+
+static inline void dasd_path_clear_preferred(struct dasd_device *device,
+ int chp)
+{
+ __clear_bit(DASD_PATH_PP, &device->path[chp].flags);
+}
+
+static inline void dasd_path_clear_oper(struct dasd_device *device, int chp)
+{
+ __clear_bit(DASD_PATH_OPERATIONAL, &device->path[chp].flags);
+ device->opm &= ~(0x80 >> chp);
+}
+
+static inline void dasd_path_clear_cable(struct dasd_device *device, int chp)
+{
+ __clear_bit(DASD_PATH_MISCABLED, &device->path[chp].flags);
+}
+
+static inline void dasd_path_cuir(struct dasd_device *device, int chp)
+{
+ __set_bit(DASD_PATH_CUIR, &device->path[chp].flags);
+}
+
+static inline int dasd_path_is_cuir(struct dasd_device *device, int chp)
+{
+ return test_bit(DASD_PATH_CUIR, &device->path[chp].flags);
+}
+
+static inline void dasd_path_clear_cuir(struct dasd_device *device, int chp)
+{
+ __clear_bit(DASD_PATH_CUIR, &device->path[chp].flags);
+}
+
+static inline void dasd_path_ifcc(struct dasd_device *device, int chp)
+{
+ set_bit(DASD_PATH_IFCC, &device->path[chp].flags);
+}
+
+static inline int dasd_path_is_ifcc(struct dasd_device *device, int chp)
+{
+ return test_bit(DASD_PATH_IFCC, &device->path[chp].flags);
+}
+
+static inline void dasd_path_clear_ifcc(struct dasd_device *device, int chp)
+{
+ clear_bit(DASD_PATH_IFCC, &device->path[chp].flags);
+}
+
+static inline void dasd_path_clear_nohpf(struct dasd_device *device, int chp)
+{
+ __clear_bit(DASD_PATH_NOHPF, &device->path[chp].flags);
+}
+
+static inline void dasd_path_miscabled(struct dasd_device *device, int chp)
+{
+ __set_bit(DASD_PATH_MISCABLED, &device->path[chp].flags);
+}
+
+static inline int dasd_path_is_miscabled(struct dasd_device *device, int chp)
+{
+ return test_bit(DASD_PATH_MISCABLED, &device->path[chp].flags);
+}
+
+static inline void dasd_path_nohpf(struct dasd_device *device, int chp)
+{
+ __set_bit(DASD_PATH_NOHPF, &device->path[chp].flags);
+}
+
+static inline int dasd_path_is_nohpf(struct dasd_device *device, int chp)
+{
+ return test_bit(DASD_PATH_NOHPF, &device->path[chp].flags);
+}
+
+/*
+ * get functions for path masks
+ * will return a path masks for the given device
+ */
+
+static inline __u8 dasd_path_get_opm(struct dasd_device *device)
+{
+ return device->opm;
+}
+
+static inline __u8 dasd_path_get_tbvpm(struct dasd_device *device)
+{
+ int chp;
+ __u8 tbvpm = 0x00;
+
+ for (chp = 0; chp < 8; chp++)
+ if (dasd_path_need_verify(device, chp))
+ tbvpm |= 0x80 >> chp;
+ return tbvpm;
+}
+
+static inline __u8 dasd_path_get_nppm(struct dasd_device *device)
+{
+ int chp;
+ __u8 npm = 0x00;
+
+ for (chp = 0; chp < 8; chp++) {
+ if (dasd_path_is_nonpreferred(device, chp))
+ npm |= 0x80 >> chp;
+ }
+ return npm;
+}
+
+static inline __u8 dasd_path_get_ppm(struct dasd_device *device)
+{
+ int chp;
+ __u8 ppm = 0x00;
+
+ for (chp = 0; chp < 8; chp++)
+ if (dasd_path_is_preferred(device, chp))
+ ppm |= 0x80 >> chp;
+ return ppm;
+}
+
+static inline __u8 dasd_path_get_cablepm(struct dasd_device *device)
+{
+ int chp;
+ __u8 cablepm = 0x00;
+
+ for (chp = 0; chp < 8; chp++)
+ if (dasd_path_is_miscabled(device, chp))
+ cablepm |= 0x80 >> chp;
+ return cablepm;
+}
+
+static inline __u8 dasd_path_get_cuirpm(struct dasd_device *device)
+{
+ int chp;
+ __u8 cuirpm = 0x00;
+
+ for (chp = 0; chp < 8; chp++)
+ if (dasd_path_is_cuir(device, chp))
+ cuirpm |= 0x80 >> chp;
+ return cuirpm;
+}
+
+static inline __u8 dasd_path_get_ifccpm(struct dasd_device *device)
+{
+ int chp;
+ __u8 ifccpm = 0x00;
+
+ for (chp = 0; chp < 8; chp++)
+ if (dasd_path_is_ifcc(device, chp))
+ ifccpm |= 0x80 >> chp;
+ return ifccpm;
+}
+
+static inline __u8 dasd_path_get_hpfpm(struct dasd_device *device)
+{
+ int chp;
+ __u8 hpfpm = 0x00;
+
+ for (chp = 0; chp < 8; chp++)
+ if (dasd_path_is_nohpf(device, chp))
+ hpfpm |= 0x80 >> chp;
+ return hpfpm;
+}
+
+/*
+ * add functions for path masks
+ * the existing path mask will be extended by the given path mask
+ */
+static inline void dasd_path_add_tbvpm(struct dasd_device *device, __u8 pm)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++)
+ if (pm & (0x80 >> chp))
+ dasd_path_verify(device, chp);
+}
+
+static inline __u8 dasd_path_get_notoperpm(struct dasd_device *device)
+{
+ int chp;
+ __u8 nopm = 0x00;
+
+ for (chp = 0; chp < 8; chp++)
+ if (dasd_path_is_nohpf(device, chp) ||
+ dasd_path_is_ifcc(device, chp) ||
+ dasd_path_is_cuir(device, chp) ||
+ dasd_path_is_miscabled(device, chp))
+ nopm |= 0x80 >> chp;
+ return nopm;
+}
+
+static inline void dasd_path_add_opm(struct dasd_device *device, __u8 pm)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++)
+ if (pm & (0x80 >> chp)) {
+ dasd_path_operational(device, chp);
+ /*
+ * if the path is used
+ * it should not be in one of the negative lists
+ */
+ dasd_path_clear_nohpf(device, chp);
+ dasd_path_clear_cuir(device, chp);
+ dasd_path_clear_cable(device, chp);
+ dasd_path_clear_ifcc(device, chp);
+ }
+}
+
+static inline void dasd_path_add_cablepm(struct dasd_device *device, __u8 pm)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++)
+ if (pm & (0x80 >> chp))
+ dasd_path_miscabled(device, chp);
+}
+
+static inline void dasd_path_add_cuirpm(struct dasd_device *device, __u8 pm)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++)
+ if (pm & (0x80 >> chp))
+ dasd_path_cuir(device, chp);
+}
+
+static inline void dasd_path_add_ifccpm(struct dasd_device *device, __u8 pm)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++)
+ if (pm & (0x80 >> chp))
+ dasd_path_ifcc(device, chp);
+}
+
+static inline void dasd_path_add_nppm(struct dasd_device *device, __u8 pm)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++)
+ if (pm & (0x80 >> chp))
+ dasd_path_nonpreferred(device, chp);
+}
+
+static inline void dasd_path_add_nohpfpm(struct dasd_device *device, __u8 pm)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++)
+ if (pm & (0x80 >> chp))
+ dasd_path_nohpf(device, chp);
+}
+
+static inline void dasd_path_add_ppm(struct dasd_device *device, __u8 pm)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++)
+ if (pm & (0x80 >> chp))
+ dasd_path_preferred(device, chp);
+}
+
+/*
+ * set functions for path masks
+ * the existing path mask will be replaced by the given path mask
+ */
+static inline void dasd_path_set_tbvpm(struct dasd_device *device, __u8 pm)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++)
+ if (pm & (0x80 >> chp))
+ dasd_path_verify(device, chp);
+ else
+ dasd_path_clear_verify(device, chp);
+}
+
+static inline void dasd_path_set_opm(struct dasd_device *device, __u8 pm)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++) {
+ dasd_path_clear_oper(device, chp);
+ if (pm & (0x80 >> chp)) {
+ dasd_path_operational(device, chp);
+ /*
+ * if the path is used
+ * it should not be in one of the negative lists
+ */
+ dasd_path_clear_nohpf(device, chp);
+ dasd_path_clear_cuir(device, chp);
+ dasd_path_clear_cable(device, chp);
+ dasd_path_clear_ifcc(device, chp);
+ }
+ }
+}
+
+/*
+ * remove functions for path masks
+ * the existing path mask will be cleared with the given path mask
+ */
+static inline void dasd_path_remove_opm(struct dasd_device *device, __u8 pm)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++) {
+ if (pm & (0x80 >> chp))
+ dasd_path_clear_oper(device, chp);
+ }
+}
+
+/*
+ * add the newly available path to the to be verified pm and remove it from
+ * normal operation until it is verified
+ */
+static inline void dasd_path_available(struct dasd_device *device, int chp)
+{
+ dasd_path_clear_oper(device, chp);
+ dasd_path_verify(device, chp);
+}
+
+static inline void dasd_path_notoper(struct dasd_device *device, int chp)
+{
+ dasd_path_clear_oper(device, chp);
+ dasd_path_clear_preferred(device, chp);
+ dasd_path_clear_nonpreferred(device, chp);
+}
+
+/*
+ * remove all paths from normal operation
+ */
+static inline void dasd_path_no_path(struct dasd_device *device)
+{
+ int chp;
+
+ for (chp = 0; chp < 8; chp++)
+ dasd_path_notoper(device, chp);
+
+ dasd_path_clear_all_verify(device);
+}
+
+/* end - path handling */
+
#endif /* DASD_H */
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index 9dfbd972f844..ec65c1e51c2a 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -21,7 +21,7 @@
#include <asm/ccwdev.h>
#include <asm/schid.h>
#include <asm/cmb.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
/* This is ugly... */
#define PRINTK_HEADER "dasd_ioctl:"
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index bad7a196bf84..70dc2c4cd3f7 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -20,7 +20,7 @@
#include <linux/proc_fs.h>
#include <asm/debug.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
/* This is ugly... */
#define PRINTK_HEADER "dasd_proc:"
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index 288f59a4147b..b9d7e755c8a3 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -41,7 +41,7 @@
#include <linux/suspend.h>
#include <linux/platform_device.h>
#include <linux/gfp.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#define XPRAM_NAME "xpram"
#define XPRAM_DEVS 1 /* one partition */
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 931d10e86837..9ec4ae056158 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -9,7 +9,6 @@
* Dan Morrison, IBM Corporation <dmorriso@cse.buffalo.edu>
*/
-#include <linux/module.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/tty.h>
@@ -26,7 +25,7 @@
#include <asm/cio.h>
#include <asm/io.h>
#include <asm/ebcdic.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/delay.h>
#include <asm/cpcmd.h>
#include <asm/setup.h>
@@ -1215,13 +1214,4 @@ static int __init tty3215_init(void)
tty3215_driver = driver;
return 0;
}
-
-static void __exit tty3215_exit(void)
-{
- tty_unregister_driver(tty3215_driver);
- put_tty_driver(tty3215_driver);
- ccw_driver_unregister(&raw3215_ccw_driver);
-}
-
-module_init(tty3215_init);
-module_exit(tty3215_exit);
+device_initcall(tty3215_init);
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index 6b1577c73fe7..285b4006f44b 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -124,7 +124,12 @@ con3270_create_status(struct con3270 *cp)
static void
con3270_update_string(struct con3270 *cp, struct string *s, int nr)
{
- if (s->len >= cp->view.cols - 5)
+ if (s->len < 4) {
+ /* This indicates a bug, but printing a warning would
+ * cause a deadlock. */
+ return;
+ }
+ if (s->string[s->len - 4] != TO_RA)
return;
raw3270_buffer_address(cp->view.dev, s->string + s->len - 3,
cp->view.cols * (nr + 1));
@@ -460,11 +465,11 @@ con3270_cline_end(struct con3270 *cp)
cp->cline->len + 4 : cp->view.cols;
s = con3270_alloc_string(cp, size);
memcpy(s->string, cp->cline->string, cp->cline->len);
- if (s->len < cp->view.cols - 5) {
+ if (cp->cline->len < cp->view.cols - 5) {
s->string[s->len - 4] = TO_RA;
s->string[s->len - 1] = 0;
} else {
- while (--size > cp->cline->len)
+ while (--size >= cp->cline->len)
s->string[size] = cp->view.ascebc[' '];
}
/* Replace cline with allocated line s and reset cline. */
diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c
index 7b9c50aa4cc9..82c913318b73 100644
--- a/drivers/s390/char/keyboard.c
+++ b/drivers/s390/char/keyboard.c
@@ -14,7 +14,7 @@
#include <linux/consolemap.h>
#include <linux/kbd_kern.h>
#include <linux/kbd_diacr.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "keyboard.h"
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index ebdeaa53182d..027ac6ae5eea 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -23,7 +23,7 @@
#include <linux/device.h>
#include <linux/slab.h>
#include <net/iucv/iucv.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/ebcdic.h>
#include <asm/extmem.h>
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
index 9b5d1138b2e2..571a7e352755 100644
--- a/drivers/s390/char/monwriter.c
+++ b/drivers/s390/char/monwriter.c
@@ -21,7 +21,7 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
#include <asm/appldata.h>
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index 7a10c56334bb..e1fc7eb043d6 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -59,6 +59,7 @@
typedef unsigned int sclp_cmdw_t;
+#define SCLP_CMDW_READ_CPU_INFO 0x00010001
#define SCLP_CMDW_READ_EVENT_DATA 0x00770005
#define SCLP_CMDW_WRITE_EVENT_DATA 0x00760005
#define SCLP_CMDW_WRITE_EVENT_MASK 0x00780005
@@ -102,6 +103,28 @@ struct init_sccb {
sccb_mask_t sclp_send_mask;
} __attribute__((packed));
+struct read_cpu_info_sccb {
+ struct sccb_header header;
+ u16 nr_configured;
+ u16 offset_configured;
+ u16 nr_standby;
+ u16 offset_standby;
+ u8 reserved[4096 - 16];
+} __attribute__((packed, aligned(PAGE_SIZE)));
+
+static inline void sclp_fill_core_info(struct sclp_core_info *info,
+ struct read_cpu_info_sccb *sccb)
+{
+ char *page = (char *) sccb;
+
+ memset(info, 0, sizeof(*info));
+ info->configured = sccb->nr_configured;
+ info->standby = sccb->nr_standby;
+ info->combined = sccb->nr_configured + sccb->nr_standby;
+ memcpy(&info->core, page + sccb->offset_configured,
+ info->combined * sizeof(struct sclp_core_entry));
+}
+
#define SCLP_HAS_CHP_INFO (sclp.facilities & 0x8000000000000000ULL)
#define SCLP_HAS_CHP_RECONFIG (sclp.facilities & 0x2000000000000000ULL)
#define SCLP_HAS_CPU_INFO (sclp.facilities & 0x0800000000000000ULL)
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index e3fc7539116b..b9c5522b8a68 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -80,33 +80,10 @@ out:
* CPU configuration related functions.
*/
-#define SCLP_CMDW_READ_CPU_INFO 0x00010001
#define SCLP_CMDW_CONFIGURE_CPU 0x00110001
#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001
-struct read_cpu_info_sccb {
- struct sccb_header header;
- u16 nr_configured;
- u16 offset_configured;
- u16 nr_standby;
- u16 offset_standby;
- u8 reserved[4096 - 16];
-} __attribute__((packed, aligned(PAGE_SIZE)));
-
-static void sclp_fill_core_info(struct sclp_core_info *info,
- struct read_cpu_info_sccb *sccb)
-{
- char *page = (char *) sccb;
-
- memset(info, 0, sizeof(*info));
- info->configured = sccb->nr_configured;
- info->standby = sccb->nr_standby;
- info->combined = sccb->nr_configured + sccb->nr_standby;
- memcpy(&info->core, page + sccb->offset_configured,
- info->combined * sizeof(struct sclp_core_entry));
-}
-
-int sclp_get_core_info(struct sclp_core_info *info)
+int _sclp_get_core_info(struct sclp_core_info *info)
{
int rc;
struct read_cpu_info_sccb *sccb;
diff --git a/drivers/s390/char/sclp_ctl.c b/drivers/s390/char/sclp_ctl.c
index ea607a4a1bdd..78a7e4f94721 100644
--- a/drivers/s390/char/sclp_ctl.c
+++ b/drivers/s390/char/sclp_ctl.c
@@ -10,7 +10,7 @@
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/gfp.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <asm/compat.h>
@@ -126,21 +126,4 @@ static struct miscdevice sclp_ctl_device = {
.name = "sclp",
.fops = &sclp_ctl_fops,
};
-
-/*
- * Register sclp_ctl misc device
- */
-static int __init sclp_ctl_init(void)
-{
- return misc_register(&sclp_ctl_device);
-}
-module_init(sclp_ctl_init);
-
-/*
- * Deregister sclp_ctl misc device
- */
-static void __exit sclp_ctl_exit(void)
-{
- misc_deregister(&sclp_ctl_device);
-}
-module_exit(sclp_ctl_exit);
+builtin_misc_device(sclp_ctl_device);
diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c
index c71df0c7dedc..f8e46c22e641 100644
--- a/drivers/s390/char/sclp_early.c
+++ b/drivers/s390/char/sclp_early.c
@@ -221,6 +221,36 @@ static int __init sclp_set_event_mask(struct init_sccb *sccb,
return sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_MASK, sccb);
}
+static struct sclp_core_info sclp_core_info_early __initdata;
+static int sclp_core_info_early_valid __initdata;
+
+static void __init sclp_init_core_info_early(struct read_cpu_info_sccb *sccb)
+{
+ int rc;
+
+ if (!SCLP_HAS_CPU_INFO)
+ return;
+ memset(sccb, 0, sizeof(*sccb));
+ sccb->header.length = sizeof(*sccb);
+ do {
+ rc = sclp_cmd_sync_early(SCLP_CMDW_READ_CPU_INFO, sccb);
+ } while (rc == -EBUSY);
+ if (rc)
+ return;
+ if (sccb->header.response_code != 0x0010)
+ return;
+ sclp_fill_core_info(&sclp_core_info_early, sccb);
+ sclp_core_info_early_valid = 1;
+}
+
+int __init _sclp_get_core_info_early(struct sclp_core_info *info)
+{
+ if (!sclp_core_info_early_valid)
+ return -EIO;
+ *info = sclp_core_info_early;
+ return 0;
+}
+
static long __init sclp_hsa_size_init(struct sdias_sccb *sccb)
{
sccb_init_eq_size(sccb);
@@ -293,6 +323,7 @@ void __init sclp_early_detect(void)
void *sccb = &sccb_early;
sclp_facilities_detect(sccb);
+ sclp_init_core_info_early(sccb);
sclp_hsa_size_detect(sccb);
/* Turn off SCLP event notifications. Also save remote masks in the
diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c
index 475e470d9768..e4958511168a 100644
--- a/drivers/s390/char/sclp_quiesce.c
+++ b/drivers/s390/char/sclp_quiesce.c
@@ -6,7 +6,6 @@
* Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
-#include <linux/module.h>
#include <linux/types.h>
#include <linux/cpumask.h>
#include <linux/smp.h>
@@ -80,5 +79,4 @@ static int __init sclp_quiesce_init(void)
{
return sclp_register(&sclp_quiesce_event);
}
-
-module_init(sclp_quiesce_init);
+device_initcall(sclp_quiesce_init);
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index 6010cd347a08..91b26df5227d 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -13,7 +13,7 @@
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/ctype.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "sclp.h"
#include "sclp_rw.h"
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index 3c6e174e19b6..236b736ae136 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -7,7 +7,6 @@
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
-#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
@@ -16,7 +15,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gfp.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "ctrlchar.h"
#include "sclp.h"
@@ -573,4 +572,4 @@ sclp_tty_init(void)
sclp_tty_driver = driver;
return 0;
}
-module_init(sclp_tty_init);
+device_initcall(sclp_tty_init);
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index 68d6ee7ae504..095481d32236 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -26,7 +26,7 @@
#include <linux/reboot.h>
#include <linux/slab.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "sclp.h"
#include "ctrlchar.h"
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index d3d1936057b4..e352047ed9f7 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -312,15 +312,10 @@ static int tape_3592_ioctl_kekl_set(struct tape_device *device,
return -ENOSYS;
if (!crypt_enabled(device))
return -EUNATCH;
- ext_kekls = kmalloc(sizeof(*ext_kekls), GFP_KERNEL);
- if (!ext_kekls)
- return -ENOMEM;
- if (copy_from_user(ext_kekls, (char __user *)arg, sizeof(*ext_kekls))) {
- rc = -EFAULT;
- goto out;
- }
+ ext_kekls = memdup_user((char __user *)arg, sizeof(*ext_kekls));
+ if (IS_ERR(ext_kekls))
+ return PTR_ERR(ext_kekls);
rc = tape_3592_kekl_set(device, ext_kekls);
-out:
kfree(ext_kekls);
return rc;
}
diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c
index 77f9b9c2f701..46ac1164f242 100644
--- a/drivers/s390/char/tape_char.c
+++ b/drivers/s390/char/tape_char.c
@@ -18,7 +18,7 @@
#include <linux/mtio.h>
#include <linux/compat.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#define TAPE_DBF_AREA tape_core_dbf
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c
index 272cb6cd1b2a..e5ebe2fbee23 100644
--- a/drivers/s390/char/tty3270.c
+++ b/drivers/s390/char/tty3270.c
@@ -24,7 +24,7 @@
#include <asm/ccwdev.h>
#include <asm/cio.h>
#include <asm/ebcdic.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "raw3270.h"
#include "tty3270.h"
diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c
index 2a67b496a9e2..65f5a794f26d 100644
--- a/drivers/s390/char/vmcp.c
+++ b/drivers/s390/char/vmcp.c
@@ -21,7 +21,7 @@
#include <asm/compat.h>
#include <asm/cpcmd.h>
#include <asm/debug.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "vmcp.h"
static debug_info_t *vmcp_debug;
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index e883063c7258..57974a1e0e03 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -21,7 +21,7 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/atomic.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/cpcmd.h>
#include <asm/debug.h>
#include <asm/ebcdic.h>
@@ -870,7 +870,7 @@ static int __init vmlogrdr_init(void)
goto cleanup;
for (i=0; i < MAXMINOR; ++i ) {
- sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL);
+ sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sys_ser[i].buffer) {
rc = -ENOMEM;
break;
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 6c30e93ab8fa..04aceb694d51 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -15,7 +15,7 @@
#include <linux/slab.h>
#include <linux/module.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/cio.h>
#include <asm/ccwdev.h>
#include <asm/debug.h>
@@ -306,10 +306,11 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
{
struct urdev *urd;
- TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n",
- intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
- irb->scsw.cmd.count);
-
+ if (!IS_ERR(irb)) {
+ TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n",
+ intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
+ irb->scsw.cmd.count);
+ }
if (!intparm) {
TRACE("ur_int_handler: unsolicited interrupt\n");
return;
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
index 16992e2a40ad..d3b51edb056e 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -7,6 +7,7 @@
*
* Copyright IBM Corp. 2003, 2008
* Author(s): Michael Holzheu
+ * License: GPL
*/
#define KMSG_COMPONENT "zdump"
@@ -16,14 +17,13 @@
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/debugfs.h>
-#include <linux/module.h>
#include <linux/memblock.h>
#include <asm/asm-offsets.h>
#include <asm/ipl.h>
#include <asm/sclp.h>
#include <asm/setup.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/debug.h>
#include <asm/processor.h>
#include <asm/irqflags.h>
@@ -320,7 +320,7 @@ static int __init zcore_init(void)
goto fail;
}
- pr_alert("DETECTED 'S390X (64 bit) OS'\n");
+ pr_alert("The dump process started for a 64-bit operating system\n");
rc = init_cpu_info();
if (rc)
goto fail;
@@ -364,22 +364,4 @@ fail:
diag308(DIAG308_REL_HSA, NULL);
return rc;
}
-
-static void __exit zcore_exit(void)
-{
- debug_unregister(zcore_dbf);
- sclp_sdias_exit();
- free_page((unsigned long) ipl_block);
- debugfs_remove(zcore_hsa_file);
- debugfs_remove(zcore_reipl_file);
- debugfs_remove(zcore_memmap_file);
- debugfs_remove(zcore_dir);
- diag308(DIAG308_REL_HSA, NULL);
-}
-
-MODULE_AUTHOR("Copyright IBM Corp. 2003,2008");
-MODULE_DESCRIPTION("zcore module for zfcpdump support");
-MODULE_LICENSE("GPL");
-
subsys_initcall(zcore_init);
-module_exit(zcore_exit);
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 9082476b51db..bf7f5d4c50e1 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -17,7 +17,7 @@
#include <linux/ctype.h>
#include <linux/device.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/cio.h>
#include <asm/ipl.h>
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index 46be25c7461e..876c7e6e3a99 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -780,7 +780,7 @@ static int cfg_wait_idle(void)
static int __init chp_init(void)
{
struct chp_id chpid;
- int ret;
+ int state, ret;
ret = crw_register_handler(CRW_RSC_CPATH, chp_process_crw);
if (ret)
@@ -791,7 +791,9 @@ static int __init chp_init(void)
return 0;
/* Register available channel-paths. */
chp_id_for_each(&chpid) {
- if (chp_info_get_status(chpid) != CHP_STATUS_NOT_RECOGNIZED)
+ state = chp_info_get_status(chpid);
+ if (state == CHP_STATUS_CONFIGURED ||
+ state == CHP_STATUS_STANDBY)
chp_new(chpid);
}
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 940e725bde1e..11674698b36d 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -95,12 +95,13 @@ struct chsc_ssd_area {
int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
{
struct chsc_ssd_area *ssd_area;
+ unsigned long flags;
int ccode;
int ret;
int i;
int mask;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
ssd_area = chsc_page;
ssd_area->request.length = 0x0010;
@@ -144,7 +145,7 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
ssd->fla[i] = ssd_area->fla[i];
}
out:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -832,9 +833,10 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable)
u32 fmt : 4;
u32 : 16;
} __attribute__ ((packed)) *secm_area;
+ unsigned long flags;
int ret, ccode;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
secm_area = chsc_page;
secm_area->request.length = 0x0050;
@@ -864,7 +866,7 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable)
CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n",
secm_area->response.code);
out:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -992,6 +994,7 @@ chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
int chsc_get_channel_measurement_chars(struct channel_path *chp)
{
+ unsigned long flags;
int ccode, ret;
struct {
@@ -1021,7 +1024,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
if (!css_chsc_characteristics.scmc || !css_chsc_characteristics.secm)
return -EINVAL;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
scmc_area = chsc_page;
scmc_area->request.length = 0x0010;
@@ -1053,7 +1056,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
(struct cmg_chars *) &scmc_area->data);
out:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -1134,6 +1137,7 @@ struct css_chsc_char css_chsc_characteristics;
int __init
chsc_determine_css_characteristics(void)
{
+ unsigned long flags;
int result;
struct {
struct chsc_header request;
@@ -1146,7 +1150,7 @@ chsc_determine_css_characteristics(void)
u32 chsc_char[508];
} __attribute__ ((packed)) *scsc_area;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
scsc_area = chsc_page;
scsc_area->request.length = 0x0010;
@@ -1168,7 +1172,7 @@ chsc_determine_css_characteristics(void)
CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n",
scsc_area->response.code);
exit:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return result;
}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 93de0b46b489..f0e57aefb5f2 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -127,7 +127,6 @@ extern int cio_resume (struct subchannel *);
extern int cio_halt (struct subchannel *);
extern int cio_start (struct subchannel *, struct ccw1 *, __u8);
extern int cio_start_key (struct subchannel *, struct ccw1 *, __u8, __u8);
-extern int cio_cancel (struct subchannel *);
extern int cio_set_options (struct subchannel *, int);
extern int cio_update_schib(struct subchannel *sch);
extern int cio_commit_config(struct subchannel *sch);
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 268aa23afa01..6b6386e9a500 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -30,7 +30,7 @@
#include <linux/device.h>
#include <linux/init.h>
#include <linux/list.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/timex.h> /* get_tod_clock() */
@@ -1389,13 +1389,7 @@ static int __init init_cmf(void)
"%s (mode %s)\n", format_string, detect_string);
return 0;
}
-module_init(init_cmf);
-
-
-MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("channel measurement facility base driver\n"
- "Copyright IBM Corp. 2003\n");
+device_initcall(init_cmf);
EXPORT_SYMBOL_GPL(enable_cmf);
EXPORT_SYMBOL_GPL(disable_cmf);
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 3d2b20ee613f..bc099b61394d 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -5,12 +5,14 @@
*
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
+ *
+ * License: GPL
*/
#define KMSG_COMPONENT "cio"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
@@ -1285,5 +1287,3 @@ void css_driver_unregister(struct css_driver *cdrv)
driver_unregister(&cdrv->drv);
}
EXPORT_SYMBOL_GPL(css_driver_unregister);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 6a58bc8f46e2..79823ee9c100 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -5,12 +5,14 @@
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *
+ * License: GPL
*/
#define KMSG_COMPONENT "cio"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
@@ -145,7 +147,6 @@ static struct css_device_id io_subchannel_ids[] = {
{ .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, },
{ /* end of list */ },
};
-MODULE_DEVICE_TABLE(css, io_subchannel_ids);
static int io_subchannel_prepare(struct subchannel *sch)
{
@@ -2150,7 +2151,6 @@ int ccw_device_siosl(struct ccw_device *cdev)
}
EXPORT_SYMBOL_GPL(ccw_device_siosl);
-MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_online);
EXPORT_SYMBOL(ccw_device_set_offline);
EXPORT_SYMBOL(ccw_driver_register);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 065b1be98e2c..ec497af99dd8 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -13,7 +13,6 @@
*/
enum dev_state {
DEV_STATE_NOT_OPER,
- DEV_STATE_SENSE_PGID,
DEV_STATE_SENSE_ID,
DEV_STATE_OFFLINE,
DEV_STATE_VERIFY,
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 8327d47e08b6..9afb5ce13007 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -1058,12 +1058,6 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_TIMEOUT] = ccw_device_nop,
[DEV_EVENT_VERIFY] = ccw_device_nop,
},
- [DEV_STATE_SENSE_PGID] = {
- [DEV_EVENT_NOTOPER] = ccw_device_request_event,
- [DEV_EVENT_INTERRUPT] = ccw_device_request_event,
- [DEV_EVENT_TIMEOUT] = ccw_device_request_event,
- [DEV_EVENT_VERIFY] = ccw_device_nop,
- },
[DEV_STATE_SENSE_ID] = {
[DEV_EVENT_NOTOPER] = ccw_device_request_event,
[DEV_EVENT_INTERRUPT] = ccw_device_request_event,
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 877d9f601e63..cf8c4ac6323a 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -3,8 +3,10 @@
*
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
+ *
+ * License: GPL
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/slab.h>
@@ -676,7 +678,6 @@ void ccw_device_get_schid(struct ccw_device *cdev, struct subchannel_id *schid)
}
EXPORT_SYMBOL_GPL(ccw_device_get_schid);
-MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_options_mask);
EXPORT_SYMBOL(ccw_device_set_options);
EXPORT_SYMBOL(ccw_device_clear_options);
diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile
index b8ab18676e69..0a7fb83f35e5 100644
--- a/drivers/s390/crypto/Makefile
+++ b/drivers/s390/crypto/Makefile
@@ -2,10 +2,11 @@
# S/390 crypto devices
#
-ap-objs := ap_bus.o
-# zcrypt_api depends on ap
-obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o
-# msgtype* depend on zcrypt_api
-obj-$(CONFIG_ZCRYPT) += zcrypt_msgtype6.o zcrypt_msgtype50.o
-# adapter drivers depend on ap, zcrypt_api and msgtype*
+ap-objs := ap_bus.o ap_card.o ap_queue.o
+obj-$(subst m,y,$(CONFIG_ZCRYPT)) += ap.o
+# zcrypt_api.o and zcrypt_msgtype*.o depend on ap.o
+zcrypt-objs := zcrypt_api.o zcrypt_card.o zcrypt_queue.o
+zcrypt-objs += zcrypt_msgtype6.o zcrypt_msgtype50.o
+obj-$(CONFIG_ZCRYPT) += zcrypt.o
+# adapter drivers depend on ap.o and zcrypt.o
obj-$(CONFIG_ZCRYPT) += zcrypt_pcixcc.o zcrypt_cex2a.o zcrypt_cex4.o
diff --git a/drivers/s390/crypto/ap_asm.h b/drivers/s390/crypto/ap_asm.h
new file mode 100644
index 000000000000..7a630047c372
--- /dev/null
+++ b/drivers/s390/crypto/ap_asm.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright IBM Corp. 2016
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *
+ * Adjunct processor bus inline assemblies.
+ */
+
+#ifndef _AP_ASM_H_
+#define _AP_ASM_H_
+
+#include <asm/isc.h>
+
+/**
+ * ap_intructions_available() - 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;
+}
+
+/**
+ * ap_tapq(): Test adjunct processor queue.
+ * @qid: The AP queue number
+ * @info: Pointer to queue descriptor
+ *
+ * Returns AP queue status structure.
+ */
+static inline struct ap_queue_status ap_tapq(ap_qid_t qid, unsigned long *info)
+{
+ 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");
+ if (info)
+ *info = reg2;
+ return reg1;
+}
+
+/**
+ * ap_pqap_rapq(): Reset adjunct processor queue.
+ * @qid: The AP queue number
+ *
+ * Returns AP queue status structure.
+ */
+static inline struct ap_queue_status ap_rapq(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;
+}
+
+/**
+ * ap_aqic(): Enable interruption for a specific AP.
+ * @qid: The AP queue number
+ * @ind: The notification indicator byte
+ *
+ * Returns AP queue status.
+ */
+static inline struct ap_queue_status ap_aqic(ap_qid_t qid, void *ind)
+{
+ register unsigned long reg0 asm ("0") = qid | (3UL << 24);
+ register unsigned long reg1_in asm ("1") = (8UL << 44) | AP_ISC;
+ register struct ap_queue_status reg1_out asm ("1");
+ register void *reg2 asm ("2") = ind;
+
+ asm volatile(
+ ".long 0xb2af0000" /* PQAP(AQIC) */
+ : "+d" (reg0), "+d" (reg1_in), "=d" (reg1_out), "+d" (reg2)
+ :
+ : "cc");
+ return reg1_out;
+}
+
+/**
+ * ap_qci(): Get AP configuration data
+ *
+ * Returns 0 on success, or -EOPNOTSUPP.
+ */
+static inline int ap_qci(void *config)
+{
+ register unsigned long reg0 asm ("0") = 0x04000000UL;
+ register unsigned long reg1 asm ("1") = -EINVAL;
+ register void *reg2 asm ("2") = (void *) config;
+
+ asm volatile(
+ ".long 0xb2af0000\n" /* PQAP(QCI) */
+ "0: la %1,0\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (reg0), "+d" (reg1), "+d" (reg2)
+ :
+ : "cc", "memory");
+
+ return reg1;
+}
+
+/**
+ * ap_nqap(): 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_nqap(ap_qid_t qid,
+ unsigned long long psmid,
+ void *msg, size_t length)
+{
+ struct msgblock { char _[length]; };
+ 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") = psmid & 0xffffffff;
+
+ asm volatile (
+ "0: .long 0xb2ad0042\n" /* NQAP */
+ " brc 2,0b"
+ : "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3)
+ : "d" (reg4), "d" (reg5), "m" (*(struct msgblock *) msg)
+ : "cc");
+ return reg1;
+}
+
+/**
+ * ap_dqap(): 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_dqap(ap_qid_t qid,
+ unsigned long long *psmid,
+ void *msg, size_t length)
+{
+ struct msgblock { char _[length]; };
+ 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" /* DQAP */
+ " brc 6,0b\n"
+ : "+d" (reg0), "=d" (reg1), "+d" (reg2),
+ "+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7),
+ "=m" (*(struct msgblock *) msg) : : "cc");
+ *psmid = (((unsigned long long) reg6) << 32) + reg7;
+ return reg1;
+}
+
+#endif /* _AP_ASM_H_ */
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index ed92fb09fc8e..5fa699192864 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -46,8 +46,12 @@
#include <linux/ktime.h>
#include <asm/facility.h>
#include <linux/crypto.h>
+#include <linux/mod_devicetable.h>
+#include <linux/debugfs.h>
#include "ap_bus.h"
+#include "ap_asm.h"
+#include "ap_debug.h"
/*
* Module description.
@@ -62,6 +66,7 @@ MODULE_ALIAS_CRYPTO("z90crypt");
* Module parameter
*/
int ap_domain_index = -1; /* Adjunct Processor Domain Index */
+static DEFINE_SPINLOCK(ap_domain_lock);
module_param_named(domain, ap_domain_index, int, S_IRUSR|S_IRGRP);
MODULE_PARM_DESC(domain, "domain index for ap devices");
EXPORT_SYMBOL(ap_domain_index);
@@ -70,13 +75,21 @@ static int ap_thread_flag = 0;
module_param_named(poll_thread, ap_thread_flag, int, S_IRUSR|S_IRGRP);
MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off).");
-static struct device *ap_root_device = NULL;
+static struct device *ap_root_device;
+
+DEFINE_SPINLOCK(ap_list_lock);
+LIST_HEAD(ap_card_list);
+
static struct ap_config_info *ap_configuration;
-static DEFINE_SPINLOCK(ap_device_list_lock);
-static LIST_HEAD(ap_device_list);
static bool initialised;
/*
+ * AP bus related debug feature things.
+ */
+static struct dentry *ap_dbf_root;
+debug_info_t *ap_dbf_info;
+
+/*
* Workqueue timer for bus rescan.
*/
static struct timer_list ap_config_timer;
@@ -89,7 +102,6 @@ static DECLARE_WORK(ap_scan_work, ap_scan_bus);
*/
static void ap_tasklet_fn(unsigned long);
static DECLARE_TASKLET(ap_tasklet, ap_tasklet_fn, 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);
@@ -129,23 +141,17 @@ static inline int ap_using_interrupts(void)
}
/**
- * ap_intructions_available() - Test if AP instructions are available.
+ * ap_airq_ptr() - Get the address of the adapter interrupt indicator
*
- * Returns 0 if the AP instructions are installed.
+ * Returns the address of the local-summary-indicator of the adapter
+ * interrupt handler for AP, or NULL if adapter interrupts are not
+ * available.
*/
-static inline int ap_instructions_available(void)
+void *ap_airq_ptr(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;
+ if (ap_using_interrupts())
+ return ap_airq.lsi_ptr;
+ return NULL;
}
/**
@@ -169,19 +175,6 @@ static int ap_configuration_available(void)
return test_facility(12);
}
-static inline struct ap_queue_status
-__pqap_tapq(ap_qid_t qid, unsigned long *info)
-{
- 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");
- *info = reg2;
- return reg1;
-}
-
/**
* ap_test_queue(): Test adjunct processor queue.
* @qid: The AP queue number
@@ -192,85 +185,16 @@ __pqap_tapq(ap_qid_t qid, unsigned long *info)
static inline struct ap_queue_status
ap_test_queue(ap_qid_t qid, unsigned long *info)
{
- struct ap_queue_status aqs;
- unsigned long _info;
-
if (test_facility(15))
qid |= 1UL << 23; /* set APFT T bit*/
- aqs = __pqap_tapq(qid, &_info);
- if (info)
- *info = _info;
- return aqs;
-}
-
-/**
- * ap_reset_queue(): 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;
-}
-
-/**
- * ap_queue_interruption_control(): Enable interruption for a specific AP.
- * @qid: The AP queue number
- * @ind: The notification indicator byte
- *
- * Returns AP queue status.
- */
-static inline struct ap_queue_status
-ap_queue_interruption_control(ap_qid_t qid, void *ind)
-{
- register unsigned long reg0 asm ("0") = qid | 0x03000000UL;
- register unsigned long reg1_in asm ("1") = 0x0000800000000000UL | AP_ISC;
- register struct ap_queue_status reg1_out asm ("1");
- register void *reg2 asm ("2") = ind;
- asm volatile(
- ".long 0xb2af0000" /* PQAP(AQIC) */
- : "+d" (reg0), "+d" (reg1_in), "=d" (reg1_out), "+d" (reg2)
- :
- : "cc" );
- return reg1_out;
-}
-
-/**
- * ap_query_configuration(): Get AP configuration data
- *
- * Returns 0 on success, or -EOPNOTSUPP.
- */
-static inline int __ap_query_configuration(void)
-{
- register unsigned long reg0 asm ("0") = 0x04000000UL;
- register unsigned long reg1 asm ("1") = -EINVAL;
- register void *reg2 asm ("2") = (void *) ap_configuration;
-
- asm volatile(
- ".long 0xb2af0000\n" /* PQAP(QCI) */
- "0: la %1,0\n"
- "1:\n"
- EX_TABLE(0b, 1b)
- : "+d" (reg0), "+d" (reg1), "+d" (reg2)
- :
- : "cc");
-
- return reg1;
+ return ap_tapq(qid, info);
}
static inline int ap_query_configuration(void)
{
if (!ap_configuration)
return -EOPNOTSUPP;
- return __ap_query_configuration();
+ return ap_qci(ap_configuration);
}
/**
@@ -331,162 +255,6 @@ static inline int ap_test_config_domain(unsigned int domain)
}
/**
- * ap_queue_enable_interruption(): Enable interruption on an AP.
- * @qid: The AP queue number
- * @ind: the notification indicator byte
- *
- * Enables interruption on AP queue via ap_queue_interruption_control(). Based
- * on the return value it waits a while and tests the AP queue if interrupts
- * have been switched on using ap_test_queue().
- */
-static int ap_queue_enable_interruption(struct ap_device *ap_dev, void *ind)
-{
- struct ap_queue_status status;
-
- status = ap_queue_interruption_control(ap_dev->qid, ind);
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- case AP_RESPONSE_OTHERWISE_CHANGED:
- return 0;
- case AP_RESPONSE_Q_NOT_AVAIL:
- case AP_RESPONSE_DECONFIGURED:
- case AP_RESPONSE_CHECKSTOPPED:
- case AP_RESPONSE_INVALID_ADDRESS:
- pr_err("Registering adapter interrupts for AP %d failed\n",
- AP_QID_DEVICE(ap_dev->qid));
- return -EOPNOTSUPP;
- case AP_RESPONSE_RESET_IN_PROGRESS:
- case AP_RESPONSE_BUSY:
- default:
- return -EBUSY;
- }
-}
-
-static inline struct ap_queue_status
-__nqap(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") = psmid & 0xffffffff;
-
- asm volatile (
- "0: .long 0xb2ad0042\n" /* NQAP */
- " brc 2,0b"
- : "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3)
- : "d" (reg4), "d" (reg5), "m" (*(msgblock *) msg)
- : "cc");
- return reg1;
-}
-
-/**
- * __ap_send(): 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
- * @special: Special Bit
- *
- * 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,
- unsigned int special)
-{
- if (special == 1)
- qid |= 0x400000UL;
- return __nqap(qid, psmid, msg, length);
-}
-
-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, 0);
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- return 0;
- case AP_RESPONSE_Q_FULL:
- case AP_RESPONSE_RESET_IN_PROGRESS:
- return -EBUSY;
- case AP_RESPONSE_REQ_FAC_NOT_INST:
- return -EINVAL;
- default: /* Device is gone. */
- return -ENODEV;
- }
-}
-EXPORT_SYMBOL(ap_send);
-
-/**
- * __ap_recv(): 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" /* DQAP */
- " 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;
-
- if (msg == NULL)
- return -EINVAL;
- 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;
- case AP_RESPONSE_RESET_IN_PROGRESS:
- return -EBUSY;
- default:
- return -ENODEV;
- }
-}
-EXPORT_SYMBOL(ap_recv);
-
-/**
* ap_query_queue(): Check if an AP queue is available.
* @qid: The AP queue number
* @queue_depth: Pointer to queue depth value
@@ -500,7 +268,7 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type,
unsigned long info;
int nd;
- if (!ap_test_config_card_id(AP_QID_DEVICE(qid)))
+ if (!ap_test_config_card_id(AP_QID_CARD(qid)))
return -ENODEV;
status = ap_test_queue(qid, &info);
@@ -511,8 +279,28 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type,
*facilities = (unsigned int)(info >> 32);
/* Update maximum domain id */
nd = (info >> 16) & 0xff;
+ /* if N bit is available, z13 and newer */
if ((info & (1UL << 57)) && nd > 0)
ap_max_domain_id = nd;
+ else /* older machine types */
+ ap_max_domain_id = 15;
+ switch (*device_type) {
+ /* For CEX2 and CEX3 the available functions
+ * are not refrected by the facilities bits.
+ * Instead it is coded into the type. So here
+ * modify the function bits based on the type.
+ */
+ case AP_DEVICE_TYPE_CEX2A:
+ case AP_DEVICE_TYPE_CEX3A:
+ *facilities |= 0x08000000;
+ break;
+ case AP_DEVICE_TYPE_CEX2C:
+ case AP_DEVICE_TYPE_CEX3C:
+ *facilities |= 0x10000000;
+ break;
+ default:
+ break;
+ }
return 0;
case AP_RESPONSE_Q_NOT_AVAIL:
case AP_RESPONSE_DECONFIGURED:
@@ -528,9 +316,7 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type,
}
}
-/* State machine definitions and helpers */
-
-static void ap_sm_wait(enum ap_wait wait)
+void ap_wait(enum ap_wait wait)
{
ktime_t hr_time;
@@ -547,7 +333,7 @@ static void ap_sm_wait(enum ap_wait wait)
case AP_WAIT_TIMEOUT:
spin_lock_bh(&ap_poll_timer_lock);
if (!hrtimer_is_queued(&ap_poll_timer)) {
- hr_time = ktime_set(0, poll_timeout);
+ hr_time = poll_timeout;
hrtimer_forward_now(&ap_poll_timer, hr_time);
hrtimer_restart(&ap_poll_timer);
}
@@ -559,350 +345,21 @@ static void ap_sm_wait(enum ap_wait wait)
}
}
-static enum ap_wait ap_sm_nop(struct ap_device *ap_dev)
-{
- return AP_WAIT_NONE;
-}
-
-/**
- * ap_sm_recv(): Receive pending reply messages from an AP device but do
- * not change the state of the device.
- * @ap_dev: pointer to the AP device
- *
- * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
- */
-static struct ap_queue_status ap_sm_recv(struct ap_device *ap_dev)
-{
- struct ap_queue_status status;
- struct ap_message *ap_msg;
-
- 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--;
- if (ap_dev->queue_count > 0)
- mod_timer(&ap_dev->timeout,
- jiffies + ap_dev->drv->request_timeout);
- 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_msg->receive(ap_dev, ap_msg, ap_dev->reply);
- break;
- }
- case AP_RESPONSE_NO_PENDING_REPLY:
- if (!status.queue_empty || ap_dev->queue_count <= 0)
- break;
- /* The card shouldn't forget requests but who knows. */
- atomic_sub(ap_dev->queue_count, &ap_poll_requests);
- 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;
- break;
- default:
- break;
- }
- return status;
-}
-
-/**
- * ap_sm_read(): Receive pending reply messages from an AP device.
- * @ap_dev: pointer to the AP device
- *
- * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
- */
-static enum ap_wait ap_sm_read(struct ap_device *ap_dev)
-{
- struct ap_queue_status status;
-
- if (!ap_dev->reply)
- return AP_WAIT_NONE;
- status = ap_sm_recv(ap_dev);
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- if (ap_dev->queue_count > 0) {
- ap_dev->state = AP_STATE_WORKING;
- return AP_WAIT_AGAIN;
- }
- ap_dev->state = AP_STATE_IDLE;
- return AP_WAIT_NONE;
- case AP_RESPONSE_NO_PENDING_REPLY:
- if (ap_dev->queue_count > 0)
- return AP_WAIT_INTERRUPT;
- ap_dev->state = AP_STATE_IDLE;
- return AP_WAIT_NONE;
- default:
- ap_dev->state = AP_STATE_BORKED;
- return AP_WAIT_NONE;
- }
-}
-
-/**
- * ap_sm_suspend_read(): Receive pending reply messages from an AP device
- * without changing the device state in between. In suspend mode we don't
- * allow sending new requests, therefore just fetch pending replies.
- * @ap_dev: pointer to the AP device
- *
- * Returns AP_WAIT_NONE or AP_WAIT_AGAIN
- */
-static enum ap_wait ap_sm_suspend_read(struct ap_device *ap_dev)
-{
- struct ap_queue_status status;
-
- if (!ap_dev->reply)
- return AP_WAIT_NONE;
- status = ap_sm_recv(ap_dev);
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- if (ap_dev->queue_count > 0)
- return AP_WAIT_AGAIN;
- /* fall through */
- default:
- return AP_WAIT_NONE;
- }
-}
-
-/**
- * ap_sm_write(): Send messages from the request queue to an AP device.
- * @ap_dev: pointer to the AP device
- *
- * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
- */
-static enum ap_wait ap_sm_write(struct ap_device *ap_dev)
-{
- struct ap_queue_status status;
- struct ap_message *ap_msg;
-
- if (ap_dev->requestq_count <= 0)
- return AP_WAIT_NONE;
- /* 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, ap_msg->special);
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- atomic_inc(&ap_poll_requests);
- ap_dev->queue_count++;
- if (ap_dev->queue_count == 1)
- mod_timer(&ap_dev->timeout,
- jiffies + ap_dev->drv->request_timeout);
- 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->state = AP_STATE_WORKING;
- return AP_WAIT_AGAIN;
- }
- /* fall through */
- case AP_RESPONSE_Q_FULL:
- ap_dev->state = AP_STATE_QUEUE_FULL;
- return AP_WAIT_INTERRUPT;
- case AP_RESPONSE_RESET_IN_PROGRESS:
- ap_dev->state = AP_STATE_RESET_WAIT;
- return AP_WAIT_TIMEOUT;
- case AP_RESPONSE_MESSAGE_TOO_BIG:
- case AP_RESPONSE_REQ_FAC_NOT_INST:
- list_del_init(&ap_msg->list);
- ap_dev->requestq_count--;
- ap_msg->rc = -EINVAL;
- ap_msg->receive(ap_dev, ap_msg, NULL);
- return AP_WAIT_AGAIN;
- default:
- ap_dev->state = AP_STATE_BORKED;
- return AP_WAIT_NONE;
- }
-}
-
-/**
- * ap_sm_read_write(): Send and receive messages to/from an AP device.
- * @ap_dev: pointer to the AP device
- *
- * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
- */
-static enum ap_wait ap_sm_read_write(struct ap_device *ap_dev)
-{
- return min(ap_sm_read(ap_dev), ap_sm_write(ap_dev));
-}
-
-/**
- * ap_sm_reset(): Reset an AP queue.
- * @qid: The AP queue number
- *
- * Submit the Reset command to an AP queue.
- */
-static enum ap_wait ap_sm_reset(struct ap_device *ap_dev)
-{
- struct ap_queue_status status;
-
- status = ap_reset_queue(ap_dev->qid);
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- case AP_RESPONSE_RESET_IN_PROGRESS:
- ap_dev->state = AP_STATE_RESET_WAIT;
- ap_dev->interrupt = AP_INTR_DISABLED;
- return AP_WAIT_TIMEOUT;
- case AP_RESPONSE_BUSY:
- return AP_WAIT_TIMEOUT;
- case AP_RESPONSE_Q_NOT_AVAIL:
- case AP_RESPONSE_DECONFIGURED:
- case AP_RESPONSE_CHECKSTOPPED:
- default:
- ap_dev->state = AP_STATE_BORKED;
- return AP_WAIT_NONE;
- }
-}
-
-/**
- * ap_sm_reset_wait(): Test queue for completion of the reset operation
- * @ap_dev: pointer to the AP device
- *
- * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0.
- */
-static enum ap_wait ap_sm_reset_wait(struct ap_device *ap_dev)
-{
- struct ap_queue_status status;
- unsigned long info;
-
- if (ap_dev->queue_count > 0 && ap_dev->reply)
- /* Try to read a completed message and get the status */
- status = ap_sm_recv(ap_dev);
- else
- /* Get the status with TAPQ */
- status = ap_test_queue(ap_dev->qid, &info);
-
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- if (ap_using_interrupts() &&
- ap_queue_enable_interruption(ap_dev,
- ap_airq.lsi_ptr) == 0)
- ap_dev->state = AP_STATE_SETIRQ_WAIT;
- else
- ap_dev->state = (ap_dev->queue_count > 0) ?
- AP_STATE_WORKING : AP_STATE_IDLE;
- return AP_WAIT_AGAIN;
- case AP_RESPONSE_BUSY:
- case AP_RESPONSE_RESET_IN_PROGRESS:
- return AP_WAIT_TIMEOUT;
- case AP_RESPONSE_Q_NOT_AVAIL:
- case AP_RESPONSE_DECONFIGURED:
- case AP_RESPONSE_CHECKSTOPPED:
- default:
- ap_dev->state = AP_STATE_BORKED;
- return AP_WAIT_NONE;
- }
-}
-
-/**
- * ap_sm_setirq_wait(): Test queue for completion of the irq enablement
- * @ap_dev: pointer to the AP device
- *
- * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0.
- */
-static enum ap_wait ap_sm_setirq_wait(struct ap_device *ap_dev)
-{
- struct ap_queue_status status;
- unsigned long info;
-
- if (ap_dev->queue_count > 0 && ap_dev->reply)
- /* Try to read a completed message and get the status */
- status = ap_sm_recv(ap_dev);
- else
- /* Get the status with TAPQ */
- status = ap_test_queue(ap_dev->qid, &info);
-
- if (status.int_enabled == 1) {
- /* Irqs are now enabled */
- ap_dev->interrupt = AP_INTR_ENABLED;
- ap_dev->state = (ap_dev->queue_count > 0) ?
- AP_STATE_WORKING : AP_STATE_IDLE;
- }
-
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- if (ap_dev->queue_count > 0)
- return AP_WAIT_AGAIN;
- /* fallthrough */
- case AP_RESPONSE_NO_PENDING_REPLY:
- return AP_WAIT_TIMEOUT;
- default:
- ap_dev->state = AP_STATE_BORKED;
- return AP_WAIT_NONE;
- }
-}
-
-/*
- * AP state machine jump table
- */
-static ap_func_t *ap_jumptable[NR_AP_STATES][NR_AP_EVENTS] = {
- [AP_STATE_RESET_START] = {
- [AP_EVENT_POLL] = ap_sm_reset,
- [AP_EVENT_TIMEOUT] = ap_sm_nop,
- },
- [AP_STATE_RESET_WAIT] = {
- [AP_EVENT_POLL] = ap_sm_reset_wait,
- [AP_EVENT_TIMEOUT] = ap_sm_nop,
- },
- [AP_STATE_SETIRQ_WAIT] = {
- [AP_EVENT_POLL] = ap_sm_setirq_wait,
- [AP_EVENT_TIMEOUT] = ap_sm_nop,
- },
- [AP_STATE_IDLE] = {
- [AP_EVENT_POLL] = ap_sm_write,
- [AP_EVENT_TIMEOUT] = ap_sm_nop,
- },
- [AP_STATE_WORKING] = {
- [AP_EVENT_POLL] = ap_sm_read_write,
- [AP_EVENT_TIMEOUT] = ap_sm_reset,
- },
- [AP_STATE_QUEUE_FULL] = {
- [AP_EVENT_POLL] = ap_sm_read,
- [AP_EVENT_TIMEOUT] = ap_sm_reset,
- },
- [AP_STATE_SUSPEND_WAIT] = {
- [AP_EVENT_POLL] = ap_sm_suspend_read,
- [AP_EVENT_TIMEOUT] = ap_sm_nop,
- },
- [AP_STATE_BORKED] = {
- [AP_EVENT_POLL] = ap_sm_nop,
- [AP_EVENT_TIMEOUT] = ap_sm_nop,
- },
-};
-
-static inline enum ap_wait ap_sm_event(struct ap_device *ap_dev,
- enum ap_event event)
-{
- return ap_jumptable[ap_dev->state][event](ap_dev);
-}
-
-static inline enum ap_wait ap_sm_event_loop(struct ap_device *ap_dev,
- enum ap_event event)
-{
- enum ap_wait wait;
-
- while ((wait = ap_sm_event(ap_dev, event)) == AP_WAIT_AGAIN)
- ;
- return wait;
-}
-
/**
* ap_request_timeout(): Handling of request timeouts
* @data: Holds the AP device.
*
* Handles request timeouts.
*/
-static void ap_request_timeout(unsigned long data)
+void ap_request_timeout(unsigned long data)
{
- struct ap_device *ap_dev = (struct ap_device *) data;
+ struct ap_queue *aq = (struct ap_queue *) data;
if (ap_suspend_flag)
return;
- spin_lock_bh(&ap_dev->lock);
- ap_sm_wait(ap_sm_event(ap_dev, AP_EVENT_TIMEOUT));
- spin_unlock_bh(&ap_dev->lock);
+ spin_lock_bh(&aq->lock);
+ ap_wait(ap_sm_event(aq, AP_EVENT_TIMEOUT));
+ spin_unlock_bh(&aq->lock);
}
/**
@@ -937,7 +394,8 @@ static void ap_interrupt_handler(struct airq_struct *airq)
*/
static void ap_tasklet_fn(unsigned long dummy)
{
- struct ap_device *ap_dev;
+ struct ap_card *ac;
+ struct ap_queue *aq;
enum ap_wait wait = AP_WAIT_NONE;
/* Reset the indicator if interrupts are used. Thus new interrupts can
@@ -947,14 +405,35 @@ static void ap_tasklet_fn(unsigned long dummy)
if (ap_using_interrupts())
xchg(ap_airq.lsi_ptr, 0);
- spin_lock(&ap_device_list_lock);
- list_for_each_entry(ap_dev, &ap_device_list, list) {
- spin_lock_bh(&ap_dev->lock);
- wait = min(wait, ap_sm_event_loop(ap_dev, AP_EVENT_POLL));
- spin_unlock_bh(&ap_dev->lock);
+ spin_lock_bh(&ap_list_lock);
+ for_each_ap_card(ac) {
+ for_each_ap_queue(aq, ac) {
+ spin_lock_bh(&aq->lock);
+ wait = min(wait, ap_sm_event_loop(aq, AP_EVENT_POLL));
+ spin_unlock_bh(&aq->lock);
+ }
}
- spin_unlock(&ap_device_list_lock);
- ap_sm_wait(wait);
+ spin_unlock_bh(&ap_list_lock);
+
+ ap_wait(wait);
+}
+
+static int ap_pending_requests(void)
+{
+ struct ap_card *ac;
+ struct ap_queue *aq;
+
+ spin_lock_bh(&ap_list_lock);
+ for_each_ap_card(ac) {
+ for_each_ap_queue(aq, ac) {
+ if (aq->queue_count == 0)
+ continue;
+ spin_unlock_bh(&ap_list_lock);
+ return 1;
+ }
+ }
+ spin_unlock_bh(&ap_list_lock);
+ return 0;
}
/**
@@ -976,8 +455,7 @@ static int ap_poll_thread(void *data)
while (!kthread_should_stop()) {
add_wait_queue(&ap_poll_wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- if (ap_suspend_flag ||
- atomic_read(&ap_poll_requests) <= 0) {
+ if (ap_suspend_flag || !ap_pending_requests()) {
schedule();
try_to_freeze();
}
@@ -989,7 +467,8 @@ static int ap_poll_thread(void *data)
continue;
}
ap_tasklet_fn(0);
- } while (!kthread_should_stop());
+ }
+
return 0;
}
@@ -1018,207 +497,8 @@ static void ap_poll_thread_stop(void)
mutex_unlock(&ap_poll_thread_mutex);
}
-/**
- * ap_queue_message(): Queue a request to an AP device.
- * @ap_dev: The AP device to queue the message to
- * @ap_msg: The message that is to be added
- */
-void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
-{
- /* For asynchronous message handling a valid receive-callback
- * is required. */
- BUG_ON(!ap_msg->receive);
-
- spin_lock_bh(&ap_dev->lock);
- /* Queue the message. */
- list_add_tail(&ap_msg->list, &ap_dev->requestq);
- ap_dev->requestq_count++;
- ap_dev->total_request_count++;
- /* Send/receive as many request from the queue as possible. */
- ap_sm_wait(ap_sm_event_loop(ap_dev, AP_EVENT_POLL));
- spin_unlock_bh(&ap_dev->lock);
-}
-EXPORT_SYMBOL(ap_queue_message);
-
-/**
- * ap_cancel_message(): Cancel a crypto request.
- * @ap_dev: The AP device that has the message queued
- * @ap_msg: The message that is to be removed
- *
- * Cancel a crypto request. This is done by removing the request
- * from the device pending or request 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.
- */
-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 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_raw_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->raw_hwtype);
-}
-
-static DEVICE_ATTR(raw_hwtype, 0444, ap_raw_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_requestq_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->requestq_count);
- spin_unlock_bh(&ap_dev->lock);
- return rc;
-}
-
-static DEVICE_ATTR(requestq_count, 0444, ap_requestq_count_show, NULL);
-
-static ssize_t ap_pendingq_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->pendingq_count);
- spin_unlock_bh(&ap_dev->lock);
- return rc;
-}
-
-static DEVICE_ATTR(pendingq_count, 0444, ap_pendingq_count_show, NULL);
-
-static ssize_t ap_reset_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct ap_device *ap_dev = to_ap_dev(dev);
- int rc = 0;
-
- spin_lock_bh(&ap_dev->lock);
- switch (ap_dev->state) {
- case AP_STATE_RESET_START:
- case AP_STATE_RESET_WAIT:
- rc = snprintf(buf, PAGE_SIZE, "Reset in progress.\n");
- break;
- case AP_STATE_WORKING:
- case AP_STATE_QUEUE_FULL:
- rc = snprintf(buf, PAGE_SIZE, "Reset Timer armed.\n");
- break;
- default:
- rc = snprintf(buf, PAGE_SIZE, "No Reset Timer set.\n");
- }
- spin_unlock_bh(&ap_dev->lock);
- return rc;
-}
-
-static DEVICE_ATTR(reset, 0444, ap_reset_show, NULL);
-
-static ssize_t ap_interrupt_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct ap_device *ap_dev = to_ap_dev(dev);
- int rc = 0;
-
- spin_lock_bh(&ap_dev->lock);
- if (ap_dev->state == AP_STATE_SETIRQ_WAIT)
- rc = snprintf(buf, PAGE_SIZE, "Enable Interrupt pending.\n");
- else if (ap_dev->interrupt == AP_INTR_ENABLED)
- rc = snprintf(buf, PAGE_SIZE, "Interrupts enabled.\n");
- else
- rc = snprintf(buf, PAGE_SIZE, "Interrupts disabled.\n");
- spin_unlock_bh(&ap_dev->lock);
- return rc;
-}
-
-static DEVICE_ATTR(interrupt, 0444, ap_interrupt_show, NULL);
-
-static ssize_t ap_modalias_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "ap:t%02X\n", to_ap_dev(dev)->device_type);
-}
-
-static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL);
-
-static ssize_t ap_functions_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct ap_device *ap_dev = to_ap_dev(dev);
- return snprintf(buf, PAGE_SIZE, "0x%08X\n", ap_dev->functions);
-}
-
-static DEVICE_ATTR(ap_functions, 0444, ap_functions_show, NULL);
-
-static struct attribute *ap_dev_attrs[] = {
- &dev_attr_hwtype.attr,
- &dev_attr_raw_hwtype.attr,
- &dev_attr_depth.attr,
- &dev_attr_request_count.attr,
- &dev_attr_requestq_count.attr,
- &dev_attr_pendingq_count.attr,
- &dev_attr_reset.attr,
- &dev_attr_interrupt.attr,
- &dev_attr_modalias.attr,
- &dev_attr_ap_functions.attr,
- NULL
-};
-static struct attribute_group ap_dev_attr_group = {
- .attrs = ap_dev_attrs
-};
+#define is_card_dev(x) ((x)->parent == ap_root_device)
+#define is_queue_dev(x) ((x)->parent != ap_root_device)
/**
* ap_bus_match()
@@ -1229,7 +509,6 @@ static struct attribute_group ap_dev_attr_group = {
*/
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;
@@ -1238,10 +517,14 @@ static int ap_bus_match(struct device *dev, struct device_driver *drv)
* 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;
+ if (is_card_dev(dev) &&
+ id->match_flags & AP_DEVICE_ID_MATCH_CARD_TYPE &&
+ id->dev_type == to_ap_dev(dev)->device_type)
+ return 1;
+ if (is_queue_dev(dev) &&
+ id->match_flags & AP_DEVICE_ID_MATCH_QUEUE_TYPE &&
+ id->dev_type == to_ap_dev(dev)->device_type)
+ return 1;
}
return 0;
}
@@ -1273,27 +556,28 @@ static int ap_uevent (struct device *dev, struct kobj_uevent_env *env)
return retval;
}
-static int ap_dev_suspend(struct device *dev, pm_message_t state)
+static int ap_dev_suspend(struct device *dev)
{
struct ap_device *ap_dev = to_ap_dev(dev);
- /* Poll on the device until all requests are finished. */
- spin_lock_bh(&ap_dev->lock);
- ap_dev->state = AP_STATE_SUSPEND_WAIT;
- while (ap_sm_event(ap_dev, AP_EVENT_POLL) != AP_WAIT_NONE)
- ;
- ap_dev->state = AP_STATE_BORKED;
- spin_unlock_bh(&ap_dev->lock);
+ if (ap_dev->drv && ap_dev->drv->suspend)
+ ap_dev->drv->suspend(ap_dev);
return 0;
}
static int ap_dev_resume(struct device *dev)
{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+
+ if (ap_dev->drv && ap_dev->drv->resume)
+ ap_dev->drv->resume(ap_dev);
return 0;
}
static void ap_bus_suspend(void)
{
+ AP_DBF(DBF_DEBUG, "ap_bus_suspend running\n");
+
ap_suspend_flag = 1;
/*
* Disable scanning for devices, thus we do not want to scan
@@ -1303,9 +587,25 @@ static void ap_bus_suspend(void)
tasklet_disable(&ap_tasklet);
}
-static int __ap_devices_unregister(struct device *dev, void *dummy)
+static int __ap_card_devices_unregister(struct device *dev, void *dummy)
+{
+ if (is_card_dev(dev))
+ device_unregister(dev);
+ return 0;
+}
+
+static int __ap_queue_devices_unregister(struct device *dev, void *dummy)
+{
+ if (is_queue_dev(dev))
+ device_unregister(dev);
+ return 0;
+}
+
+static int __ap_queue_devices_with_id_unregister(struct device *dev, void *data)
{
- device_unregister(dev);
+ if (is_queue_dev(dev) &&
+ AP_QID_CARD(to_ap_queue(dev)->qid) == (int)(long) data)
+ device_unregister(dev);
return 0;
}
@@ -1313,8 +613,15 @@ static void ap_bus_resume(void)
{
int rc;
- /* Unconditionally remove all AP devices */
- bus_for_each_dev(&ap_bus_type, NULL, NULL, __ap_devices_unregister);
+ AP_DBF(DBF_DEBUG, "ap_bus_resume running\n");
+
+ /* remove all queue devices */
+ bus_for_each_dev(&ap_bus_type, NULL, NULL,
+ __ap_queue_devices_unregister);
+ /* remove all card devices */
+ bus_for_each_dev(&ap_bus_type, NULL, NULL,
+ __ap_card_devices_unregister);
+
/* Reset thin interrupt setting */
if (ap_interrupts_available() && !ap_using_interrupts()) {
rc = register_adapter_interrupt(&ap_airq);
@@ -1356,25 +663,15 @@ static struct notifier_block ap_power_notifier = {
.notifier_call = ap_power_event,
};
+static SIMPLE_DEV_PM_OPS(ap_bus_pm_ops, ap_dev_suspend, ap_dev_resume);
+
static struct bus_type ap_bus_type = {
.name = "ap",
.match = &ap_bus_match,
.uevent = &ap_uevent,
- .suspend = ap_dev_suspend,
- .resume = ap_dev_resume,
+ .pm = &ap_bus_pm_ops,
};
-void ap_device_init_reply(struct ap_device *ap_dev,
- struct ap_message *reply)
-{
- ap_dev->reply = reply;
-
- spin_lock_bh(&ap_dev->lock);
- ap_sm_wait(ap_sm_event(ap_dev, AP_EVENT_POLL));
- spin_unlock_bh(&ap_dev->lock);
-}
-EXPORT_SYMBOL(ap_device_init_reply);
-
static int ap_device_probe(struct device *dev)
{
struct ap_device *ap_dev = to_ap_dev(dev);
@@ -1388,61 +685,22 @@ static int ap_device_probe(struct device *dev)
return rc;
}
-/**
- * __ap_flush_queue(): Flush requests.
- * @ap_dev: Pointer to the AP device
- *
- * Flush all requests from the request/pending queue of an AP device.
- */
-static 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_msg->rc = -EAGAIN;
- ap_msg->receive(ap_dev, ap_msg, NULL);
- }
- list_for_each_entry_safe(ap_msg, next, &ap_dev->requestq, list) {
- list_del_init(&ap_msg->list);
- ap_dev->requestq_count--;
- ap_msg->rc = -EAGAIN;
- ap_msg->receive(ap_dev, ap_msg, NULL);
- }
-}
-
-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;
- ap_flush_queue(ap_dev);
- del_timer_sync(&ap_dev->timeout);
- spin_lock_bh(&ap_device_list_lock);
- list_del_init(&ap_dev->list);
- spin_unlock_bh(&ap_device_list_lock);
+ spin_lock_bh(&ap_list_lock);
+ if (is_card_dev(dev))
+ list_del_init(&to_ap_card(dev)->list);
+ else
+ list_del_init(&to_ap_queue(dev)->list);
+ spin_unlock_bh(&ap_list_lock);
if (ap_drv->remove)
ap_drv->remove(ap_dev);
- spin_lock_bh(&ap_dev->lock);
- atomic_sub(ap_dev->queue_count, &ap_poll_requests);
- spin_unlock_bh(&ap_dev->lock);
return 0;
}
-static void ap_device_release(struct device *dev)
-{
- kfree(to_ap_dev(dev));
-}
-
int ap_driver_register(struct ap_driver *ap_drv, struct module *owner,
char *name)
{
@@ -1485,18 +743,30 @@ 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_domain_store(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ int domain;
+
+ if (sscanf(buf, "%i\n", &domain) != 1 ||
+ domain < 0 || domain > ap_max_domain_id)
+ return -EINVAL;
+ spin_lock_bh(&ap_domain_lock);
+ ap_domain_index = domain;
+ spin_unlock_bh(&ap_domain_lock);
+
+ AP_DBF(DBF_DEBUG, "store new default domain=%d\n", domain);
+
+ return count;
+}
+
+static BUS_ATTR(ap_domain, 0644, ap_domain_show, ap_domain_store);
static ssize_t ap_control_domain_mask_show(struct bus_type *bus, char *buf)
{
if (!ap_configuration) /* QCI not supported */
return snprintf(buf, PAGE_SIZE, "not supported\n");
- if (!test_facility(76))
- /* format 0 - 16 bit domain field */
- return snprintf(buf, PAGE_SIZE, "%08x%08x\n",
- ap_configuration->adm[0],
- ap_configuration->adm[1]);
- /* format 1 - 256 bit domain field */
+
return snprintf(buf, PAGE_SIZE,
"0x%08x%08x%08x%08x%08x%08x%08x%08x\n",
ap_configuration->adm[0], ap_configuration->adm[1],
@@ -1508,6 +778,22 @@ static ssize_t ap_control_domain_mask_show(struct bus_type *bus, char *buf)
static BUS_ATTR(ap_control_domain_mask, 0444,
ap_control_domain_mask_show, NULL);
+static ssize_t ap_usage_domain_mask_show(struct bus_type *bus, char *buf)
+{
+ if (!ap_configuration) /* QCI not supported */
+ return snprintf(buf, PAGE_SIZE, "not supported\n");
+
+ return snprintf(buf, PAGE_SIZE,
+ "0x%08x%08x%08x%08x%08x%08x%08x%08x\n",
+ ap_configuration->aqm[0], ap_configuration->aqm[1],
+ ap_configuration->aqm[2], ap_configuration->aqm[3],
+ ap_configuration->aqm[4], ap_configuration->aqm[5],
+ ap_configuration->aqm[6], ap_configuration->aqm[7]);
+}
+
+static BUS_ATTR(ap_usage_domain_mask, 0444,
+ ap_usage_domain_mask_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);
@@ -1574,7 +860,7 @@ static ssize_t poll_timeout_store(struct bus_type *bus, const char *buf,
time > 120000000000ULL)
return -EINVAL;
poll_timeout = time;
- hr_time = ktime_set(0, poll_timeout);
+ hr_time = poll_timeout;
spin_lock_bh(&ap_poll_timer_lock);
hrtimer_cancel(&ap_poll_timer);
@@ -1603,6 +889,7 @@ static BUS_ATTR(ap_max_domain_id, 0444, ap_max_domain_id_show, NULL);
static struct bus_attribute *const ap_bus_attrs[] = {
&bus_attr_ap_domain,
&bus_attr_ap_control_domain_mask,
+ &bus_attr_ap_usage_domain_mask,
&bus_attr_config_time,
&bus_attr_poll_thread,
&bus_attr_ap_interrupts,
@@ -1627,9 +914,12 @@ static int ap_select_domain(void)
* the "domain=" parameter or the domain with the maximum number
* of devices.
*/
- if (ap_domain_index >= 0)
+ spin_lock_bh(&ap_domain_lock);
+ if (ap_domain_index >= 0) {
/* Domain has already been selected. */
+ spin_unlock_bh(&ap_domain_lock);
return 0;
+ }
best_domain = -1;
max_count = 0;
for (i = 0; i < AP_DOMAINS; i++) {
@@ -1651,109 +941,171 @@ static int ap_select_domain(void)
}
if (best_domain >= 0){
ap_domain_index = best_domain;
+ spin_unlock_bh(&ap_domain_lock);
return 0;
}
+ spin_unlock_bh(&ap_domain_lock);
return -ENODEV;
}
-/**
- * __ap_scan_bus(): Scan the AP bus.
- * @dev: Pointer to device
- * @data: Pointer to data
- *
- * Scan the AP bus for new devices.
+/*
+ * helper function to be used with bus_find_dev
+ * matches for the card device with the given id
*/
-static int __ap_scan_bus(struct device *dev, void *data)
+static int __match_card_device_with_id(struct device *dev, void *data)
{
- return to_ap_dev(dev)->qid == (ap_qid_t)(unsigned long) data;
+ return is_card_dev(dev) && to_ap_card(dev)->id == (int)(long) data;
}
+/* helper function to be used with bus_find_dev
+ * matches for the queue device with a given qid
+ */
+static int __match_queue_device_with_qid(struct device *dev, void *data)
+{
+ return is_queue_dev(dev) && to_ap_queue(dev)->qid == (int)(long) data;
+}
+
+/**
+ * ap_scan_bus(): Scan the AP bus for new devices
+ * Runs periodically, workqueue timer (ap_config_time)
+ */
static void ap_scan_bus(struct work_struct *unused)
{
- struct ap_device *ap_dev;
+ struct ap_queue *aq;
+ struct ap_card *ac;
struct device *dev;
ap_qid_t qid;
- int queue_depth = 0, device_type = 0;
- unsigned int device_functions = 0;
- int rc, i, borked;
+ int depth = 0, type = 0;
+ unsigned int functions = 0;
+ int rc, id, dom, borked, domains;
+
+ AP_DBF(DBF_DEBUG, "ap_scan_bus running\n");
ap_query_configuration();
if (ap_select_domain() != 0)
goto out;
- for (i = 0; i < AP_DEVICES; i++) {
- qid = AP_MKQID(i, ap_domain_index);
+ for (id = 0; id < AP_DEVICES; id++) {
+ /* check if device is registered */
dev = bus_find_device(&ap_bus_type, NULL,
- (void *)(unsigned long)qid,
- __ap_scan_bus);
- rc = ap_query_queue(qid, &queue_depth, &device_type,
- &device_functions);
- if (dev) {
- ap_dev = to_ap_dev(dev);
- spin_lock_bh(&ap_dev->lock);
- if (rc == -ENODEV)
- ap_dev->state = AP_STATE_BORKED;
- borked = ap_dev->state == AP_STATE_BORKED;
- spin_unlock_bh(&ap_dev->lock);
- if (borked) /* Remove broken device */
+ (void *)(long) id,
+ __match_card_device_with_id);
+ ac = dev ? to_ap_card(dev) : NULL;
+ if (!ap_test_config_card_id(id)) {
+ if (dev) {
+ /* Card device has been removed from
+ * configuration, remove the belonging
+ * queue devices.
+ */
+ bus_for_each_dev(&ap_bus_type, NULL,
+ (void *)(long) id,
+ __ap_queue_devices_with_id_unregister);
+ /* now remove the card device */
device_unregister(dev);
- put_device(dev);
- if (!borked)
- continue;
- }
- if (rc)
- continue;
- ap_dev = kzalloc(sizeof(*ap_dev), GFP_KERNEL);
- if (!ap_dev)
- break;
- ap_dev->qid = qid;
- ap_dev->state = AP_STATE_RESET_START;
- ap_dev->interrupt = AP_INTR_DISABLED;
- ap_dev->queue_depth = queue_depth;
- ap_dev->raw_hwtype = device_type;
- ap_dev->device_type = device_type;
- ap_dev->functions = device_functions;
- spin_lock_init(&ap_dev->lock);
- INIT_LIST_HEAD(&ap_dev->pendingq);
- INIT_LIST_HEAD(&ap_dev->requestq);
- INIT_LIST_HEAD(&ap_dev->list);
- setup_timer(&ap_dev->timeout, ap_request_timeout,
- (unsigned long) ap_dev);
-
- ap_dev->device.bus = &ap_bus_type;
- ap_dev->device.parent = ap_root_device;
- rc = dev_set_name(&ap_dev->device, "card%02x",
- AP_QID_DEVICE(ap_dev->qid));
- if (rc) {
- kfree(ap_dev);
- continue;
- }
- /* Add to list of devices */
- spin_lock_bh(&ap_device_list_lock);
- list_add(&ap_dev->list, &ap_device_list);
- spin_unlock_bh(&ap_device_list_lock);
- /* Start with a device reset */
- spin_lock_bh(&ap_dev->lock);
- ap_sm_wait(ap_sm_event(ap_dev, AP_EVENT_POLL));
- spin_unlock_bh(&ap_dev->lock);
- /* Register device */
- ap_dev->device.release = ap_device_release;
- rc = device_register(&ap_dev->device);
- if (rc) {
- spin_lock_bh(&ap_dev->lock);
- list_del_init(&ap_dev->list);
- spin_unlock_bh(&ap_dev->lock);
- put_device(&ap_dev->device);
+ put_device(dev);
+ }
continue;
}
- /* Add device attributes. */
- rc = sysfs_create_group(&ap_dev->device.kobj,
- &ap_dev_attr_group);
- if (rc) {
- device_unregister(&ap_dev->device);
- continue;
+ /* According to the configuration there should be a card
+ * device, so check if there is at least one valid queue
+ * and maybe create queue devices and the card device.
+ */
+ domains = 0;
+ for (dom = 0; dom < AP_DOMAINS; dom++) {
+ qid = AP_MKQID(id, dom);
+ dev = bus_find_device(&ap_bus_type, NULL,
+ (void *)(long) qid,
+ __match_queue_device_with_qid);
+ aq = dev ? to_ap_queue(dev) : NULL;
+ if (!ap_test_config_domain(dom)) {
+ if (dev) {
+ /* Queue device exists but has been
+ * removed from configuration.
+ */
+ device_unregister(dev);
+ put_device(dev);
+ }
+ continue;
+ }
+ rc = ap_query_queue(qid, &depth, &type, &functions);
+ if (dev) {
+ spin_lock_bh(&aq->lock);
+ if (rc == -ENODEV ||
+ /* adapter reconfiguration */
+ (ac && ac->functions != functions))
+ aq->state = AP_STATE_BORKED;
+ borked = aq->state == AP_STATE_BORKED;
+ spin_unlock_bh(&aq->lock);
+ if (borked) /* Remove broken device */
+ device_unregister(dev);
+ put_device(dev);
+ if (!borked) {
+ domains++;
+ continue;
+ }
+ }
+ if (rc)
+ continue;
+ /* new queue device needed */
+ if (!ac) {
+ /* but first create the card device */
+ ac = ap_card_create(id, depth,
+ type, functions);
+ if (!ac)
+ continue;
+ ac->ap_dev.device.bus = &ap_bus_type;
+ ac->ap_dev.device.parent = ap_root_device;
+ dev_set_name(&ac->ap_dev.device,
+ "card%02x", id);
+ /* Register card with AP bus */
+ rc = device_register(&ac->ap_dev.device);
+ if (rc) {
+ put_device(&ac->ap_dev.device);
+ ac = NULL;
+ break;
+ }
+ /* get it and thus adjust reference counter */
+ get_device(&ac->ap_dev.device);
+ /* Add card device to card list */
+ spin_lock_bh(&ap_list_lock);
+ list_add(&ac->list, &ap_card_list);
+ spin_unlock_bh(&ap_list_lock);
+ }
+ /* now create the new queue device */
+ aq = ap_queue_create(qid, type);
+ if (!aq)
+ continue;
+ aq->card = ac;
+ aq->ap_dev.device.bus = &ap_bus_type;
+ aq->ap_dev.device.parent = &ac->ap_dev.device;
+ dev_set_name(&aq->ap_dev.device,
+ "%02x.%04x", id, dom);
+ /* Add queue device to card queue list */
+ spin_lock_bh(&ap_list_lock);
+ list_add(&aq->list, &ac->queues);
+ spin_unlock_bh(&ap_list_lock);
+ /* Start with a device reset */
+ spin_lock_bh(&aq->lock);
+ ap_wait(ap_sm_event(aq, AP_EVENT_POLL));
+ spin_unlock_bh(&aq->lock);
+ /* Register device */
+ rc = device_register(&aq->ap_dev.device);
+ if (rc) {
+ spin_lock_bh(&ap_list_lock);
+ list_del_init(&aq->list);
+ spin_unlock_bh(&ap_list_lock);
+ put_device(&aq->ap_dev.device);
+ continue;
+ }
+ domains++;
+ } /* end domain loop */
+ if (ac) {
+ /* remove card dev if there are no queue devices */
+ if (!domains)
+ device_unregister(&ac->ap_dev.device);
+ put_device(&ac->ap_dev.device);
}
- }
+ } /* end device loop */
out:
mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
}
@@ -1772,7 +1124,7 @@ static void ap_reset_domain(void)
if (ap_domain_index == -1 || !ap_test_config_domain(ap_domain_index))
return;
for (i = 0; i < AP_DEVICES; i++)
- ap_reset_queue(AP_MKQID(i, ap_domain_index));
+ ap_rapq(AP_MKQID(i, ap_domain_index));
}
static void ap_reset_all(void)
@@ -1785,7 +1137,7 @@ static void ap_reset_all(void)
for (j = 0; j < AP_DEVICES; j++) {
if (!ap_test_config_card_id(j))
continue;
- ap_reset_queue(AP_MKQID(j, i));
+ ap_rapq(AP_MKQID(j, i));
}
}
}
@@ -1794,6 +1146,23 @@ static struct reset_call ap_reset_call = {
.fn = ap_reset_all,
};
+int __init ap_debug_init(void)
+{
+ ap_dbf_root = debugfs_create_dir("ap", NULL);
+ ap_dbf_info = debug_register("ap", 1, 1,
+ DBF_MAX_SPRINTF_ARGS * sizeof(long));
+ debug_register_view(ap_dbf_info, &debug_sprintf_view);
+ debug_set_level(ap_dbf_info, DBF_ERR);
+
+ return 0;
+}
+
+void ap_debug_exit(void)
+{
+ debugfs_remove(ap_dbf_root);
+ debug_unregister(ap_dbf_info);
+}
+
/**
* ap_module_init(): The module initialization code.
*
@@ -1804,6 +1173,10 @@ int __init ap_module_init(void)
int max_domain_id;
int rc, i;
+ rc = ap_debug_init();
+ if (rc)
+ return rc;
+
if (ap_instructions_available() != 0) {
pr_warn("The hardware system does not support AP instructions\n");
return -ENODEV;
@@ -1913,7 +1286,15 @@ void ap_module_exit(void)
del_timer_sync(&ap_config_timer);
hrtimer_cancel(&ap_poll_timer);
tasklet_kill(&ap_tasklet);
- bus_for_each_dev(&ap_bus_type, NULL, NULL, __ap_devices_unregister);
+
+ /* first remove queue devices */
+ bus_for_each_dev(&ap_bus_type, NULL, NULL,
+ __ap_queue_devices_unregister);
+ /* now remove the card devices */
+ bus_for_each_dev(&ap_bus_type, NULL, NULL,
+ __ap_card_devices_unregister);
+
+ /* remove bus attributes */
for (i = 0; ap_bus_attrs[i]; i++)
bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
unregister_pm_notifier(&ap_power_notifier);
@@ -1923,6 +1304,8 @@ void ap_module_exit(void)
unregister_reset_call(&ap_reset_call);
if (ap_using_interrupts())
unregister_adapter_interrupt(&ap_airq);
+
+ ap_debug_exit();
}
module_init(ap_module_init);
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index d7fdf5c024d7..4dc7c88fb054 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -27,7 +27,6 @@
#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. */
@@ -38,14 +37,17 @@
extern int ap_domain_index;
+extern spinlock_t ap_list_lock;
+extern struct list_head ap_card_list;
+
/**
* The ap_qid_t identifier of an ap queue. It contains a
- * 6 bit device index and a 4 bit queue index (domain).
+ * 6 bit card index and a 4 bit queue index (domain).
*/
typedef unsigned int ap_qid_t;
-#define AP_MKQID(_device, _queue) (((_device) & 63) << 8 | ((_queue) & 255))
-#define AP_QID_DEVICE(_qid) (((_qid) >> 8) & 63)
+#define AP_MKQID(_card, _queue) (((_card) & 63) << 8 | ((_queue) & 255))
+#define AP_QID_CARD(_qid) (((_qid) >> 8) & 63)
#define AP_QID_QUEUE(_qid) ((_qid) & 255)
/**
@@ -55,7 +57,7 @@ typedef unsigned int ap_qid_t;
* @queue_full: Is 1 if the queue is full
* @pad: A 4 bit pad
* @int_enabled: Shows if interrupts are enabled for the AP
- * @response_conde: Holds the 8 bit response code
+ * @response_code: Holds the 8 bit response code
* @pad2: A 16 bit pad
*
* The ap queue status word is returned by all three AP functions
@@ -105,6 +107,7 @@ static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
#define AP_DEVICE_TYPE_CEX3C 9
#define AP_DEVICE_TYPE_CEX4 10
#define AP_DEVICE_TYPE_CEX5 11
+#define AP_DEVICE_TYPE_CEX6 12
/*
* Known function facilities
@@ -166,7 +169,8 @@ struct ap_driver {
int (*probe)(struct ap_device *);
void (*remove)(struct ap_device *);
- int request_timeout; /* request timeout in jiffies */
+ void (*suspend)(struct ap_device *);
+ void (*resume)(struct ap_device *);
};
#define to_ap_drv(x) container_of((x), struct ap_driver, driver)
@@ -174,38 +178,51 @@ struct ap_driver {
int ap_driver_register(struct ap_driver *, struct module *, char *);
void ap_driver_unregister(struct ap_driver *);
-typedef enum ap_wait (ap_func_t)(struct ap_device *ap_dev);
-
struct ap_device {
struct device device;
struct ap_driver *drv; /* Pointer to AP device driver. */
- spinlock_t lock; /* Per device lock. */
- struct list_head list; /* private list of all AP devices. */
+ int device_type; /* AP device type. */
+};
- enum ap_state state; /* State of the AP device. */
+#define to_ap_dev(x) container_of((x), struct ap_device, device)
- ap_qid_t qid; /* AP queue id. */
- int queue_depth; /* AP queue depth.*/
- int device_type; /* AP device type. */
+struct ap_card {
+ struct ap_device ap_dev;
+ struct list_head list; /* Private list of AP cards. */
+ struct list_head queues; /* List of assoc. AP queues */
+ void *private; /* ap driver private pointer. */
int raw_hwtype; /* AP raw hardware type. */
unsigned int functions; /* AP device function bitfield. */
- struct timer_list timeout; /* Timer for request timeouts. */
+ int queue_depth; /* AP queue depth.*/
+ int id; /* AP card number. */
+ atomic_t total_request_count; /* # requests ever for this AP device.*/
+};
+
+#define to_ap_card(x) container_of((x), struct ap_card, ap_dev.device)
+struct ap_queue {
+ struct ap_device ap_dev;
+ struct list_head list; /* Private list of AP queues. */
+ struct ap_card *card; /* Ptr to assoc. AP card. */
+ spinlock_t lock; /* Per device lock. */
+ void *private; /* ap driver private pointer. */
+ ap_qid_t qid; /* AP queue id. */
int interrupt; /* indicate if interrupts are enabled */
int queue_count; /* # messages currently on AP queue. */
-
- struct list_head pendingq; /* List of message sent to AP queue. */
+ enum ap_state state; /* State of the AP device. */
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. */
-
+ int total_request_count; /* # requests ever for this AP device.*/
+ int request_timeout; /* Request timout in jiffies. */
+ struct timer_list timeout; /* Timer for request timeouts. */
+ struct list_head pendingq; /* List of message sent to AP queue. */
+ struct list_head requestq; /* List of message yet to be sent. */
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)
+#define to_ap_queue(x) container_of((x), struct ap_queue, ap_dev.device)
+
+typedef enum ap_wait (ap_func_t)(struct ap_queue *queue);
struct ap_message {
struct list_head list; /* Request queueing. */
@@ -217,7 +234,7 @@ struct ap_message {
void *private; /* ap driver private pointer. */
unsigned int special:1; /* Used for special commands. */
/* receive is called from tasklet context */
- void (*receive)(struct ap_device *, struct ap_message *,
+ void (*receive)(struct ap_queue *, struct ap_message *,
struct ap_message *);
};
@@ -232,10 +249,6 @@ struct ap_config_info {
unsigned char reserved4[16];
} __packed;
-#define AP_DEVICE(dt) \
- .dev_type=(dt), \
- .match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE,
-
/**
* ap_init_message() - Initialize ap_message.
* Initialize a message before using. Otherwise this might result in
@@ -250,6 +263,12 @@ static inline void ap_init_message(struct ap_message *ap_msg)
ap_msg->receive = NULL;
}
+#define for_each_ap_card(_ac) \
+ list_for_each_entry(_ac, &ap_card_list, list)
+
+#define for_each_ap_queue(_aq, _ac) \
+ list_for_each_entry(_aq, &(_ac)->queues, list)
+
/*
* Note: don't use ap_send/ap_recv after using ap_queue_message
* for the first time. Otherwise the ap message queue will get
@@ -258,11 +277,26 @@ static inline void ap_init_message(struct ap_message *ap_msg)
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);
+enum ap_wait ap_sm_event(struct ap_queue *aq, enum ap_event event);
+enum ap_wait ap_sm_event_loop(struct ap_queue *aq, enum ap_event event);
+
+void ap_queue_message(struct ap_queue *aq, struct ap_message *ap_msg);
+void ap_cancel_message(struct ap_queue *aq, struct ap_message *ap_msg);
+void ap_flush_queue(struct ap_queue *aq);
+
+void *ap_airq_ptr(void);
+void ap_wait(enum ap_wait wait);
+void ap_request_timeout(unsigned long data);
void ap_bus_force_rescan(void);
-void ap_device_init_reply(struct ap_device *ap_dev, struct ap_message *ap_msg);
+
+void ap_queue_init_reply(struct ap_queue *aq, struct ap_message *ap_msg);
+struct ap_queue *ap_queue_create(ap_qid_t qid, int device_type);
+void ap_queue_remove(struct ap_queue *aq);
+void ap_queue_suspend(struct ap_device *ap_dev);
+void ap_queue_resume(struct ap_device *ap_dev);
+
+struct ap_card *ap_card_create(int id, int queue_depth, int device_type,
+ unsigned int device_functions);
int ap_module_init(void);
void ap_module_exit(void);
diff --git a/drivers/s390/crypto/ap_card.c b/drivers/s390/crypto/ap_card.c
new file mode 100644
index 000000000000..0110d44172a3
--- /dev/null
+++ b/drivers/s390/crypto/ap_card.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright IBM Corp. 2016
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *
+ * Adjunct processor bus, card related code.
+ */
+
+#define KMSG_COMPONENT "ap"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/facility.h>
+
+#include "ap_bus.h"
+#include "ap_asm.h"
+
+/*
+ * AP card related attributes.
+ */
+static ssize_t ap_hwtype_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_card *ac = to_ap_card(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ac->ap_dev.device_type);
+}
+
+static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL);
+
+static ssize_t ap_raw_hwtype_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_card *ac = to_ap_card(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ac->raw_hwtype);
+}
+
+static DEVICE_ATTR(raw_hwtype, 0444, ap_raw_hwtype_show, NULL);
+
+static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ap_card *ac = to_ap_card(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ac->queue_depth);
+}
+
+static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL);
+
+static ssize_t ap_functions_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_card *ac = to_ap_card(dev);
+
+ return snprintf(buf, PAGE_SIZE, "0x%08X\n", ac->functions);
+}
+
+static DEVICE_ATTR(ap_functions, 0444, ap_functions_show, NULL);
+
+static ssize_t ap_request_count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ap_card *ac = to_ap_card(dev);
+ unsigned int req_cnt;
+
+ req_cnt = 0;
+ spin_lock_bh(&ap_list_lock);
+ req_cnt = atomic_read(&ac->total_request_count);
+ spin_unlock_bh(&ap_list_lock);
+ return snprintf(buf, PAGE_SIZE, "%d\n", req_cnt);
+}
+
+static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL);
+
+static ssize_t ap_requestq_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_card *ac = to_ap_card(dev);
+ struct ap_queue *aq;
+ unsigned int reqq_cnt;
+
+ reqq_cnt = 0;
+ spin_lock_bh(&ap_list_lock);
+ for_each_ap_queue(aq, ac)
+ reqq_cnt += aq->requestq_count;
+ spin_unlock_bh(&ap_list_lock);
+ return snprintf(buf, PAGE_SIZE, "%d\n", reqq_cnt);
+}
+
+static DEVICE_ATTR(requestq_count, 0444, ap_requestq_count_show, NULL);
+
+static ssize_t ap_pendingq_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_card *ac = to_ap_card(dev);
+ struct ap_queue *aq;
+ unsigned int penq_cnt;
+
+ penq_cnt = 0;
+ spin_lock_bh(&ap_list_lock);
+ for_each_ap_queue(aq, ac)
+ penq_cnt += aq->pendingq_count;
+ spin_unlock_bh(&ap_list_lock);
+ return snprintf(buf, PAGE_SIZE, "%d\n", penq_cnt);
+}
+
+static DEVICE_ATTR(pendingq_count, 0444, ap_pendingq_count_show, NULL);
+
+static ssize_t ap_modalias_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "ap:t%02X\n", to_ap_dev(dev)->device_type);
+}
+
+static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL);
+
+static struct attribute *ap_card_dev_attrs[] = {
+ &dev_attr_hwtype.attr,
+ &dev_attr_raw_hwtype.attr,
+ &dev_attr_depth.attr,
+ &dev_attr_ap_functions.attr,
+ &dev_attr_request_count.attr,
+ &dev_attr_requestq_count.attr,
+ &dev_attr_pendingq_count.attr,
+ &dev_attr_modalias.attr,
+ NULL
+};
+
+static struct attribute_group ap_card_dev_attr_group = {
+ .attrs = ap_card_dev_attrs
+};
+
+static const struct attribute_group *ap_card_dev_attr_groups[] = {
+ &ap_card_dev_attr_group,
+ NULL
+};
+
+struct device_type ap_card_type = {
+ .name = "ap_card",
+ .groups = ap_card_dev_attr_groups,
+};
+
+static void ap_card_device_release(struct device *dev)
+{
+ kfree(to_ap_card(dev));
+}
+
+struct ap_card *ap_card_create(int id, int queue_depth, int device_type,
+ unsigned int functions)
+{
+ struct ap_card *ac;
+
+ ac = kzalloc(sizeof(*ac), GFP_KERNEL);
+ if (!ac)
+ return NULL;
+ INIT_LIST_HEAD(&ac->queues);
+ ac->ap_dev.device.release = ap_card_device_release;
+ ac->ap_dev.device.type = &ap_card_type;
+ ac->ap_dev.device_type = device_type;
+ /* CEX6 toleration: map to CEX5 */
+ if (device_type == AP_DEVICE_TYPE_CEX6)
+ ac->ap_dev.device_type = AP_DEVICE_TYPE_CEX5;
+ ac->raw_hwtype = device_type;
+ ac->queue_depth = queue_depth;
+ ac->functions = functions;
+ ac->id = id;
+ return ac;
+}
diff --git a/drivers/s390/crypto/ap_debug.h b/drivers/s390/crypto/ap_debug.h
new file mode 100644
index 000000000000..78dbff842dae
--- /dev/null
+++ b/drivers/s390/crypto/ap_debug.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright IBM Corp. 2016
+ * Author(s): Harald Freudenberger <freude@de.ibm.com>
+ */
+#ifndef AP_DEBUG_H
+#define AP_DEBUG_H
+
+#include <asm/debug.h>
+
+#define DBF_ERR 3 /* error conditions */
+#define DBF_WARN 4 /* warning conditions */
+#define DBF_INFO 5 /* informational */
+#define DBF_DEBUG 6 /* for debugging only */
+
+#define RC2ERR(rc) ((rc) ? DBF_ERR : DBF_INFO)
+#define RC2WARN(rc) ((rc) ? DBF_WARN : DBF_INFO)
+
+#define DBF_MAX_SPRINTF_ARGS 5
+
+#define AP_DBF(...) \
+ debug_sprintf_event(ap_dbf_info, ##__VA_ARGS__)
+
+extern debug_info_t *ap_dbf_info;
+
+int ap_debug_init(void);
+void ap_debug_exit(void);
+
+#endif /* AP_DEBUG_H */
diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c
new file mode 100644
index 000000000000..b58a917dc510
--- /dev/null
+++ b/drivers/s390/crypto/ap_queue.c
@@ -0,0 +1,701 @@
+/*
+ * Copyright IBM Corp. 2016
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *
+ * Adjunct processor bus, queue related code.
+ */
+
+#define KMSG_COMPONENT "ap"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/facility.h>
+
+#include "ap_bus.h"
+#include "ap_asm.h"
+
+/**
+ * ap_queue_enable_interruption(): Enable interruption on an AP queue.
+ * @qid: The AP queue number
+ * @ind: the notification indicator byte
+ *
+ * Enables interruption on AP queue via ap_aqic(). Based on the return
+ * value it waits a while and tests the AP queue if interrupts
+ * have been switched on using ap_test_queue().
+ */
+static int ap_queue_enable_interruption(struct ap_queue *aq, void *ind)
+{
+ struct ap_queue_status status;
+
+ status = ap_aqic(aq->qid, ind);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ case AP_RESPONSE_OTHERWISE_CHANGED:
+ return 0;
+ case AP_RESPONSE_Q_NOT_AVAIL:
+ case AP_RESPONSE_DECONFIGURED:
+ case AP_RESPONSE_CHECKSTOPPED:
+ case AP_RESPONSE_INVALID_ADDRESS:
+ pr_err("Registering adapter interrupts for AP device %02x.%04x failed\n",
+ AP_QID_CARD(aq->qid),
+ AP_QID_QUEUE(aq->qid));
+ return -EOPNOTSUPP;
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ case AP_RESPONSE_BUSY:
+ default:
+ return -EBUSY;
+ }
+}
+
+/**
+ * __ap_send(): 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
+ * @special: Special Bit
+ *
+ * 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,
+ unsigned int special)
+{
+ if (special == 1)
+ qid |= 0x400000UL;
+ return ap_nqap(qid, psmid, msg, length);
+}
+
+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, 0);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ return 0;
+ case AP_RESPONSE_Q_FULL:
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ return -EBUSY;
+ case AP_RESPONSE_REQ_FAC_NOT_INST:
+ return -EINVAL;
+ default: /* Device is gone. */
+ return -ENODEV;
+ }
+}
+EXPORT_SYMBOL(ap_send);
+
+int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length)
+{
+ struct ap_queue_status status;
+
+ if (msg == NULL)
+ return -EINVAL;
+ status = ap_dqap(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;
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ return -EBUSY;
+ default:
+ return -ENODEV;
+ }
+}
+EXPORT_SYMBOL(ap_recv);
+
+/* State machine definitions and helpers */
+
+static enum ap_wait ap_sm_nop(struct ap_queue *aq)
+{
+ return AP_WAIT_NONE;
+}
+
+/**
+ * ap_sm_recv(): Receive pending reply messages from an AP queue but do
+ * not change the state of the device.
+ * @aq: pointer to the AP queue
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static struct ap_queue_status ap_sm_recv(struct ap_queue *aq)
+{
+ struct ap_queue_status status;
+ struct ap_message *ap_msg;
+
+ status = ap_dqap(aq->qid, &aq->reply->psmid,
+ aq->reply->message, aq->reply->length);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ aq->queue_count--;
+ if (aq->queue_count > 0)
+ mod_timer(&aq->timeout,
+ jiffies + aq->request_timeout);
+ list_for_each_entry(ap_msg, &aq->pendingq, list) {
+ if (ap_msg->psmid != aq->reply->psmid)
+ continue;
+ list_del_init(&ap_msg->list);
+ aq->pendingq_count--;
+ ap_msg->receive(aq, ap_msg, aq->reply);
+ break;
+ }
+ case AP_RESPONSE_NO_PENDING_REPLY:
+ if (!status.queue_empty || aq->queue_count <= 0)
+ break;
+ /* The card shouldn't forget requests but who knows. */
+ aq->queue_count = 0;
+ list_splice_init(&aq->pendingq, &aq->requestq);
+ aq->requestq_count += aq->pendingq_count;
+ aq->pendingq_count = 0;
+ break;
+ default:
+ break;
+ }
+ return status;
+}
+
+/**
+ * ap_sm_read(): Receive pending reply messages from an AP queue.
+ * @aq: pointer to the AP queue
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static enum ap_wait ap_sm_read(struct ap_queue *aq)
+{
+ struct ap_queue_status status;
+
+ if (!aq->reply)
+ return AP_WAIT_NONE;
+ status = ap_sm_recv(aq);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ if (aq->queue_count > 0) {
+ aq->state = AP_STATE_WORKING;
+ return AP_WAIT_AGAIN;
+ }
+ aq->state = AP_STATE_IDLE;
+ return AP_WAIT_NONE;
+ case AP_RESPONSE_NO_PENDING_REPLY:
+ if (aq->queue_count > 0)
+ return AP_WAIT_INTERRUPT;
+ aq->state = AP_STATE_IDLE;
+ return AP_WAIT_NONE;
+ default:
+ aq->state = AP_STATE_BORKED;
+ return AP_WAIT_NONE;
+ }
+}
+
+/**
+ * ap_sm_suspend_read(): Receive pending reply messages from an AP queue
+ * without changing the device state in between. In suspend mode we don't
+ * allow sending new requests, therefore just fetch pending replies.
+ * @aq: pointer to the AP queue
+ *
+ * Returns AP_WAIT_NONE or AP_WAIT_AGAIN
+ */
+static enum ap_wait ap_sm_suspend_read(struct ap_queue *aq)
+{
+ struct ap_queue_status status;
+
+ if (!aq->reply)
+ return AP_WAIT_NONE;
+ status = ap_sm_recv(aq);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ if (aq->queue_count > 0)
+ return AP_WAIT_AGAIN;
+ /* fall through */
+ default:
+ return AP_WAIT_NONE;
+ }
+}
+
+/**
+ * ap_sm_write(): Send messages from the request queue to an AP queue.
+ * @aq: pointer to the AP queue
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static enum ap_wait ap_sm_write(struct ap_queue *aq)
+{
+ struct ap_queue_status status;
+ struct ap_message *ap_msg;
+
+ if (aq->requestq_count <= 0)
+ return AP_WAIT_NONE;
+ /* Start the next request on the queue. */
+ ap_msg = list_entry(aq->requestq.next, struct ap_message, list);
+ status = __ap_send(aq->qid, ap_msg->psmid,
+ ap_msg->message, ap_msg->length, ap_msg->special);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ aq->queue_count++;
+ if (aq->queue_count == 1)
+ mod_timer(&aq->timeout, jiffies + aq->request_timeout);
+ list_move_tail(&ap_msg->list, &aq->pendingq);
+ aq->requestq_count--;
+ aq->pendingq_count++;
+ if (aq->queue_count < aq->card->queue_depth) {
+ aq->state = AP_STATE_WORKING;
+ return AP_WAIT_AGAIN;
+ }
+ /* fall through */
+ case AP_RESPONSE_Q_FULL:
+ aq->state = AP_STATE_QUEUE_FULL;
+ return AP_WAIT_INTERRUPT;
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ aq->state = AP_STATE_RESET_WAIT;
+ return AP_WAIT_TIMEOUT;
+ case AP_RESPONSE_MESSAGE_TOO_BIG:
+ case AP_RESPONSE_REQ_FAC_NOT_INST:
+ list_del_init(&ap_msg->list);
+ aq->requestq_count--;
+ ap_msg->rc = -EINVAL;
+ ap_msg->receive(aq, ap_msg, NULL);
+ return AP_WAIT_AGAIN;
+ default:
+ aq->state = AP_STATE_BORKED;
+ return AP_WAIT_NONE;
+ }
+}
+
+/**
+ * ap_sm_read_write(): Send and receive messages to/from an AP queue.
+ * @aq: pointer to the AP queue
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static enum ap_wait ap_sm_read_write(struct ap_queue *aq)
+{
+ return min(ap_sm_read(aq), ap_sm_write(aq));
+}
+
+/**
+ * ap_sm_reset(): Reset an AP queue.
+ * @qid: The AP queue number
+ *
+ * Submit the Reset command to an AP queue.
+ */
+static enum ap_wait ap_sm_reset(struct ap_queue *aq)
+{
+ struct ap_queue_status status;
+
+ status = ap_rapq(aq->qid);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ aq->state = AP_STATE_RESET_WAIT;
+ aq->interrupt = AP_INTR_DISABLED;
+ return AP_WAIT_TIMEOUT;
+ case AP_RESPONSE_BUSY:
+ return AP_WAIT_TIMEOUT;
+ case AP_RESPONSE_Q_NOT_AVAIL:
+ case AP_RESPONSE_DECONFIGURED:
+ case AP_RESPONSE_CHECKSTOPPED:
+ default:
+ aq->state = AP_STATE_BORKED;
+ return AP_WAIT_NONE;
+ }
+}
+
+/**
+ * ap_sm_reset_wait(): Test queue for completion of the reset operation
+ * @aq: pointer to the AP queue
+ *
+ * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0.
+ */
+static enum ap_wait ap_sm_reset_wait(struct ap_queue *aq)
+{
+ struct ap_queue_status status;
+ void *lsi_ptr;
+
+ if (aq->queue_count > 0 && aq->reply)
+ /* Try to read a completed message and get the status */
+ status = ap_sm_recv(aq);
+ else
+ /* Get the status with TAPQ */
+ status = ap_tapq(aq->qid, NULL);
+
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ lsi_ptr = ap_airq_ptr();
+ if (lsi_ptr && ap_queue_enable_interruption(aq, lsi_ptr) == 0)
+ aq->state = AP_STATE_SETIRQ_WAIT;
+ else
+ aq->state = (aq->queue_count > 0) ?
+ AP_STATE_WORKING : AP_STATE_IDLE;
+ return AP_WAIT_AGAIN;
+ case AP_RESPONSE_BUSY:
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ return AP_WAIT_TIMEOUT;
+ case AP_RESPONSE_Q_NOT_AVAIL:
+ case AP_RESPONSE_DECONFIGURED:
+ case AP_RESPONSE_CHECKSTOPPED:
+ default:
+ aq->state = AP_STATE_BORKED;
+ return AP_WAIT_NONE;
+ }
+}
+
+/**
+ * ap_sm_setirq_wait(): Test queue for completion of the irq enablement
+ * @aq: pointer to the AP queue
+ *
+ * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0.
+ */
+static enum ap_wait ap_sm_setirq_wait(struct ap_queue *aq)
+{
+ struct ap_queue_status status;
+
+ if (aq->queue_count > 0 && aq->reply)
+ /* Try to read a completed message and get the status */
+ status = ap_sm_recv(aq);
+ else
+ /* Get the status with TAPQ */
+ status = ap_tapq(aq->qid, NULL);
+
+ if (status.int_enabled == 1) {
+ /* Irqs are now enabled */
+ aq->interrupt = AP_INTR_ENABLED;
+ aq->state = (aq->queue_count > 0) ?
+ AP_STATE_WORKING : AP_STATE_IDLE;
+ }
+
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ if (aq->queue_count > 0)
+ return AP_WAIT_AGAIN;
+ /* fallthrough */
+ case AP_RESPONSE_NO_PENDING_REPLY:
+ return AP_WAIT_TIMEOUT;
+ default:
+ aq->state = AP_STATE_BORKED;
+ return AP_WAIT_NONE;
+ }
+}
+
+/*
+ * AP state machine jump table
+ */
+static ap_func_t *ap_jumptable[NR_AP_STATES][NR_AP_EVENTS] = {
+ [AP_STATE_RESET_START] = {
+ [AP_EVENT_POLL] = ap_sm_reset,
+ [AP_EVENT_TIMEOUT] = ap_sm_nop,
+ },
+ [AP_STATE_RESET_WAIT] = {
+ [AP_EVENT_POLL] = ap_sm_reset_wait,
+ [AP_EVENT_TIMEOUT] = ap_sm_nop,
+ },
+ [AP_STATE_SETIRQ_WAIT] = {
+ [AP_EVENT_POLL] = ap_sm_setirq_wait,
+ [AP_EVENT_TIMEOUT] = ap_sm_nop,
+ },
+ [AP_STATE_IDLE] = {
+ [AP_EVENT_POLL] = ap_sm_write,
+ [AP_EVENT_TIMEOUT] = ap_sm_nop,
+ },
+ [AP_STATE_WORKING] = {
+ [AP_EVENT_POLL] = ap_sm_read_write,
+ [AP_EVENT_TIMEOUT] = ap_sm_reset,
+ },
+ [AP_STATE_QUEUE_FULL] = {
+ [AP_EVENT_POLL] = ap_sm_read,
+ [AP_EVENT_TIMEOUT] = ap_sm_reset,
+ },
+ [AP_STATE_SUSPEND_WAIT] = {
+ [AP_EVENT_POLL] = ap_sm_suspend_read,
+ [AP_EVENT_TIMEOUT] = ap_sm_nop,
+ },
+ [AP_STATE_BORKED] = {
+ [AP_EVENT_POLL] = ap_sm_nop,
+ [AP_EVENT_TIMEOUT] = ap_sm_nop,
+ },
+};
+
+enum ap_wait ap_sm_event(struct ap_queue *aq, enum ap_event event)
+{
+ return ap_jumptable[aq->state][event](aq);
+}
+
+enum ap_wait ap_sm_event_loop(struct ap_queue *aq, enum ap_event event)
+{
+ enum ap_wait wait;
+
+ while ((wait = ap_sm_event(aq, event)) == AP_WAIT_AGAIN)
+ ;
+ return wait;
+}
+
+/*
+ * Power management for queue devices
+ */
+void ap_queue_suspend(struct ap_device *ap_dev)
+{
+ struct ap_queue *aq = to_ap_queue(&ap_dev->device);
+
+ /* Poll on the device until all requests are finished. */
+ spin_lock_bh(&aq->lock);
+ aq->state = AP_STATE_SUSPEND_WAIT;
+ while (ap_sm_event(aq, AP_EVENT_POLL) != AP_WAIT_NONE)
+ ;
+ aq->state = AP_STATE_BORKED;
+ spin_unlock_bh(&aq->lock);
+}
+EXPORT_SYMBOL(ap_queue_suspend);
+
+void ap_queue_resume(struct ap_device *ap_dev)
+{
+}
+EXPORT_SYMBOL(ap_queue_resume);
+
+/*
+ * AP queue related attributes.
+ */
+static ssize_t ap_request_count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ap_queue *aq = to_ap_queue(dev);
+ unsigned int req_cnt;
+
+ spin_lock_bh(&aq->lock);
+ req_cnt = aq->total_request_count;
+ spin_unlock_bh(&aq->lock);
+ return snprintf(buf, PAGE_SIZE, "%d\n", req_cnt);
+}
+
+static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL);
+
+static ssize_t ap_requestq_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_queue *aq = to_ap_queue(dev);
+ unsigned int reqq_cnt = 0;
+
+ spin_lock_bh(&aq->lock);
+ reqq_cnt = aq->requestq_count;
+ spin_unlock_bh(&aq->lock);
+ return snprintf(buf, PAGE_SIZE, "%d\n", reqq_cnt);
+}
+
+static DEVICE_ATTR(requestq_count, 0444, ap_requestq_count_show, NULL);
+
+static ssize_t ap_pendingq_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_queue *aq = to_ap_queue(dev);
+ unsigned int penq_cnt = 0;
+
+ spin_lock_bh(&aq->lock);
+ penq_cnt = aq->pendingq_count;
+ spin_unlock_bh(&aq->lock);
+ return snprintf(buf, PAGE_SIZE, "%d\n", penq_cnt);
+}
+
+static DEVICE_ATTR(pendingq_count, 0444, ap_pendingq_count_show, NULL);
+
+static ssize_t ap_reset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_queue *aq = to_ap_queue(dev);
+ int rc = 0;
+
+ spin_lock_bh(&aq->lock);
+ switch (aq->state) {
+ case AP_STATE_RESET_START:
+ case AP_STATE_RESET_WAIT:
+ rc = snprintf(buf, PAGE_SIZE, "Reset in progress.\n");
+ break;
+ case AP_STATE_WORKING:
+ case AP_STATE_QUEUE_FULL:
+ rc = snprintf(buf, PAGE_SIZE, "Reset Timer armed.\n");
+ break;
+ default:
+ rc = snprintf(buf, PAGE_SIZE, "No Reset Timer set.\n");
+ }
+ spin_unlock_bh(&aq->lock);
+ return rc;
+}
+
+static DEVICE_ATTR(reset, 0444, ap_reset_show, NULL);
+
+static ssize_t ap_interrupt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_queue *aq = to_ap_queue(dev);
+ int rc = 0;
+
+ spin_lock_bh(&aq->lock);
+ if (aq->state == AP_STATE_SETIRQ_WAIT)
+ rc = snprintf(buf, PAGE_SIZE, "Enable Interrupt pending.\n");
+ else if (aq->interrupt == AP_INTR_ENABLED)
+ rc = snprintf(buf, PAGE_SIZE, "Interrupts enabled.\n");
+ else
+ rc = snprintf(buf, PAGE_SIZE, "Interrupts disabled.\n");
+ spin_unlock_bh(&aq->lock);
+ return rc;
+}
+
+static DEVICE_ATTR(interrupt, 0444, ap_interrupt_show, NULL);
+
+static struct attribute *ap_queue_dev_attrs[] = {
+ &dev_attr_request_count.attr,
+ &dev_attr_requestq_count.attr,
+ &dev_attr_pendingq_count.attr,
+ &dev_attr_reset.attr,
+ &dev_attr_interrupt.attr,
+ NULL
+};
+
+static struct attribute_group ap_queue_dev_attr_group = {
+ .attrs = ap_queue_dev_attrs
+};
+
+static const struct attribute_group *ap_queue_dev_attr_groups[] = {
+ &ap_queue_dev_attr_group,
+ NULL
+};
+
+struct device_type ap_queue_type = {
+ .name = "ap_queue",
+ .groups = ap_queue_dev_attr_groups,
+};
+
+static void ap_queue_device_release(struct device *dev)
+{
+ kfree(to_ap_queue(dev));
+}
+
+struct ap_queue *ap_queue_create(ap_qid_t qid, int device_type)
+{
+ struct ap_queue *aq;
+
+ aq = kzalloc(sizeof(*aq), GFP_KERNEL);
+ if (!aq)
+ return NULL;
+ aq->ap_dev.device.release = ap_queue_device_release;
+ aq->ap_dev.device.type = &ap_queue_type;
+ aq->ap_dev.device_type = device_type;
+ /* CEX6 toleration: map to CEX5 */
+ if (device_type == AP_DEVICE_TYPE_CEX6)
+ aq->ap_dev.device_type = AP_DEVICE_TYPE_CEX5;
+ aq->qid = qid;
+ aq->state = AP_STATE_RESET_START;
+ aq->interrupt = AP_INTR_DISABLED;
+ spin_lock_init(&aq->lock);
+ INIT_LIST_HEAD(&aq->pendingq);
+ INIT_LIST_HEAD(&aq->requestq);
+ setup_timer(&aq->timeout, ap_request_timeout, (unsigned long) aq);
+
+ return aq;
+}
+
+void ap_queue_init_reply(struct ap_queue *aq, struct ap_message *reply)
+{
+ aq->reply = reply;
+
+ spin_lock_bh(&aq->lock);
+ ap_wait(ap_sm_event(aq, AP_EVENT_POLL));
+ spin_unlock_bh(&aq->lock);
+}
+EXPORT_SYMBOL(ap_queue_init_reply);
+
+/**
+ * ap_queue_message(): Queue a request to an AP device.
+ * @aq: The AP device to queue the message to
+ * @ap_msg: The message that is to be added
+ */
+void ap_queue_message(struct ap_queue *aq, struct ap_message *ap_msg)
+{
+ /* For asynchronous message handling a valid receive-callback
+ * is required.
+ */
+ BUG_ON(!ap_msg->receive);
+
+ spin_lock_bh(&aq->lock);
+ /* Queue the message. */
+ list_add_tail(&ap_msg->list, &aq->requestq);
+ aq->requestq_count++;
+ aq->total_request_count++;
+ atomic_inc(&aq->card->total_request_count);
+ /* Send/receive as many request from the queue as possible. */
+ ap_wait(ap_sm_event_loop(aq, AP_EVENT_POLL));
+ spin_unlock_bh(&aq->lock);
+}
+EXPORT_SYMBOL(ap_queue_message);
+
+/**
+ * ap_cancel_message(): Cancel a crypto request.
+ * @aq: The AP device that has the message queued
+ * @ap_msg: The message that is to be removed
+ *
+ * Cancel a crypto request. This is done by removing the request
+ * from the device pending or request 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.
+ */
+void ap_cancel_message(struct ap_queue *aq, struct ap_message *ap_msg)
+{
+ struct ap_message *tmp;
+
+ spin_lock_bh(&aq->lock);
+ if (!list_empty(&ap_msg->list)) {
+ list_for_each_entry(tmp, &aq->pendingq, list)
+ if (tmp->psmid == ap_msg->psmid) {
+ aq->pendingq_count--;
+ goto found;
+ }
+ aq->requestq_count--;
+found:
+ list_del_init(&ap_msg->list);
+ }
+ spin_unlock_bh(&aq->lock);
+}
+EXPORT_SYMBOL(ap_cancel_message);
+
+/**
+ * __ap_flush_queue(): Flush requests.
+ * @aq: Pointer to the AP queue
+ *
+ * Flush all requests from the request/pending queue of an AP device.
+ */
+static void __ap_flush_queue(struct ap_queue *aq)
+{
+ struct ap_message *ap_msg, *next;
+
+ list_for_each_entry_safe(ap_msg, next, &aq->pendingq, list) {
+ list_del_init(&ap_msg->list);
+ aq->pendingq_count--;
+ ap_msg->rc = -EAGAIN;
+ ap_msg->receive(aq, ap_msg, NULL);
+ }
+ list_for_each_entry_safe(ap_msg, next, &aq->requestq, list) {
+ list_del_init(&ap_msg->list);
+ aq->requestq_count--;
+ ap_msg->rc = -EAGAIN;
+ ap_msg->receive(aq, ap_msg, NULL);
+ }
+}
+
+void ap_flush_queue(struct ap_queue *aq)
+{
+ spin_lock_bh(&aq->lock);
+ __ap_flush_queue(aq);
+ spin_unlock_bh(&aq->lock);
+}
+EXPORT_SYMBOL(ap_flush_queue);
+
+void ap_queue_remove(struct ap_queue *aq)
+{
+ ap_flush_queue(aq);
+ del_timer_sync(&aq->timeout);
+}
+EXPORT_SYMBOL(ap_queue_remove);
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 5d3d04c040c2..51eece9af577 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -36,15 +36,19 @@
#include <linux/compat.h>
#include <linux/slab.h>
#include <linux/atomic.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/hw_random.h>
#include <linux/debugfs.h>
#include <asm/debug.h>
-#include "zcrypt_debug.h"
+#define CREATE_TRACE_POINTS
+#include <asm/trace/zcrypt.h>
+
#include "zcrypt_api.h"
+#include "zcrypt_debug.h"
#include "zcrypt_msgtype6.h"
+#include "zcrypt_msgtype50.h"
/*
* Module description.
@@ -54,76 +58,31 @@ MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " \
"Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
+/*
+ * zcrypt tracepoint functions
+ */
+EXPORT_TRACEPOINT_SYMBOL(s390_zcrypt_req);
+EXPORT_TRACEPOINT_SYMBOL(s390_zcrypt_rep);
+
static int zcrypt_hwrng_seed = 1;
module_param_named(hwrng_seed, zcrypt_hwrng_seed, int, S_IRUSR|S_IRGRP);
MODULE_PARM_DESC(hwrng_seed, "Turn on/off hwrng auto seed, default is 1 (on).");
-static DEFINE_SPINLOCK(zcrypt_device_lock);
-static LIST_HEAD(zcrypt_device_list);
-static int zcrypt_device_count = 0;
+DEFINE_SPINLOCK(zcrypt_list_lock);
+LIST_HEAD(zcrypt_card_list);
+int zcrypt_device_count;
+
static atomic_t zcrypt_open_count = ATOMIC_INIT(0);
static atomic_t zcrypt_rescan_count = ATOMIC_INIT(0);
atomic_t zcrypt_rescan_req = ATOMIC_INIT(0);
EXPORT_SYMBOL(zcrypt_rescan_req);
-static int zcrypt_rng_device_add(void);
-static void zcrypt_rng_device_remove(void);
-
-static DEFINE_SPINLOCK(zcrypt_ops_list_lock);
static LIST_HEAD(zcrypt_ops_list);
-static debug_info_t *zcrypt_dbf_common;
-static debug_info_t *zcrypt_dbf_devices;
-static struct dentry *debugfs_root;
-
-/*
- * 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;
- ZCRYPT_DBF_DEV(DBF_INFO, zdev, "dev%04xo%dman", zdev->ap_dev->qid,
- zdev->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,
-};
+/* Zcrypt related debug feature stuff. */
+static struct dentry *zcrypt_dbf_root;
+debug_info_t *zcrypt_dbf_info;
/**
* Process a rescan of the transport layer.
@@ -136,242 +95,34 @@ static inline int zcrypt_process_rescan(void)
atomic_set(&zcrypt_rescan_req, 0);
atomic_inc(&zcrypt_rescan_count);
ap_bus_force_rescan();
- ZCRYPT_DBF_COMMON(DBF_INFO, "rescan%07d",
- atomic_inc_return(&zcrypt_rescan_count));
+ ZCRYPT_DBF(DBF_INFO, "rescan count=%07d",
+ atomic_inc_return(&zcrypt_rescan_count));
return 1;
}
return 0;
}
-/**
- * __zcrypt_increase_preference(): Increase preference of a crypto device.
- * @zdev: Pointer the crypto device
- *
- * 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_move(&zdev->list, l);
-}
-
-/**
- * __zcrypt_decrease_preference(): Decrease preference of a crypto device.
- * @zdev: Pointer to a crypto device.
- *
- * 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_move_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);
- zdev->dbf_area = zcrypt_dbf_devices;
- 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);
-
-/**
- * zcrypt_device_register() - Register a crypto device.
- * @zdev: Pointer to a crypto device
- *
- * Register a crypto device. Returns 0 if successful.
- */
-int zcrypt_device_register(struct zcrypt_device *zdev)
-{
- int rc;
-
- if (!zdev->ops)
- return -ENODEV;
- 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. */
- ZCRYPT_DBF_DEV(DBF_INFO, zdev, "dev%04xo%dreg", zdev->ap_dev->qid,
- zdev->online);
- list_add_tail(&zdev->list, &zcrypt_device_list);
- __zcrypt_increase_preference(zdev);
- zcrypt_device_count++;
- spin_unlock_bh(&zcrypt_device_lock);
- if (zdev->ops->rng) {
- rc = zcrypt_rng_device_add();
- if (rc)
- goto out_unregister;
- }
- return 0;
-
-out_unregister:
- 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);
-out:
- return rc;
-}
-EXPORT_SYMBOL(zcrypt_device_register);
-
-/**
- * zcrypt_device_unregister(): Unregister a crypto device.
- * @zdev: Pointer to crypto device
- *
- * Unregister a crypto device.
- */
-void zcrypt_device_unregister(struct zcrypt_device *zdev)
-{
- if (zdev->ops->rng)
- zcrypt_rng_device_remove();
- 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);
-
void zcrypt_msgtype_register(struct zcrypt_ops *zops)
{
- spin_lock_bh(&zcrypt_ops_list_lock);
list_add_tail(&zops->list, &zcrypt_ops_list);
- spin_unlock_bh(&zcrypt_ops_list_lock);
}
-EXPORT_SYMBOL(zcrypt_msgtype_register);
void zcrypt_msgtype_unregister(struct zcrypt_ops *zops)
{
- spin_lock_bh(&zcrypt_ops_list_lock);
list_del_init(&zops->list);
- spin_unlock_bh(&zcrypt_ops_list_lock);
}
-EXPORT_SYMBOL(zcrypt_msgtype_unregister);
-static inline
-struct zcrypt_ops *__ops_lookup(unsigned char *name, int variant)
+struct zcrypt_ops *zcrypt_msgtype(unsigned char *name, int variant)
{
struct zcrypt_ops *zops;
- int found = 0;
- spin_lock_bh(&zcrypt_ops_list_lock);
- list_for_each_entry(zops, &zcrypt_ops_list, list) {
+ list_for_each_entry(zops, &zcrypt_ops_list, list)
if ((zops->variant == variant) &&
- (!strncmp(zops->name, name, sizeof(zops->name)))) {
- found = 1;
- break;
- }
- }
- if (!found || !try_module_get(zops->owner))
- zops = NULL;
-
- spin_unlock_bh(&zcrypt_ops_list_lock);
-
- return zops;
-}
-
-struct zcrypt_ops *zcrypt_msgtype_request(unsigned char *name, int variant)
-{
- struct zcrypt_ops *zops = NULL;
-
- zops = __ops_lookup(name, variant);
- if (!zops) {
- request_module("%s", name);
- zops = __ops_lookup(name, variant);
- }
- return zops;
-}
-EXPORT_SYMBOL(zcrypt_msgtype_request);
-
-void zcrypt_msgtype_release(struct zcrypt_ops *zops)
-{
- if (zops)
- module_put(zops->owner);
+ (!strncmp(zops->name, name, sizeof(zops->name))))
+ return zops;
+ return NULL;
}
-EXPORT_SYMBOL(zcrypt_msgtype_release);
+EXPORT_SYMBOL(zcrypt_msgtype);
/**
* zcrypt_read (): Not supported beyond zcrypt 1.3.1.
@@ -417,16 +168,80 @@ static int zcrypt_release(struct inode *inode, struct file *filp)
return 0;
}
+static inline struct zcrypt_queue *zcrypt_pick_queue(struct zcrypt_card *zc,
+ struct zcrypt_queue *zq,
+ unsigned int weight)
+{
+ if (!zq || !try_module_get(zq->queue->ap_dev.drv->driver.owner))
+ return NULL;
+ zcrypt_queue_get(zq);
+ get_device(&zq->queue->ap_dev.device);
+ atomic_add(weight, &zc->load);
+ atomic_add(weight, &zq->load);
+ zq->request_count++;
+ return zq;
+}
+
+static inline void zcrypt_drop_queue(struct zcrypt_card *zc,
+ struct zcrypt_queue *zq,
+ unsigned int weight)
+{
+ struct module *mod = zq->queue->ap_dev.drv->driver.owner;
+
+ zq->request_count--;
+ atomic_sub(weight, &zc->load);
+ atomic_sub(weight, &zq->load);
+ put_device(&zq->queue->ap_dev.device);
+ zcrypt_queue_put(zq);
+ module_put(mod);
+}
+
+static inline bool zcrypt_card_compare(struct zcrypt_card *zc,
+ struct zcrypt_card *pref_zc,
+ unsigned weight, unsigned pref_weight)
+{
+ if (!pref_zc)
+ return 0;
+ weight += atomic_read(&zc->load);
+ pref_weight += atomic_read(&pref_zc->load);
+ if (weight == pref_weight)
+ return atomic_read(&zc->card->total_request_count) >
+ atomic_read(&pref_zc->card->total_request_count);
+ return weight > pref_weight;
+}
+
+static inline bool zcrypt_queue_compare(struct zcrypt_queue *zq,
+ struct zcrypt_queue *pref_zq,
+ unsigned weight, unsigned pref_weight)
+{
+ if (!pref_zq)
+ return 0;
+ weight += atomic_read(&zq->load);
+ pref_weight += atomic_read(&pref_zq->load);
+ if (weight == pref_weight)
+ return &zq->queue->total_request_count >
+ &pref_zq->queue->total_request_count;
+ return weight > pref_weight;
+}
+
/*
* zcrypt ioctls.
*/
static long zcrypt_rsa_modexpo(struct ica_rsa_modexpo *mex)
{
- struct zcrypt_device *zdev;
- int rc;
+ struct zcrypt_card *zc, *pref_zc;
+ struct zcrypt_queue *zq, *pref_zq;
+ unsigned int weight, pref_weight;
+ unsigned int func_code;
+ int qid = 0, rc = -ENODEV;
+
+ trace_s390_zcrypt_req(mex, TP_ICARSAMODEXPO);
+
+ if (mex->outputdatalength < mex->inputdatalength) {
+ rc = -EINVAL;
+ goto out;
+ }
- 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
@@ -434,44 +249,73 @@ static long zcrypt_rsa_modexpo(struct ica_rsa_modexpo *mex)
*/
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)
+ rc = get_rsa_modex_fc(mex, &func_code);
+ if (rc)
+ goto out;
+
+ pref_zc = NULL;
+ pref_zq = NULL;
+ spin_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ /* Check for online accelarator and CCA cards */
+ if (!zc->online || !(zc->card->functions & 0x18000000))
+ continue;
+ /* Check for size limits */
+ if (zc->min_mod_size > mex->inputdatalength ||
+ zc->max_mod_size < mex->inputdatalength)
+ continue;
+ /* get weight index of the card device */
+ weight = zc->speed_rating[func_code];
+ if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight))
continue;
- zcrypt_device_get(zdev);
- get_device(&zdev->ap_dev->device);
- zdev->request_count++;
- __zcrypt_decrease_preference(zdev);
- if (try_module_get(zdev->ap_dev->drv->driver.owner)) {
- spin_unlock_bh(&zcrypt_device_lock);
- rc = zdev->ops->rsa_modexpo(zdev, mex);
- spin_lock_bh(&zcrypt_device_lock);
- module_put(zdev->ap_dev->drv->driver.owner);
+ for_each_zcrypt_queue(zq, zc) {
+ /* check if device is online and eligible */
+ if (!zq->online || !zq->ops->rsa_modexpo)
+ continue;
+ if (zcrypt_queue_compare(zq, pref_zq,
+ weight, pref_weight))
+ continue;
+ pref_zc = zc;
+ pref_zq = zq;
+ pref_weight = weight;
}
- else
- rc = -EAGAIN;
- 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;
+ pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+ spin_unlock(&zcrypt_list_lock);
+
+ if (!pref_zq) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ qid = pref_zq->queue->qid;
+ rc = pref_zq->ops->rsa_modexpo(pref_zq, mex);
+
+ spin_lock(&zcrypt_list_lock);
+ zcrypt_drop_queue(pref_zc, pref_zq, weight);
+ spin_unlock(&zcrypt_list_lock);
+
+out:
+ trace_s390_zcrypt_rep(mex, func_code, rc,
+ AP_QID_CARD(qid), AP_QID_QUEUE(qid));
+ return rc;
}
static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt)
{
- struct zcrypt_device *zdev;
- unsigned long long z1, z2, z3;
- int rc, copied;
+ struct zcrypt_card *zc, *pref_zc;
+ struct zcrypt_queue *zq, *pref_zq;
+ unsigned int weight, pref_weight;
+ unsigned int func_code;
+ int qid = 0, rc = -ENODEV;
+
+ trace_s390_zcrypt_req(crt, TP_ICARSACRT);
+
+ if (crt->outputdatalength < crt->inputdatalength) {
+ rc = -EINVAL;
+ goto out;
+ }
- if (crt->outputdatalength < crt->inputdatalength)
- return -EINVAL;
/*
* As long as outputdatalength is big enough, we can set the
* outputdatalength equal to the inputdatalength, since that is the
@@ -479,308 +323,445 @@ static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt)
*/
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)
+ rc = get_rsa_crt_fc(crt, &func_code);
+ if (rc)
+ goto out;
+
+ pref_zc = NULL;
+ pref_zq = NULL;
+ spin_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ /* Check for online accelarator and CCA cards */
+ if (!zc->online || !(zc->card->functions & 0x18000000))
+ continue;
+ /* Check for size limits */
+ if (zc->min_mod_size > crt->inputdatalength ||
+ zc->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) {
- unsigned int len;
- spin_unlock_bh(&zcrypt_device_lock);
- /* len is max 256 / 2 - 120 = 8
- * For bigger device just assume len of leading
- * 0s is 8 as stated in the requirements for
- * ica_rsa_modexpo_crt struct in zcrypt.h.
- */
- if (crt->inputdatalength <= 256)
- len = crt->inputdatalength / 2 - 120;
- else
- len = 8;
- if (len > sizeof(z1))
- return -EFAULT;
- z1 = z2 = z3 = 0;
- 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;
- z1 = z2 = z3 = 0;
- 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. */
+ /* get weight index of the card device */
+ weight = zc->speed_rating[func_code];
+ if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight))
+ continue;
+ for_each_zcrypt_queue(zq, zc) {
+ /* check if device is online and eligible */
+ if (!zq->online || !zq->ops->rsa_modexpo_crt)
continue;
+ if (zcrypt_queue_compare(zq, pref_zq,
+ weight, pref_weight))
+ continue;
+ pref_zc = zc;
+ pref_zq = zq;
+ pref_weight = weight;
}
- zcrypt_device_get(zdev);
- get_device(&zdev->ap_dev->device);
- zdev->request_count++;
- __zcrypt_decrease_preference(zdev);
- if (try_module_get(zdev->ap_dev->drv->driver.owner)) {
- spin_unlock_bh(&zcrypt_device_lock);
- rc = zdev->ops->rsa_modexpo_crt(zdev, crt);
- spin_lock_bh(&zcrypt_device_lock);
- module_put(zdev->ap_dev->drv->driver.owner);
- }
- else
- rc = -EAGAIN;
- 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;
+ pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+ spin_unlock(&zcrypt_list_lock);
+
+ if (!pref_zq) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ qid = pref_zq->queue->qid;
+ rc = pref_zq->ops->rsa_modexpo_crt(pref_zq, crt);
+
+ spin_lock(&zcrypt_list_lock);
+ zcrypt_drop_queue(pref_zc, pref_zq, weight);
+ spin_unlock(&zcrypt_list_lock);
+
+out:
+ trace_s390_zcrypt_rep(crt, func_code, rc,
+ AP_QID_CARD(qid), AP_QID_QUEUE(qid));
+ return rc;
}
static long zcrypt_send_cprb(struct ica_xcRB *xcRB)
{
- struct zcrypt_device *zdev;
- int rc;
+ struct zcrypt_card *zc, *pref_zc;
+ struct zcrypt_queue *zq, *pref_zq;
+ struct ap_message ap_msg;
+ unsigned int weight, pref_weight;
+ unsigned int func_code;
+ unsigned short *domain;
+ int qid = 0, rc = -ENODEV;
+
+ trace_s390_zcrypt_req(xcRB, TB_ZSECSENDCPRB);
- spin_lock_bh(&zcrypt_device_lock);
- list_for_each_entry(zdev, &zcrypt_device_list, list) {
- if (!zdev->online || !zdev->ops->send_cprb ||
- (zdev->ops->variant == MSGTYPE06_VARIANT_EP11) ||
- (xcRB->user_defined != AUTOSELECT &&
- AP_QID_DEVICE(zdev->ap_dev->qid) != xcRB->user_defined))
+ rc = get_cprb_fc(xcRB, &ap_msg, &func_code, &domain);
+ if (rc)
+ goto out;
+
+ pref_zc = NULL;
+ pref_zq = NULL;
+ spin_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ /* Check for online CCA cards */
+ if (!zc->online || !(zc->card->functions & 0x10000000))
+ continue;
+ /* Check for user selected CCA card */
+ if (xcRB->user_defined != AUTOSELECT &&
+ xcRB->user_defined != zc->card->id)
continue;
- zcrypt_device_get(zdev);
- get_device(&zdev->ap_dev->device);
- zdev->request_count++;
- __zcrypt_decrease_preference(zdev);
- if (try_module_get(zdev->ap_dev->drv->driver.owner)) {
- spin_unlock_bh(&zcrypt_device_lock);
- rc = zdev->ops->send_cprb(zdev, xcRB);
- spin_lock_bh(&zcrypt_device_lock);
- module_put(zdev->ap_dev->drv->driver.owner);
+ /* get weight index of the card device */
+ weight = speed_idx_cca(func_code) * zc->speed_rating[SECKEY];
+ if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight))
+ continue;
+ for_each_zcrypt_queue(zq, zc) {
+ /* check if device is online and eligible */
+ if (!zq->online ||
+ !zq->ops->send_cprb ||
+ ((*domain != (unsigned short) AUTOSELECT) &&
+ (*domain != AP_QID_QUEUE(zq->queue->qid))))
+ continue;
+ if (zcrypt_queue_compare(zq, pref_zq,
+ weight, pref_weight))
+ continue;
+ pref_zc = zc;
+ pref_zq = zq;
+ pref_weight = weight;
}
- else
- rc = -EAGAIN;
- 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;
-}
+ pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+ spin_unlock(&zcrypt_list_lock);
-struct ep11_target_dev_list {
- unsigned short targets_num;
- struct ep11_target_dev *targets;
-};
+ if (!pref_zq) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ /* in case of auto select, provide the correct domain */
+ qid = pref_zq->queue->qid;
+ if (*domain == (unsigned short) AUTOSELECT)
+ *domain = AP_QID_QUEUE(qid);
-static bool is_desired_ep11dev(unsigned int dev_qid,
- struct ep11_target_dev_list dev_list)
+ rc = pref_zq->ops->send_cprb(pref_zq, xcRB, &ap_msg);
+
+ spin_lock(&zcrypt_list_lock);
+ zcrypt_drop_queue(pref_zc, pref_zq, weight);
+ spin_unlock(&zcrypt_list_lock);
+
+out:
+ trace_s390_zcrypt_rep(xcRB, func_code, rc,
+ AP_QID_CARD(qid), AP_QID_QUEUE(qid));
+ return rc;
+}
+
+static bool is_desired_ep11_card(unsigned int dev_id,
+ unsigned short target_num,
+ struct ep11_target_dev *targets)
{
- int n;
+ while (target_num-- > 0) {
+ if (dev_id == targets->ap_id)
+ return true;
+ targets++;
+ }
+ return false;
+}
- for (n = 0; n < dev_list.targets_num; n++, dev_list.targets++) {
- if ((AP_QID_DEVICE(dev_qid) == dev_list.targets->ap_id) &&
- (AP_QID_QUEUE(dev_qid) == dev_list.targets->dom_id)) {
+static bool is_desired_ep11_queue(unsigned int dev_qid,
+ unsigned short target_num,
+ struct ep11_target_dev *targets)
+{
+ while (target_num-- > 0) {
+ if (AP_MKQID(targets->ap_id, targets->dom_id) == dev_qid)
return true;
- }
+ targets++;
}
return false;
}
static long zcrypt_send_ep11_cprb(struct ep11_urb *xcrb)
{
- struct zcrypt_device *zdev;
- bool autoselect = false;
- int rc;
- struct ep11_target_dev_list ep11_dev_list = {
- .targets_num = 0x00,
- .targets = NULL,
- };
+ struct zcrypt_card *zc, *pref_zc;
+ struct zcrypt_queue *zq, *pref_zq;
+ struct ep11_target_dev *targets;
+ unsigned short target_num;
+ unsigned int weight, pref_weight;
+ unsigned int func_code;
+ struct ap_message ap_msg;
+ int qid = 0, rc = -ENODEV;
+
+ trace_s390_zcrypt_req(xcrb, TP_ZSENDEP11CPRB);
- ep11_dev_list.targets_num = (unsigned short) xcrb->targets_num;
+ target_num = (unsigned short) xcrb->targets_num;
/* empty list indicates autoselect (all available targets) */
- if (ep11_dev_list.targets_num == 0)
- autoselect = true;
- else {
- ep11_dev_list.targets = kcalloc((unsigned short)
- xcrb->targets_num,
- sizeof(struct ep11_target_dev),
- GFP_KERNEL);
- if (!ep11_dev_list.targets)
- return -ENOMEM;
+ targets = NULL;
+ if (target_num != 0) {
+ struct ep11_target_dev __user *uptr;
- if (copy_from_user(ep11_dev_list.targets,
- (struct ep11_target_dev __force __user *)
- xcrb->targets, xcrb->targets_num *
- sizeof(struct ep11_target_dev)))
- return -EFAULT;
+ targets = kcalloc(target_num, sizeof(*targets), GFP_KERNEL);
+ if (!targets) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ uptr = (struct ep11_target_dev __force __user *) xcrb->targets;
+ if (copy_from_user(targets, uptr,
+ target_num * sizeof(*targets))) {
+ rc = -EFAULT;
+ goto out;
+ }
}
- spin_lock_bh(&zcrypt_device_lock);
- list_for_each_entry(zdev, &zcrypt_device_list, list) {
- /* check if device is eligible */
- if (!zdev->online ||
- zdev->ops->variant != MSGTYPE06_VARIANT_EP11)
- continue;
+ rc = get_ep11cprb_fc(xcrb, &ap_msg, &func_code);
+ if (rc)
+ goto out_free;
- /* check if device is selected as valid target */
- if (!is_desired_ep11dev(zdev->ap_dev->qid, ep11_dev_list) &&
- !autoselect)
+ pref_zc = NULL;
+ pref_zq = NULL;
+ spin_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ /* Check for online EP11 cards */
+ if (!zc->online || !(zc->card->functions & 0x04000000))
+ continue;
+ /* Check for user selected EP11 card */
+ if (targets &&
+ !is_desired_ep11_card(zc->card->id, target_num, targets))
continue;
+ /* get weight index of the card device */
+ weight = speed_idx_ep11(func_code) * zc->speed_rating[SECKEY];
+ if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight))
+ continue;
+ for_each_zcrypt_queue(zq, zc) {
+ /* check if device is online and eligible */
+ if (!zq->online ||
+ !zq->ops->send_ep11_cprb ||
+ (targets &&
+ !is_desired_ep11_queue(zq->queue->qid,
+ target_num, targets)))
+ continue;
+ if (zcrypt_queue_compare(zq, pref_zq,
+ weight, pref_weight))
+ continue;
+ pref_zc = zc;
+ pref_zq = zq;
+ pref_weight = weight;
+ }
+ }
+ pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+ spin_unlock(&zcrypt_list_lock);
- zcrypt_device_get(zdev);
- get_device(&zdev->ap_dev->device);
- zdev->request_count++;
- __zcrypt_decrease_preference(zdev);
- if (try_module_get(zdev->ap_dev->drv->driver.owner)) {
- spin_unlock_bh(&zcrypt_device_lock);
- rc = zdev->ops->send_ep11_cprb(zdev, xcrb);
- spin_lock_bh(&zcrypt_device_lock);
- module_put(zdev->ap_dev->drv->driver.owner);
- } else {
- rc = -EAGAIN;
- }
- 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;
+ if (!pref_zq) {
+ rc = -ENODEV;
+ goto out_free;
}
- spin_unlock_bh(&zcrypt_device_lock);
- return -ENODEV;
+
+ qid = pref_zq->queue->qid;
+ rc = pref_zq->ops->send_ep11_cprb(pref_zq, xcrb, &ap_msg);
+
+ spin_lock(&zcrypt_list_lock);
+ zcrypt_drop_queue(pref_zc, pref_zq, weight);
+ spin_unlock(&zcrypt_list_lock);
+
+out_free:
+ kfree(targets);
+out:
+ trace_s390_zcrypt_rep(xcrb, func_code, rc,
+ AP_QID_CARD(qid), AP_QID_QUEUE(qid));
+ return rc;
}
static long zcrypt_rng(char *buffer)
{
- struct zcrypt_device *zdev;
- int rc;
+ struct zcrypt_card *zc, *pref_zc;
+ struct zcrypt_queue *zq, *pref_zq;
+ unsigned int weight, pref_weight;
+ unsigned int func_code;
+ struct ap_message ap_msg;
+ unsigned int domain;
+ int qid = 0, rc = -ENODEV;
+
+ trace_s390_zcrypt_req(buffer, TP_HWRNGCPRB);
- spin_lock_bh(&zcrypt_device_lock);
- list_for_each_entry(zdev, &zcrypt_device_list, list) {
- if (!zdev->online || !zdev->ops->rng)
+ rc = get_rng_fc(&ap_msg, &func_code, &domain);
+ if (rc)
+ goto out;
+
+ pref_zc = NULL;
+ pref_zq = NULL;
+ spin_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ /* Check for online CCA cards */
+ if (!zc->online || !(zc->card->functions & 0x10000000))
continue;
- zcrypt_device_get(zdev);
- get_device(&zdev->ap_dev->device);
- zdev->request_count++;
- __zcrypt_decrease_preference(zdev);
- if (try_module_get(zdev->ap_dev->drv->driver.owner)) {
- spin_unlock_bh(&zcrypt_device_lock);
- rc = zdev->ops->rng(zdev, buffer);
- spin_lock_bh(&zcrypt_device_lock);
- module_put(zdev->ap_dev->drv->driver.owner);
- } else
- rc = -EAGAIN;
- 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;
+ /* get weight index of the card device */
+ weight = zc->speed_rating[func_code];
+ if (zcrypt_card_compare(zc, pref_zc, weight, pref_weight))
+ continue;
+ for_each_zcrypt_queue(zq, zc) {
+ /* check if device is online and eligible */
+ if (!zq->online || !zq->ops->rng)
+ continue;
+ if (zcrypt_queue_compare(zq, pref_zq,
+ weight, pref_weight))
+ continue;
+ pref_zc = zc;
+ pref_zq = zq;
+ pref_weight = weight;
+ }
+ }
+ pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, weight);
+ spin_unlock(&zcrypt_list_lock);
+
+ if (!pref_zq)
+ return -ENODEV;
+
+ qid = pref_zq->queue->qid;
+ rc = pref_zq->ops->rng(pref_zq, buffer, &ap_msg);
+
+ spin_lock(&zcrypt_list_lock);
+ zcrypt_drop_queue(pref_zc, pref_zq, weight);
+ spin_unlock(&zcrypt_list_lock);
+
+out:
+ trace_s390_zcrypt_rep(buffer, func_code, rc,
+ AP_QID_CARD(qid), AP_QID_QUEUE(qid));
+ return rc;
+}
+
+static void zcrypt_device_status_mask(struct zcrypt_device_matrix *matrix)
+{
+ struct zcrypt_card *zc;
+ struct zcrypt_queue *zq;
+ struct zcrypt_device_status *stat;
+
+ memset(matrix, 0, sizeof(*matrix));
+ spin_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ for_each_zcrypt_queue(zq, zc) {
+ stat = matrix->device;
+ stat += AP_QID_CARD(zq->queue->qid) * MAX_ZDEV_DOMAINS;
+ stat += AP_QID_QUEUE(zq->queue->qid);
+ stat->hwtype = zc->card->ap_dev.device_type;
+ stat->functions = zc->card->functions >> 26;
+ stat->qid = zq->queue->qid;
+ stat->online = zq->online ? 0x01 : 0x00;
+ }
}
- spin_unlock_bh(&zcrypt_device_lock);
- return -ENODEV;
+ spin_unlock(&zcrypt_list_lock);
}
+EXPORT_SYMBOL(zcrypt_device_status_mask);
static void zcrypt_status_mask(char status[AP_DEVICES])
{
- struct zcrypt_device *zdev;
+ struct zcrypt_card *zc;
+ struct zcrypt_queue *zq;
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);
+ spin_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ for_each_zcrypt_queue(zq, zc) {
+ if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
+ continue;
+ status[AP_QID_CARD(zq->queue->qid)] =
+ zc->online ? zc->user_space_type : 0x0d;
+ }
+ }
+ spin_unlock(&zcrypt_list_lock);
}
static void zcrypt_qdepth_mask(char qdepth[AP_DEVICES])
{
- struct zcrypt_device *zdev;
+ struct zcrypt_card *zc;
+ struct zcrypt_queue *zq;
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_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ for_each_zcrypt_queue(zq, zc) {
+ if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
+ continue;
+ spin_lock(&zq->queue->lock);
+ qdepth[AP_QID_CARD(zq->queue->qid)] =
+ zq->queue->pendingq_count +
+ zq->queue->requestq_count;
+ spin_unlock(&zq->queue->lock);
+ }
}
- spin_unlock_bh(&zcrypt_device_lock);
+ spin_unlock(&zcrypt_list_lock);
}
static void zcrypt_perdev_reqcnt(int reqcnt[AP_DEVICES])
{
- struct zcrypt_device *zdev;
+ struct zcrypt_card *zc;
+ struct zcrypt_queue *zq;
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_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ for_each_zcrypt_queue(zq, zc) {
+ if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
+ continue;
+ spin_lock(&zq->queue->lock);
+ reqcnt[AP_QID_CARD(zq->queue->qid)] =
+ zq->queue->total_request_count;
+ spin_unlock(&zq->queue->lock);
+ }
}
- spin_unlock_bh(&zcrypt_device_lock);
+ spin_unlock(&zcrypt_list_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);
+ struct zcrypt_card *zc;
+ struct zcrypt_queue *zq;
+ int pendingq_count;
+
+ pendingq_count = 0;
+ spin_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ for_each_zcrypt_queue(zq, zc) {
+ if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
+ continue;
+ spin_lock(&zq->queue->lock);
+ pendingq_count += zq->queue->pendingq_count;
+ spin_unlock(&zq->queue->lock);
+ }
}
- spin_unlock_bh(&zcrypt_device_lock);
+ spin_unlock(&zcrypt_list_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);
+ struct zcrypt_card *zc;
+ struct zcrypt_queue *zq;
+ int requestq_count;
+
+ requestq_count = 0;
+ spin_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ for_each_zcrypt_queue(zq, zc) {
+ if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
+ continue;
+ spin_lock(&zq->queue->lock);
+ requestq_count += zq->queue->requestq_count;
+ spin_unlock(&zq->queue->lock);
+ }
}
- spin_unlock_bh(&zcrypt_device_lock);
+ spin_unlock(&zcrypt_list_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)
+ struct zcrypt_card *zc;
+ struct zcrypt_queue *zq;
+ int device_count;
+
+ device_count = 0;
+ spin_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ if (zc->card->id != type)
+ continue;
+ for_each_zcrypt_queue(zq, zc) {
+ if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
+ continue;
device_count++;
- spin_unlock_bh(&zcrypt_device_lock);
+ }
+ }
+ spin_unlock(&zcrypt_list_lock);
return device_count;
}
@@ -887,6 +868,25 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
return -EFAULT;
return rc;
}
+ case ZDEVICESTATUS: {
+ struct zcrypt_device_matrix *device_status;
+
+ device_status = kzalloc(sizeof(struct zcrypt_device_matrix),
+ GFP_KERNEL);
+ if (!device_status)
+ return -ENOMEM;
+
+ zcrypt_device_status_mask(device_status);
+
+ if (copy_to_user((char __user *) arg, device_status,
+ sizeof(struct zcrypt_device_matrix))) {
+ kfree(device_status);
+ return -EFAULT;
+ }
+
+ kfree(device_status);
+ return 0;
+ }
case Z90STAT_STATUS_MASK: {
char status[AP_DEVICES];
zcrypt_status_mask(status);
@@ -1249,29 +1249,36 @@ static int zcrypt_proc_open(struct inode *inode, struct file *file)
static void zcrypt_disable_card(int index)
{
- struct zcrypt_device *zdev;
+ struct zcrypt_card *zc;
+ struct zcrypt_queue *zq;
- 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_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ for_each_zcrypt_queue(zq, zc) {
+ if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
+ continue;
+ zq->online = 0;
+ ap_flush_queue(zq->queue);
}
- spin_unlock_bh(&zcrypt_device_lock);
+ }
+ spin_unlock(&zcrypt_list_lock);
}
static void zcrypt_enable_card(int index)
{
- struct zcrypt_device *zdev;
+ struct zcrypt_card *zc;
+ struct zcrypt_queue *zq;
- 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_lock(&zcrypt_list_lock);
+ for_each_zcrypt_card(zc) {
+ for_each_zcrypt_queue(zq, zc) {
+ if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index)
+ continue;
+ zq->online = 1;
+ ap_flush_queue(zq->queue);
}
- spin_unlock_bh(&zcrypt_device_lock);
+ }
+ spin_unlock(&zcrypt_list_lock);
}
static ssize_t zcrypt_proc_write(struct file *file, const char __user *buffer,
@@ -1369,7 +1376,7 @@ static struct hwrng zcrypt_rng_dev = {
.quality = 990,
};
-static int zcrypt_rng_device_add(void)
+int zcrypt_rng_device_add(void)
{
int rc = 0;
@@ -1399,7 +1406,7 @@ out:
return rc;
}
-static void zcrypt_rng_device_remove(void)
+void zcrypt_rng_device_remove(void)
{
mutex_lock(&zcrypt_rng_mutex);
zcrypt_rng_device_count--;
@@ -1412,24 +1419,19 @@ static void zcrypt_rng_device_remove(void)
int __init zcrypt_debug_init(void)
{
- debugfs_root = debugfs_create_dir("zcrypt", NULL);
-
- zcrypt_dbf_common = debug_register("zcrypt_common", 1, 1, 16);
- debug_register_view(zcrypt_dbf_common, &debug_hex_ascii_view);
- debug_set_level(zcrypt_dbf_common, DBF_ERR);
-
- zcrypt_dbf_devices = debug_register("zcrypt_devices", 1, 1, 16);
- debug_register_view(zcrypt_dbf_devices, &debug_hex_ascii_view);
- debug_set_level(zcrypt_dbf_devices, DBF_ERR);
+ zcrypt_dbf_root = debugfs_create_dir("zcrypt", NULL);
+ zcrypt_dbf_info = debug_register("zcrypt", 1, 1,
+ DBF_MAX_SPRINTF_ARGS * sizeof(long));
+ debug_register_view(zcrypt_dbf_info, &debug_sprintf_view);
+ debug_set_level(zcrypt_dbf_info, DBF_ERR);
return 0;
}
void zcrypt_debug_exit(void)
{
- debugfs_remove(debugfs_root);
- debug_unregister(zcrypt_dbf_common);
- debug_unregister(zcrypt_dbf_devices);
+ debugfs_remove(zcrypt_dbf_root);
+ debug_unregister(zcrypt_dbf_info);
}
/**
@@ -1453,12 +1455,15 @@ int __init zcrypt_api_init(void)
goto out;
/* Set up the proc file system */
- zcrypt_entry = proc_create("driver/z90crypt", 0644, NULL, &zcrypt_proc_fops);
+ zcrypt_entry = proc_create("driver/z90crypt", 0644, NULL,
+ &zcrypt_proc_fops);
if (!zcrypt_entry) {
rc = -ENOMEM;
goto out_misc;
}
+ zcrypt_msgtype6_init();
+ zcrypt_msgtype50_init();
return 0;
out_misc:
@@ -1472,10 +1477,12 @@ out:
*
* The module termination code.
*/
-void zcrypt_api_exit(void)
+void __exit zcrypt_api_exit(void)
{
remove_proc_entry("driver/z90crypt", NULL);
misc_deregister(&zcrypt_misc_device);
+ zcrypt_msgtype6_exit();
+ zcrypt_msgtype50_exit();
zcrypt_debug_exit();
}
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 38618f05ad92..274a59051534 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -84,57 +84,110 @@ struct ica_z90_status {
*/
#define ZCRYPT_RNG_BUFFER_SIZE 4096
-struct zcrypt_device;
+/*
+ * Identifier for Crypto Request Performance Index
+ */
+enum crypto_ops {
+ MEX_1K,
+ MEX_2K,
+ MEX_4K,
+ CRT_1K,
+ CRT_2K,
+ CRT_4K,
+ HWRNG,
+ SECKEY,
+ NUM_OPS
+};
+
+struct zcrypt_queue;
struct zcrypt_ops {
- long (*rsa_modexpo)(struct zcrypt_device *, struct ica_rsa_modexpo *);
- long (*rsa_modexpo_crt)(struct zcrypt_device *,
+ long (*rsa_modexpo)(struct zcrypt_queue *, struct ica_rsa_modexpo *);
+ long (*rsa_modexpo_crt)(struct zcrypt_queue *,
struct ica_rsa_modexpo_crt *);
- long (*send_cprb)(struct zcrypt_device *, struct ica_xcRB *);
- long (*send_ep11_cprb)(struct zcrypt_device *, struct ep11_urb *);
- long (*rng)(struct zcrypt_device *, char *);
+ long (*send_cprb)(struct zcrypt_queue *, struct ica_xcRB *,
+ struct ap_message *);
+ long (*send_ep11_cprb)(struct zcrypt_queue *, struct ep11_urb *,
+ struct ap_message *);
+ long (*rng)(struct zcrypt_queue *, char *, struct ap_message *);
struct list_head list; /* zcrypt ops list. */
struct module *owner;
int variant;
char name[128];
};
-struct zcrypt_device {
+struct zcrypt_card {
struct list_head list; /* Device list. */
- spinlock_t lock; /* Per device lock. */
+ struct list_head zqueues; /* List of zcrypt queues */
struct kref refcount; /* device refcounting */
- struct ap_device *ap_dev; /* The "real" ap device. */
- struct zcrypt_ops *ops; /* Crypto operations. */
+ struct ap_card *card; /* The "real" ap card device. */
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 max_exp_bit_length;
+ int speed_rating[NUM_OPS]; /* Speed idx of crypto ops. */
+ atomic_t load; /* Utilization of the crypto device */
int request_count; /* # current requests. */
+};
- struct ap_message reply; /* Per-device reply structure. */
- int max_exp_bit_length;
+struct zcrypt_queue {
+ struct list_head list; /* Device list. */
+ struct kref refcount; /* device refcounting */
+ struct zcrypt_card *zcard;
+ struct zcrypt_ops *ops; /* Crypto operations. */
+ struct ap_queue *queue; /* The "real" ap queue device. */
+ int online; /* User online/offline */
+
+ atomic_t load; /* Utilization of the crypto device */
- debug_info_t *dbf_area; /* debugging */
+ int request_count; /* # current requests. */
+
+ struct ap_message reply; /* Per-device reply structure. */
};
/* transport layer rescanning */
extern atomic_t zcrypt_rescan_req;
-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 *);
+extern spinlock_t zcrypt_list_lock;
+extern int zcrypt_device_count;
+extern struct list_head zcrypt_card_list;
+
+#define for_each_zcrypt_card(_zc) \
+ list_for_each_entry(_zc, &zcrypt_card_list, list)
+
+#define for_each_zcrypt_queue(_zq, _zc) \
+ list_for_each_entry(_zq, &(_zc)->zqueues, list)
+
+struct zcrypt_card *zcrypt_card_alloc(void);
+void zcrypt_card_free(struct zcrypt_card *);
+void zcrypt_card_get(struct zcrypt_card *);
+int zcrypt_card_put(struct zcrypt_card *);
+int zcrypt_card_register(struct zcrypt_card *);
+void zcrypt_card_unregister(struct zcrypt_card *);
+struct zcrypt_card *zcrypt_card_get_best(unsigned int *,
+ unsigned int, unsigned int);
+void zcrypt_card_put_best(struct zcrypt_card *, unsigned int);
+
+struct zcrypt_queue *zcrypt_queue_alloc(size_t);
+void zcrypt_queue_free(struct zcrypt_queue *);
+void zcrypt_queue_get(struct zcrypt_queue *);
+int zcrypt_queue_put(struct zcrypt_queue *);
+int zcrypt_queue_register(struct zcrypt_queue *);
+void zcrypt_queue_unregister(struct zcrypt_queue *);
+void zcrypt_queue_force_online(struct zcrypt_queue *, int);
+struct zcrypt_queue *zcrypt_queue_get_best(unsigned int, unsigned int);
+void zcrypt_queue_put_best(struct zcrypt_queue *, unsigned int);
+
+int zcrypt_rng_device_add(void);
+void zcrypt_rng_device_remove(void);
+
void zcrypt_msgtype_register(struct zcrypt_ops *);
void zcrypt_msgtype_unregister(struct zcrypt_ops *);
-struct zcrypt_ops *zcrypt_msgtype_request(unsigned char *, int);
-void zcrypt_msgtype_release(struct zcrypt_ops *);
+struct zcrypt_ops *zcrypt_msgtype(unsigned char *, int);
int zcrypt_api_init(void);
void zcrypt_api_exit(void);
diff --git a/drivers/s390/crypto/zcrypt_card.c b/drivers/s390/crypto/zcrypt_card.c
new file mode 100644
index 000000000000..53436ea52230
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_card.c
@@ -0,0 +1,187 @@
+/*
+ * zcrypt 2.1.0
+ *
+ * Copyright IBM Corp. 2001, 2012
+ * 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>
+ * MSGTYPE restruct: Holger Dengler <hd@linux.vnet.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.
+ */
+
+#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/seq_file.h>
+#include <linux/compat.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+#include <linux/hw_random.h>
+#include <linux/debugfs.h>
+#include <asm/debug.h>
+
+#include "zcrypt_debug.h"
+#include "zcrypt_api.h"
+
+#include "zcrypt_msgtype6.h"
+#include "zcrypt_msgtype50.h"
+
+/*
+ * Device attributes common for all crypto card devices.
+ */
+
+static ssize_t zcrypt_card_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct zcrypt_card *zc = to_ap_card(dev)->private;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", zc->type_string);
+}
+
+static DEVICE_ATTR(type, 0444, zcrypt_card_type_show, NULL);
+
+static ssize_t zcrypt_card_online_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct zcrypt_card *zc = to_ap_card(dev)->private;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", zc->online);
+}
+
+static ssize_t zcrypt_card_online_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct zcrypt_card *zc = to_ap_card(dev)->private;
+ struct zcrypt_queue *zq;
+ int online, id;
+
+ if (sscanf(buf, "%d\n", &online) != 1 || online < 0 || online > 1)
+ return -EINVAL;
+
+ zc->online = online;
+ id = zc->card->id;
+
+ ZCRYPT_DBF(DBF_INFO, "card=%02x online=%d\n", id, online);
+
+ spin_lock(&zcrypt_list_lock);
+ list_for_each_entry(zq, &zc->zqueues, list)
+ zcrypt_queue_force_online(zq, online);
+ spin_unlock(&zcrypt_list_lock);
+ return count;
+}
+
+static DEVICE_ATTR(online, 0644, zcrypt_card_online_show,
+ zcrypt_card_online_store);
+
+static struct attribute *zcrypt_card_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_online.attr,
+ NULL,
+};
+
+static struct attribute_group zcrypt_card_attr_group = {
+ .attrs = zcrypt_card_attrs,
+};
+
+struct zcrypt_card *zcrypt_card_alloc(void)
+{
+ struct zcrypt_card *zc;
+
+ zc = kzalloc(sizeof(struct zcrypt_card), GFP_KERNEL);
+ if (!zc)
+ return NULL;
+ INIT_LIST_HEAD(&zc->list);
+ INIT_LIST_HEAD(&zc->zqueues);
+ kref_init(&zc->refcount);
+ return zc;
+}
+EXPORT_SYMBOL(zcrypt_card_alloc);
+
+void zcrypt_card_free(struct zcrypt_card *zc)
+{
+ kfree(zc);
+}
+EXPORT_SYMBOL(zcrypt_card_free);
+
+static void zcrypt_card_release(struct kref *kref)
+{
+ struct zcrypt_card *zdev =
+ container_of(kref, struct zcrypt_card, refcount);
+ zcrypt_card_free(zdev);
+}
+
+void zcrypt_card_get(struct zcrypt_card *zc)
+{
+ kref_get(&zc->refcount);
+}
+EXPORT_SYMBOL(zcrypt_card_get);
+
+int zcrypt_card_put(struct zcrypt_card *zc)
+{
+ return kref_put(&zc->refcount, zcrypt_card_release);
+}
+EXPORT_SYMBOL(zcrypt_card_put);
+
+/**
+ * zcrypt_card_register() - Register a crypto card device.
+ * @zc: Pointer to a crypto card device
+ *
+ * Register a crypto card device. Returns 0 if successful.
+ */
+int zcrypt_card_register(struct zcrypt_card *zc)
+{
+ int rc;
+
+ rc = sysfs_create_group(&zc->card->ap_dev.device.kobj,
+ &zcrypt_card_attr_group);
+ if (rc)
+ return rc;
+
+ spin_lock(&zcrypt_list_lock);
+ list_add_tail(&zc->list, &zcrypt_card_list);
+ spin_unlock(&zcrypt_list_lock);
+
+ zc->online = 1;
+
+ ZCRYPT_DBF(DBF_INFO, "card=%02x register online=1\n", zc->card->id);
+
+ return rc;
+}
+EXPORT_SYMBOL(zcrypt_card_register);
+
+/**
+ * zcrypt_card_unregister(): Unregister a crypto card device.
+ * @zc: Pointer to crypto card device
+ *
+ * Unregister a crypto card device.
+ */
+void zcrypt_card_unregister(struct zcrypt_card *zc)
+{
+ ZCRYPT_DBF(DBF_INFO, "card=%02x unregister\n", zc->card->id);
+
+ spin_lock(&zcrypt_list_lock);
+ list_del_init(&zc->list);
+ spin_unlock(&zcrypt_list_lock);
+ sysfs_remove_group(&zc->card->ap_dev.device.kobj,
+ &zcrypt_card_attr_group);
+}
+EXPORT_SYMBOL(zcrypt_card_unregister);
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index 15104aaa075a..b97c5d5ee5a4 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -30,7 +30,8 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/atomic.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
+#include <linux/mod_devicetable.h>
#include "ap_bus.h"
#include "zcrypt_api.h"
@@ -43,9 +44,6 @@
#define CEX3A_MIN_MOD_SIZE CEX2A_MIN_MOD_SIZE
#define CEX3A_MAX_MOD_SIZE 512 /* 4096 bits */
-#define CEX2A_SPEED_RATING 970
-#define CEX3A_SPEED_RATING 900 /* Fixme: Needs finetuning */
-
#define CEX2A_MAX_MESSAGE_SIZE 0x390 /* sizeof(struct type50_crb2_msg) */
#define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */
@@ -57,107 +55,195 @@
#define CEX2A_CLEANUP_TIME (15*HZ)
#define CEX3A_CLEANUP_TIME CEX2A_CLEANUP_TIME
-static struct ap_device_id zcrypt_cex2a_ids[] = {
- { AP_DEVICE(AP_DEVICE_TYPE_CEX2A) },
- { AP_DEVICE(AP_DEVICE_TYPE_CEX3A) },
- { /* end of list */ },
-};
-
-MODULE_DEVICE_TABLE(ap, zcrypt_cex2a_ids);
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("CEX2A Cryptographic Coprocessor device driver, " \
"Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
-static int zcrypt_cex2a_probe(struct ap_device *ap_dev);
-static void zcrypt_cex2a_remove(struct ap_device *ap_dev);
+static struct ap_device_id zcrypt_cex2a_card_ids[] = {
+ { .dev_type = AP_DEVICE_TYPE_CEX2A,
+ .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
+ { .dev_type = AP_DEVICE_TYPE_CEX3A,
+ .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
+ { /* end of list */ },
+};
+
+MODULE_DEVICE_TABLE(ap, zcrypt_cex2a_card_ids);
-static struct ap_driver zcrypt_cex2a_driver = {
- .probe = zcrypt_cex2a_probe,
- .remove = zcrypt_cex2a_remove,
- .ids = zcrypt_cex2a_ids,
- .request_timeout = CEX2A_CLEANUP_TIME,
+static struct ap_device_id zcrypt_cex2a_queue_ids[] = {
+ { .dev_type = AP_DEVICE_TYPE_CEX2A,
+ .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+ { .dev_type = AP_DEVICE_TYPE_CEX3A,
+ .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+ { /* end of list */ },
};
+MODULE_DEVICE_TABLE(ap, zcrypt_cex2a_queue_ids);
+
/**
- * Probe function for CEX2A cards. It always accepts the AP device
- * since the bus_match already checked the hardware type.
+ * Probe function for CEX2A card devices. It always accepts the AP device
+ * since the bus_match already checked the card type.
* @ap_dev: pointer to the AP device.
*/
-static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
+static int zcrypt_cex2a_card_probe(struct ap_device *ap_dev)
{
- struct zcrypt_device *zdev = NULL;
+ /*
+ * Normalized speed ratings per crypto adapter
+ * MEX_1k, MEX_2k, MEX_4k, CRT_1k, CRT_2k, CRT_4k, RNG, SECKEY
+ */
+ static const int CEX2A_SPEED_IDX[] = {
+ 800, 1000, 2000, 900, 1200, 2400, 0, 0};
+ static const int CEX3A_SPEED_IDX[] = {
+ 400, 500, 1000, 450, 550, 1200, 0, 0};
+
+ struct ap_card *ac = to_ap_card(&ap_dev->device);
+ struct zcrypt_card *zc;
int rc = 0;
+ zc = zcrypt_card_alloc();
+ if (!zc)
+ return -ENOMEM;
+ zc->card = ac;
+ ac->private = zc;
+
+ if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX2A) {
+ zc->min_mod_size = CEX2A_MIN_MOD_SIZE;
+ zc->max_mod_size = CEX2A_MAX_MOD_SIZE;
+ memcpy(zc->speed_rating, CEX2A_SPEED_IDX,
+ sizeof(CEX2A_SPEED_IDX));
+ zc->max_exp_bit_length = CEX2A_MAX_MOD_SIZE;
+ zc->type_string = "CEX2A";
+ zc->user_space_type = ZCRYPT_CEX2A;
+ } else if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX3A) {
+ zc->min_mod_size = CEX2A_MIN_MOD_SIZE;
+ zc->max_mod_size = CEX2A_MAX_MOD_SIZE;
+ zc->max_exp_bit_length = CEX2A_MAX_MOD_SIZE;
+ if (ap_test_bit(&ac->functions, AP_FUNC_MEX4K) &&
+ ap_test_bit(&ac->functions, AP_FUNC_CRT4K)) {
+ zc->max_mod_size = CEX3A_MAX_MOD_SIZE;
+ zc->max_exp_bit_length = CEX3A_MAX_MOD_SIZE;
+ }
+ memcpy(zc->speed_rating, CEX3A_SPEED_IDX,
+ sizeof(CEX3A_SPEED_IDX));
+ zc->type_string = "CEX3A";
+ zc->user_space_type = ZCRYPT_CEX3A;
+ } else {
+ zcrypt_card_free(zc);
+ return -ENODEV;
+ }
+ zc->online = 1;
+
+ rc = zcrypt_card_register(zc);
+ if (rc) {
+ ac->private = NULL;
+ zcrypt_card_free(zc);
+ }
+
+ return rc;
+}
+
+/**
+ * This is called to remove the CEX2A card driver information
+ * if an AP card device is removed.
+ */
+static void zcrypt_cex2a_card_remove(struct ap_device *ap_dev)
+{
+ struct zcrypt_card *zc = to_ap_card(&ap_dev->device)->private;
+
+ if (zc)
+ zcrypt_card_unregister(zc);
+}
+
+static struct ap_driver zcrypt_cex2a_card_driver = {
+ .probe = zcrypt_cex2a_card_probe,
+ .remove = zcrypt_cex2a_card_remove,
+ .ids = zcrypt_cex2a_card_ids,
+};
+
+/**
+ * Probe function for CEX2A queue devices. It always accepts the AP device
+ * since the bus_match already checked the queue type.
+ * @ap_dev: pointer to the AP device.
+ */
+static int zcrypt_cex2a_queue_probe(struct ap_device *ap_dev)
+{
+ struct ap_queue *aq = to_ap_queue(&ap_dev->device);
+ struct zcrypt_queue *zq = NULL;
+ int rc;
+
switch (ap_dev->device_type) {
case AP_DEVICE_TYPE_CEX2A:
- zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE);
- if (!zdev)
+ zq = zcrypt_queue_alloc(CEX2A_MAX_RESPONSE_SIZE);
+ if (!zq)
return -ENOMEM;
- 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;
- zdev->max_exp_bit_length = CEX2A_MAX_MOD_SIZE;
break;
case AP_DEVICE_TYPE_CEX3A:
- zdev = zcrypt_device_alloc(CEX3A_MAX_RESPONSE_SIZE);
- if (!zdev)
+ zq = zcrypt_queue_alloc(CEX3A_MAX_RESPONSE_SIZE);
+ if (!zq)
return -ENOMEM;
- zdev->user_space_type = ZCRYPT_CEX3A;
- zdev->type_string = "CEX3A";
- zdev->min_mod_size = CEX2A_MIN_MOD_SIZE;
- zdev->max_mod_size = CEX2A_MAX_MOD_SIZE;
- zdev->max_exp_bit_length = CEX2A_MAX_MOD_SIZE;
- if (ap_test_bit(&ap_dev->functions, AP_FUNC_MEX4K) &&
- ap_test_bit(&ap_dev->functions, AP_FUNC_CRT4K)) {
- zdev->max_mod_size = CEX3A_MAX_MOD_SIZE;
- zdev->max_exp_bit_length = CEX3A_MAX_MOD_SIZE;
- }
- zdev->short_crt = 1;
- zdev->speed_rating = CEX3A_SPEED_RATING;
break;
}
- if (!zdev)
+ if (!zq)
return -ENODEV;
- zdev->ops = zcrypt_msgtype_request(MSGTYPE50_NAME,
- MSGTYPE50_VARIANT_DEFAULT);
- zdev->ap_dev = ap_dev;
- zdev->online = 1;
- ap_device_init_reply(ap_dev, &zdev->reply);
- ap_dev->private = zdev;
- rc = zcrypt_device_register(zdev);
+ zq->ops = zcrypt_msgtype(MSGTYPE50_NAME, MSGTYPE50_VARIANT_DEFAULT);
+ zq->queue = aq;
+ zq->online = 1;
+ atomic_set(&zq->load, 0);
+ ap_queue_init_reply(aq, &zq->reply);
+ aq->request_timeout = CEX2A_CLEANUP_TIME,
+ aq->private = zq;
+ rc = zcrypt_queue_register(zq);
if (rc) {
- ap_dev->private = NULL;
- zcrypt_msgtype_release(zdev->ops);
- zcrypt_device_free(zdev);
+ aq->private = NULL;
+ zcrypt_queue_free(zq);
}
+
return rc;
}
/**
- * This is called to remove the extended CEX2A driver information
- * if an AP device is removed.
+ * This is called to remove the CEX2A queue driver information
+ * if an AP queue device is removed.
*/
-static void zcrypt_cex2a_remove(struct ap_device *ap_dev)
+static void zcrypt_cex2a_queue_remove(struct ap_device *ap_dev)
{
- struct zcrypt_device *zdev = ap_dev->private;
- struct zcrypt_ops *zops = zdev->ops;
+ struct ap_queue *aq = to_ap_queue(&ap_dev->device);
+ struct zcrypt_queue *zq = aq->private;
- zcrypt_device_unregister(zdev);
- zcrypt_msgtype_release(zops);
+ ap_queue_remove(aq);
+ if (zq)
+ zcrypt_queue_unregister(zq);
}
+static struct ap_driver zcrypt_cex2a_queue_driver = {
+ .probe = zcrypt_cex2a_queue_probe,
+ .remove = zcrypt_cex2a_queue_remove,
+ .suspend = ap_queue_suspend,
+ .resume = ap_queue_resume,
+ .ids = zcrypt_cex2a_queue_ids,
+};
+
int __init zcrypt_cex2a_init(void)
{
- return ap_driver_register(&zcrypt_cex2a_driver, THIS_MODULE, "cex2a");
+ int rc;
+
+ rc = ap_driver_register(&zcrypt_cex2a_card_driver,
+ THIS_MODULE, "cex2acard");
+ if (rc)
+ return rc;
+
+ rc = ap_driver_register(&zcrypt_cex2a_queue_driver,
+ THIS_MODULE, "cex2aqueue");
+ if (rc)
+ ap_driver_unregister(&zcrypt_cex2a_card_driver);
+
+ return rc;
}
void __exit zcrypt_cex2a_exit(void)
{
- ap_driver_unregister(&zcrypt_cex2a_driver);
+ ap_driver_unregister(&zcrypt_cex2a_queue_driver);
+ ap_driver_unregister(&zcrypt_cex2a_card_driver);
}
module_init(zcrypt_cex2a_init);
diff --git a/drivers/s390/crypto/zcrypt_cex4.c b/drivers/s390/crypto/zcrypt_cex4.c
index ccb2e78ebf0e..4e91163d70a6 100644
--- a/drivers/s390/crypto/zcrypt_cex4.c
+++ b/drivers/s390/crypto/zcrypt_cex4.c
@@ -9,6 +9,7 @@
#include <linux/err.h>
#include <linux/atomic.h>
#include <linux/uaccess.h>
+#include <linux/mod_devicetable.h>
#include "ap_bus.h"
#include "zcrypt_api.h"
@@ -24,13 +25,6 @@
#define CEX4C_MIN_MOD_SIZE 16 /* 256 bits */
#define CEX4C_MAX_MOD_SIZE 512 /* 4096 bits */
-#define CEX4A_SPEED_RATING 900 /* TODO new card, new speed rating */
-#define CEX4C_SPEED_RATING 6500 /* TODO new card, new speed rating */
-#define CEX4P_SPEED_RATING 7000 /* TODO new card, new speed rating */
-#define CEX5A_SPEED_RATING 450 /* TODO new card, new speed rating */
-#define CEX5C_SPEED_RATING 3250 /* TODO new card, new speed rating */
-#define CEX5P_SPEED_RATING 3500 /* TODO new card, new speed rating */
-
#define CEX4A_MAX_MESSAGE_SIZE MSGTYPE50_CRB3_MAX_MSG_SIZE
#define CEX4C_MAX_MESSAGE_SIZE MSGTYPE06_MAX_MSG_SIZE
@@ -41,147 +35,246 @@
*/
#define CEX4_CLEANUP_TIME (900*HZ)
-static struct ap_device_id zcrypt_cex4_ids[] = {
- { AP_DEVICE(AP_DEVICE_TYPE_CEX4) },
- { AP_DEVICE(AP_DEVICE_TYPE_CEX5) },
- { /* end of list */ },
-};
-
-MODULE_DEVICE_TABLE(ap, zcrypt_cex4_ids);
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("CEX4 Cryptographic Card device driver, " \
"Copyright IBM Corp. 2012");
MODULE_LICENSE("GPL");
-static int zcrypt_cex4_probe(struct ap_device *ap_dev);
-static void zcrypt_cex4_remove(struct ap_device *ap_dev);
+static struct ap_device_id zcrypt_cex4_card_ids[] = {
+ { .dev_type = AP_DEVICE_TYPE_CEX4,
+ .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
+ { .dev_type = AP_DEVICE_TYPE_CEX5,
+ .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
+ { /* end of list */ },
+};
-static struct ap_driver zcrypt_cex4_driver = {
- .probe = zcrypt_cex4_probe,
- .remove = zcrypt_cex4_remove,
- .ids = zcrypt_cex4_ids,
- .request_timeout = CEX4_CLEANUP_TIME,
+MODULE_DEVICE_TABLE(ap, zcrypt_cex4_card_ids);
+
+static struct ap_device_id zcrypt_cex4_queue_ids[] = {
+ { .dev_type = AP_DEVICE_TYPE_CEX4,
+ .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+ { .dev_type = AP_DEVICE_TYPE_CEX5,
+ .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+ { /* end of list */ },
};
+MODULE_DEVICE_TABLE(ap, zcrypt_cex4_queue_ids);
+
/**
- * Probe function for CEX4 cards. It always accepts the AP device
+ * Probe function for CEX4 card device. 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_cex4_probe(struct ap_device *ap_dev)
+static int zcrypt_cex4_card_probe(struct ap_device *ap_dev)
{
- struct zcrypt_device *zdev = NULL;
+ /*
+ * Normalized speed ratings per crypto adapter
+ * MEX_1k, MEX_2k, MEX_4k, CRT_1k, CRT_2k, CRT_4k, RNG, SECKEY
+ */
+ static const int CEX4A_SPEED_IDX[] = {
+ 5, 6, 59, 20, 115, 581, 0, 0};
+ static const int CEX5A_SPEED_IDX[] = {
+ 3, 3, 6, 8, 32, 218, 0, 0};
+ static const int CEX4C_SPEED_IDX[] = {
+ 24, 25, 82, 41, 138, 1111, 79, 8};
+ static const int CEX5C_SPEED_IDX[] = {
+ 10, 14, 23, 17, 45, 242, 63, 4};
+ static const int CEX4P_SPEED_IDX[] = {
+ 142, 198, 1852, 203, 331, 1563, 0, 8};
+ static const int CEX5P_SPEED_IDX[] = {
+ 49, 67, 131, 52, 85, 287, 0, 4};
+
+ struct ap_card *ac = to_ap_card(&ap_dev->device);
+ struct zcrypt_card *zc;
int rc = 0;
- switch (ap_dev->device_type) {
- case AP_DEVICE_TYPE_CEX4:
- case AP_DEVICE_TYPE_CEX5:
- if (ap_test_bit(&ap_dev->functions, AP_FUNC_ACCEL)) {
- zdev = zcrypt_device_alloc(CEX4A_MAX_MESSAGE_SIZE);
- if (!zdev)
- return -ENOMEM;
- if (ap_dev->device_type == AP_DEVICE_TYPE_CEX4) {
- zdev->type_string = "CEX4A";
- zdev->speed_rating = CEX4A_SPEED_RATING;
- } else {
- zdev->type_string = "CEX5A";
- zdev->speed_rating = CEX5A_SPEED_RATING;
- }
- zdev->user_space_type = ZCRYPT_CEX3A;
- zdev->min_mod_size = CEX4A_MIN_MOD_SIZE;
- if (ap_test_bit(&ap_dev->functions, AP_FUNC_MEX4K) &&
- ap_test_bit(&ap_dev->functions, AP_FUNC_CRT4K)) {
- zdev->max_mod_size =
- CEX4A_MAX_MOD_SIZE_4K;
- zdev->max_exp_bit_length =
- CEX4A_MAX_MOD_SIZE_4K;
- } else {
- zdev->max_mod_size =
- CEX4A_MAX_MOD_SIZE_2K;
- zdev->max_exp_bit_length =
- CEX4A_MAX_MOD_SIZE_2K;
- }
- zdev->short_crt = 1;
- zdev->ops = zcrypt_msgtype_request(MSGTYPE50_NAME,
- MSGTYPE50_VARIANT_DEFAULT);
- } else if (ap_test_bit(&ap_dev->functions, AP_FUNC_COPRO)) {
- zdev = zcrypt_device_alloc(CEX4C_MAX_MESSAGE_SIZE);
- if (!zdev)
- return -ENOMEM;
- if (ap_dev->device_type == AP_DEVICE_TYPE_CEX4) {
- zdev->type_string = "CEX4C";
- zdev->speed_rating = CEX4C_SPEED_RATING;
- } else {
- zdev->type_string = "CEX5C";
- zdev->speed_rating = CEX5C_SPEED_RATING;
- }
- zdev->user_space_type = ZCRYPT_CEX3C;
- zdev->min_mod_size = CEX4C_MIN_MOD_SIZE;
- zdev->max_mod_size = CEX4C_MAX_MOD_SIZE;
- zdev->max_exp_bit_length = CEX4C_MAX_MOD_SIZE;
- zdev->short_crt = 0;
- zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
- MSGTYPE06_VARIANT_DEFAULT);
- } else if (ap_test_bit(&ap_dev->functions, AP_FUNC_EP11)) {
- zdev = zcrypt_device_alloc(CEX4C_MAX_MESSAGE_SIZE);
- if (!zdev)
- return -ENOMEM;
- if (ap_dev->device_type == AP_DEVICE_TYPE_CEX4) {
- zdev->type_string = "CEX4P";
- zdev->speed_rating = CEX4P_SPEED_RATING;
- } else {
- zdev->type_string = "CEX5P";
- zdev->speed_rating = CEX5P_SPEED_RATING;
- }
- zdev->user_space_type = ZCRYPT_CEX4;
- zdev->min_mod_size = CEX4C_MIN_MOD_SIZE;
- zdev->max_mod_size = CEX4C_MAX_MOD_SIZE;
- zdev->max_exp_bit_length = CEX4C_MAX_MOD_SIZE;
- zdev->short_crt = 0;
- zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
- MSGTYPE06_VARIANT_EP11);
+ zc = zcrypt_card_alloc();
+ if (!zc)
+ return -ENOMEM;
+ zc->card = ac;
+ ac->private = zc;
+ if (ap_test_bit(&ac->functions, AP_FUNC_ACCEL)) {
+ if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX4) {
+ zc->type_string = "CEX4A";
+ zc->user_space_type = ZCRYPT_CEX4;
+ memcpy(zc->speed_rating, CEX4A_SPEED_IDX,
+ sizeof(CEX4A_SPEED_IDX));
+ } else {
+ zc->type_string = "CEX5A";
+ zc->user_space_type = ZCRYPT_CEX5;
+ memcpy(zc->speed_rating, CEX5A_SPEED_IDX,
+ sizeof(CEX5A_SPEED_IDX));
}
- break;
- }
- if (!zdev)
+ zc->min_mod_size = CEX4A_MIN_MOD_SIZE;
+ if (ap_test_bit(&ac->functions, AP_FUNC_MEX4K) &&
+ ap_test_bit(&ac->functions, AP_FUNC_CRT4K)) {
+ zc->max_mod_size = CEX4A_MAX_MOD_SIZE_4K;
+ zc->max_exp_bit_length =
+ CEX4A_MAX_MOD_SIZE_4K;
+ } else {
+ zc->max_mod_size = CEX4A_MAX_MOD_SIZE_2K;
+ zc->max_exp_bit_length =
+ CEX4A_MAX_MOD_SIZE_2K;
+ }
+ } else if (ap_test_bit(&ac->functions, AP_FUNC_COPRO)) {
+ if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX4) {
+ zc->type_string = "CEX4C";
+ /* wrong user space type, must be CEX4
+ * just keep it for cca compatibility
+ */
+ zc->user_space_type = ZCRYPT_CEX3C;
+ memcpy(zc->speed_rating, CEX4C_SPEED_IDX,
+ sizeof(CEX4C_SPEED_IDX));
+ } else {
+ zc->type_string = "CEX5C";
+ /* wrong user space type, must be CEX5
+ * just keep it for cca compatibility
+ */
+ zc->user_space_type = ZCRYPT_CEX3C;
+ memcpy(zc->speed_rating, CEX5C_SPEED_IDX,
+ sizeof(CEX5C_SPEED_IDX));
+ }
+ zc->min_mod_size = CEX4C_MIN_MOD_SIZE;
+ zc->max_mod_size = CEX4C_MAX_MOD_SIZE;
+ zc->max_exp_bit_length = CEX4C_MAX_MOD_SIZE;
+ } else if (ap_test_bit(&ac->functions, AP_FUNC_EP11)) {
+ if (ac->ap_dev.device_type == AP_DEVICE_TYPE_CEX4) {
+ zc->type_string = "CEX4P";
+ zc->user_space_type = ZCRYPT_CEX4;
+ memcpy(zc->speed_rating, CEX4P_SPEED_IDX,
+ sizeof(CEX4P_SPEED_IDX));
+ } else {
+ zc->type_string = "CEX5P";
+ zc->user_space_type = ZCRYPT_CEX5;
+ memcpy(zc->speed_rating, CEX5P_SPEED_IDX,
+ sizeof(CEX5P_SPEED_IDX));
+ }
+ zc->min_mod_size = CEX4C_MIN_MOD_SIZE;
+ zc->max_mod_size = CEX4C_MAX_MOD_SIZE;
+ zc->max_exp_bit_length = CEX4C_MAX_MOD_SIZE;
+ } else {
+ zcrypt_card_free(zc);
return -ENODEV;
- zdev->ap_dev = ap_dev;
- zdev->online = 1;
- ap_device_init_reply(ap_dev, &zdev->reply);
- ap_dev->private = zdev;
- rc = zcrypt_device_register(zdev);
+ }
+ zc->online = 1;
+
+ rc = zcrypt_card_register(zc);
if (rc) {
- zcrypt_msgtype_release(zdev->ops);
- ap_dev->private = NULL;
- zcrypt_device_free(zdev);
+ ac->private = NULL;
+ zcrypt_card_free(zc);
}
+
return rc;
}
/**
- * This is called to remove the extended CEX4 driver information
- * if an AP device is removed.
+ * This is called to remove the CEX4 card driver information
+ * if an AP card device is removed.
+ */
+static void zcrypt_cex4_card_remove(struct ap_device *ap_dev)
+{
+ struct zcrypt_card *zc = to_ap_card(&ap_dev->device)->private;
+
+ if (zc)
+ zcrypt_card_unregister(zc);
+}
+
+static struct ap_driver zcrypt_cex4_card_driver = {
+ .probe = zcrypt_cex4_card_probe,
+ .remove = zcrypt_cex4_card_remove,
+ .ids = zcrypt_cex4_card_ids,
+};
+
+/**
+ * Probe function for CEX4 queue device. It always accepts the AP device
+ * since the bus_match already checked the hardware type.
+ * @ap_dev: pointer to the AP device.
*/
-static void zcrypt_cex4_remove(struct ap_device *ap_dev)
+static int zcrypt_cex4_queue_probe(struct ap_device *ap_dev)
{
- struct zcrypt_device *zdev = ap_dev->private;
- struct zcrypt_ops *zops;
+ struct ap_queue *aq = to_ap_queue(&ap_dev->device);
+ struct zcrypt_queue *zq;
+ int rc;
- if (zdev) {
- zops = zdev->ops;
- zcrypt_device_unregister(zdev);
- zcrypt_msgtype_release(zops);
+ if (ap_test_bit(&aq->card->functions, AP_FUNC_ACCEL)) {
+ zq = zcrypt_queue_alloc(CEX4A_MAX_MESSAGE_SIZE);
+ if (!zq)
+ return -ENOMEM;
+ zq->ops = zcrypt_msgtype(MSGTYPE50_NAME,
+ MSGTYPE50_VARIANT_DEFAULT);
+ } else if (ap_test_bit(&aq->card->functions, AP_FUNC_COPRO)) {
+ zq = zcrypt_queue_alloc(CEX4C_MAX_MESSAGE_SIZE);
+ if (!zq)
+ return -ENOMEM;
+ zq->ops = zcrypt_msgtype(MSGTYPE06_NAME,
+ MSGTYPE06_VARIANT_DEFAULT);
+ } else if (ap_test_bit(&aq->card->functions, AP_FUNC_EP11)) {
+ zq = zcrypt_queue_alloc(CEX4C_MAX_MESSAGE_SIZE);
+ if (!zq)
+ return -ENOMEM;
+ zq->ops = zcrypt_msgtype(MSGTYPE06_NAME,
+ MSGTYPE06_VARIANT_EP11);
+ } else {
+ return -ENODEV;
}
+ zq->queue = aq;
+ zq->online = 1;
+ atomic_set(&zq->load, 0);
+ ap_queue_init_reply(aq, &zq->reply);
+ aq->request_timeout = CEX4_CLEANUP_TIME,
+ aq->private = zq;
+ rc = zcrypt_queue_register(zq);
+ if (rc) {
+ aq->private = NULL;
+ zcrypt_queue_free(zq);
+ }
+
+ return rc;
+}
+
+/**
+ * This is called to remove the CEX4 queue driver information
+ * if an AP queue device is removed.
+ */
+static void zcrypt_cex4_queue_remove(struct ap_device *ap_dev)
+{
+ struct ap_queue *aq = to_ap_queue(&ap_dev->device);
+ struct zcrypt_queue *zq = aq->private;
+
+ ap_queue_remove(aq);
+ if (zq)
+ zcrypt_queue_unregister(zq);
}
+static struct ap_driver zcrypt_cex4_queue_driver = {
+ .probe = zcrypt_cex4_queue_probe,
+ .remove = zcrypt_cex4_queue_remove,
+ .suspend = ap_queue_suspend,
+ .resume = ap_queue_resume,
+ .ids = zcrypt_cex4_queue_ids,
+};
+
int __init zcrypt_cex4_init(void)
{
- return ap_driver_register(&zcrypt_cex4_driver, THIS_MODULE, "cex4");
+ int rc;
+
+ rc = ap_driver_register(&zcrypt_cex4_card_driver,
+ THIS_MODULE, "cex4card");
+ if (rc)
+ return rc;
+
+ rc = ap_driver_register(&zcrypt_cex4_queue_driver,
+ THIS_MODULE, "cex4queue");
+ if (rc)
+ ap_driver_unregister(&zcrypt_cex4_card_driver);
+
+ return rc;
}
void __exit zcrypt_cex4_exit(void)
{
- ap_driver_unregister(&zcrypt_cex4_driver);
+ ap_driver_unregister(&zcrypt_cex4_queue_driver);
+ ap_driver_unregister(&zcrypt_cex4_card_driver);
}
module_init(zcrypt_cex4_init);
diff --git a/drivers/s390/crypto/zcrypt_debug.h b/drivers/s390/crypto/zcrypt_debug.h
index 28d9349de1ad..13e38defb6b8 100644
--- a/drivers/s390/crypto/zcrypt_debug.h
+++ b/drivers/s390/crypto/zcrypt_debug.h
@@ -1,51 +1,27 @@
/*
- * Copyright IBM Corp. 2012
+ * Copyright IBM Corp. 2016
* Author(s): Holger Dengler (hd@linux.vnet.ibm.com)
+ * Harald Freudenberger <freude@de.ibm.com>
*/
#ifndef ZCRYPT_DEBUG_H
#define ZCRYPT_DEBUG_H
#include <asm/debug.h>
-#include "zcrypt_api.h"
-/* that gives us 15 characters in the text event views */
-#define ZCRYPT_DBF_LEN 16
-
-#define DBF_ERR 3 /* error conditions */
-#define DBF_WARN 4 /* warning conditions */
-#define DBF_INFO 6 /* informational */
+#define DBF_ERR 3 /* error conditions */
+#define DBF_WARN 4 /* warning conditions */
+#define DBF_INFO 5 /* informational */
+#define DBF_DEBUG 6 /* for debugging only */
+#define RC2ERR(rc) ((rc) ? DBF_ERR : DBF_INFO)
#define RC2WARN(rc) ((rc) ? DBF_WARN : DBF_INFO)
-#define ZCRYPT_DBF_COMMON(level, text...) \
- do { \
- if (debug_level_enabled(zcrypt_dbf_common, level)) { \
- char debug_buffer[ZCRYPT_DBF_LEN]; \
- snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
- debug_text_event(zcrypt_dbf_common, level, \
- debug_buffer); \
- } \
- } while (0)
-
-#define ZCRYPT_DBF_DEVICES(level, text...) \
- do { \
- if (debug_level_enabled(zcrypt_dbf_devices, level)) { \
- char debug_buffer[ZCRYPT_DBF_LEN]; \
- snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
- debug_text_event(zcrypt_dbf_devices, level, \
- debug_buffer); \
- } \
- } while (0)
-
-#define ZCRYPT_DBF_DEV(level, device, text...) \
- do { \
- if (debug_level_enabled(device->dbf_area, level)) { \
- char debug_buffer[ZCRYPT_DBF_LEN]; \
- snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
- debug_text_event(device->dbf_area, level, \
- debug_buffer); \
- } \
- } while (0)
+#define DBF_MAX_SPRINTF_ARGS 5
+
+#define ZCRYPT_DBF(...) \
+ debug_sprintf_event(zcrypt_dbf_info, ##__VA_ARGS__)
+
+extern debug_info_t *zcrypt_dbf_info;
int zcrypt_debug_init(void);
void zcrypt_debug_exit(void);
diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h
index de1b6c1d172c..13df60209ed3 100644
--- a/drivers/s390/crypto/zcrypt_error.h
+++ b/drivers/s390/crypto/zcrypt_error.h
@@ -55,52 +55,61 @@ struct error_hdr {
#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 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_INVALID_DOMAIN_PRECHECK 0x42
+#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_INVALID_DOMAIN_PENDING 0x8A
+#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_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 */
+#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,
+static inline int convert_error(struct zcrypt_queue *zq,
struct ap_message *reply)
{
struct error_hdr *ehdr = reply->message;
+ int card = AP_QID_CARD(zq->queue->qid);
+ int queue = AP_QID_QUEUE(zq->queue->qid);
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:
+ case REP82_ERROR_INVALID_DOMAIN_PRECHECK:
+ case REP82_ERROR_INVALID_DOMAIN_PENDING:
// REP88_ERROR_INVALID_KEY // '82' CEX2A
// REP88_ERROR_OPERAND // '84' CEX2A
// REP88_ERROR_OPERAND_EVEN_MOD // '85' CEX2A
/* Invalid input data. */
+ ZCRYPT_DBF(DBF_WARN,
+ "device=%02x.%04x reply=0x%02x => rc=EINVAL\n",
+ card, queue, ehdr->reply_code);
return -EINVAL;
case REP82_ERROR_MESSAGE_TYPE:
// REP88_ERROR_MESSAGE_TYPE // '20' CEX2A
@@ -110,32 +119,32 @@ static inline int convert_error(struct zcrypt_device *zdev,
* and then repeat the request.
*/
atomic_set(&zcrypt_rescan_req, 1);
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- AP_QID_DEVICE(zdev->ap_dev->qid));
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
- AP_QID_DEVICE(zdev->ap_dev->qid), zdev->online,
- ehdr->reply_code);
+ zq->online = 0;
+ pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
+ card, queue);
+ ZCRYPT_DBF(DBF_ERR,
+ "device=%02x.%04x reply=0x%02x => online=0 rc=EAGAIN\n",
+ card, queue, ehdr->reply_code);
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. */
atomic_set(&zcrypt_rescan_req, 1);
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- AP_QID_DEVICE(zdev->ap_dev->qid));
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
- AP_QID_DEVICE(zdev->ap_dev->qid), zdev->online,
- ehdr->reply_code);
+ zq->online = 0;
+ pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
+ card, queue);
+ ZCRYPT_DBF(DBF_ERR,
+ "device=%02x.%04x reply=0x%02x => online=0 rc=EAGAIN\n",
+ card, queue, ehdr->reply_code);
return -EAGAIN;
default:
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- AP_QID_DEVICE(zdev->ap_dev->qid));
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
- AP_QID_DEVICE(zdev->ap_dev->qid), zdev->online,
- ehdr->reply_code);
+ zq->online = 0;
+ pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
+ card, queue);
+ ZCRYPT_DBF(DBF_ERR,
+ "device=%02x.%04x reply=0x%02x => online=0 rc=EAGAIN\n",
+ card, queue, ehdr->reply_code);
return -EAGAIN; /* repeat the request on a different device. */
}
}
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.c b/drivers/s390/crypto/zcrypt_msgtype50.c
index eedfaa2cf715..6dd5d7c58dd0 100644
--- a/drivers/s390/crypto/zcrypt_msgtype50.c
+++ b/drivers/s390/crypto/zcrypt_msgtype50.c
@@ -53,9 +53,6 @@ MODULE_DESCRIPTION("Cryptographic Accelerator (message type 50), " \
"Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
-static void zcrypt_cex2a_receive(struct ap_device *, struct ap_message *,
- struct ap_message *);
-
/**
* The type 50 message family is associated with a CEX2A card.
*
@@ -173,16 +170,48 @@ struct type80_hdr {
unsigned char reserved3[8];
} __packed;
+unsigned int get_rsa_modex_fc(struct ica_rsa_modexpo *mex, int *fcode)
+{
+
+ if (!mex->inputdatalength)
+ return -EINVAL;
+
+ if (mex->inputdatalength <= 128) /* 1024 bit */
+ *fcode = MEX_1K;
+ else if (mex->inputdatalength <= 256) /* 2048 bit */
+ *fcode = MEX_2K;
+ else /* 4096 bit */
+ *fcode = MEX_4K;
+
+ return 0;
+}
+
+unsigned int get_rsa_crt_fc(struct ica_rsa_modexpo_crt *crt, int *fcode)
+{
+
+ if (!crt->inputdatalength)
+ return -EINVAL;
+
+ if (crt->inputdatalength <= 128) /* 1024 bit */
+ *fcode = CRT_1K;
+ else if (crt->inputdatalength <= 256) /* 2048 bit */
+ *fcode = CRT_2K;
+ else /* 4096 bit */
+ *fcode = CRT_4K;
+
+ return 0;
+}
+
/**
* Convert a ICAMEX message to a type50 MEX message.
*
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
+ * @zq: crypto queue pointer
+ * @ap_msg: 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,
+static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_queue *zq,
struct ap_message *ap_msg,
struct ica_rsa_modexpo *mex)
{
@@ -234,13 +263,13 @@ static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev,
/**
* Convert a ICACRT message to a type50 CRT message.
*
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
+ * @zq: crypto queue pointer
+ * @ap_msg: 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,
+static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_queue *zq,
struct ap_message *ap_msg,
struct ica_rsa_modexpo_crt *crt)
{
@@ -283,7 +312,7 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev,
u = crb2->u + sizeof(crb2->u) - short_len;
inp = crb2->message + sizeof(crb2->message) - mod_len;
} else if ((mod_len <= 512) && /* up to 4096 bit key size */
- (zdev->max_mod_size == CEX3A_MAX_MOD_SIZE)) { /* >= CEX3A */
+ (zq->zcard->max_mod_size == CEX3A_MAX_MOD_SIZE)) {
struct type50_crb3_msg *crb3 = ap_msg->message;
memset(crb3, 0, sizeof(*crb3));
ap_msg->length = sizeof(*crb3);
@@ -317,14 +346,14 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev,
/**
* Copy results from a type 80 reply message back to user space.
*
- * @zdev: crypto device pointer
+ * @zq: 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,
+static int convert_type80(struct zcrypt_queue *zq,
struct ap_message *reply,
char __user *outputdata,
unsigned int outputdatalength)
@@ -334,16 +363,18 @@ static int convert_type80(struct zcrypt_device *zdev,
if (t80h->len < sizeof(*t80h) + outputdatalength) {
/* The result is too short, the CEX2A card may not do that.. */
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- AP_QID_DEVICE(zdev->ap_dev->qid));
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
- AP_QID_DEVICE(zdev->ap_dev->qid),
- zdev->online, t80h->code);
-
+ zq->online = 0;
+ pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid));
+ ZCRYPT_DBF(DBF_ERR,
+ "device=%02x.%04x code=0x%02x => online=0 rc=EAGAIN\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid),
+ t80h->code);
return -EAGAIN; /* repeat the request on a different device. */
}
- if (zdev->user_space_type == ZCRYPT_CEX2A)
+ if (zq->zcard->user_space_type == ZCRYPT_CEX2A)
BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE);
else
BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE);
@@ -353,25 +384,31 @@ static int convert_type80(struct zcrypt_device *zdev,
return 0;
}
-static int convert_response(struct zcrypt_device *zdev,
+static int convert_response(struct zcrypt_queue *zq,
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]) {
+ unsigned char rtype = ((unsigned char *) reply->message)[1];
+
+ switch (rtype) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
- return convert_error(zdev, reply);
+ return convert_error(zq, reply);
case TYPE80_RSP_CODE:
- return convert_type80(zdev, reply,
+ return convert_type80(zq, reply,
outputdata, outputdatalength);
default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- AP_QID_DEVICE(zdev->ap_dev->qid));
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%dfail",
- AP_QID_DEVICE(zdev->ap_dev->qid), zdev->online);
+ zq->online = 0;
+ pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid));
+ ZCRYPT_DBF(DBF_ERR,
+ "device=%02x.%04x rtype=0x%02x => online=0 rc=EAGAIN\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid),
+ (unsigned int) rtype);
return -EAGAIN; /* repeat the request on a different device. */
}
}
@@ -380,11 +417,11 @@ static int convert_response(struct zcrypt_device *zdev,
* 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
+ * @aq: 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,
+static void zcrypt_cex2a_receive(struct ap_queue *aq,
struct ap_message *msg,
struct ap_message *reply)
{
@@ -400,7 +437,7 @@ static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
goto out; /* ap_msg->rc indicates the error */
t80h = reply->message;
if (t80h->type == TYPE80_RSP_CODE) {
- if (ap_dev->device_type == AP_DEVICE_TYPE_CEX2A)
+ if (aq->ap_dev.device_type == AP_DEVICE_TYPE_CEX2A)
length = min_t(int,
CEX2A_MAX_RESPONSE_SIZE, t80h->len);
else
@@ -418,11 +455,11 @@ 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
+ * @zq: pointer to zcrypt_queue 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,
+static long zcrypt_cex2a_modexpo(struct zcrypt_queue *zq,
struct ica_rsa_modexpo *mex)
{
struct ap_message ap_msg;
@@ -430,7 +467,7 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
int rc;
ap_init_message(&ap_msg);
- if (zdev->user_space_type == ZCRYPT_CEX2A)
+ if (zq->zcard->user_space_type == ZCRYPT_CEX2A)
ap_msg.message = kmalloc(MSGTYPE50_CRB2_MAX_MSG_SIZE,
GFP_KERNEL);
else
@@ -442,20 +479,20 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
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);
+ rc = ICAMEX_msg_to_type50MEX_msg(zq, &ap_msg, mex);
if (rc)
goto out_free;
init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
+ ap_queue_message(zq->queue, &ap_msg);
rc = wait_for_completion_interruptible(&work);
if (rc == 0) {
rc = ap_msg.rc;
if (rc == 0)
- rc = convert_response(zdev, &ap_msg, mex->outputdata,
+ rc = convert_response(zq, &ap_msg, mex->outputdata,
mex->outputdatalength);
} else
/* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
+ ap_cancel_message(zq->queue, &ap_msg);
out_free:
kfree(ap_msg.message);
return rc;
@@ -464,11 +501,11 @@ out_free:
/**
* 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
+ * @zq: pointer to zcrypt_queue 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,
+static long zcrypt_cex2a_modexpo_crt(struct zcrypt_queue *zq,
struct ica_rsa_modexpo_crt *crt)
{
struct ap_message ap_msg;
@@ -476,7 +513,7 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
int rc;
ap_init_message(&ap_msg);
- if (zdev->user_space_type == ZCRYPT_CEX2A)
+ if (zq->zcard->user_space_type == ZCRYPT_CEX2A)
ap_msg.message = kmalloc(MSGTYPE50_CRB2_MAX_MSG_SIZE,
GFP_KERNEL);
else
@@ -488,20 +525,20 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
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);
+ rc = ICACRT_msg_to_type50CRT_msg(zq, &ap_msg, crt);
if (rc)
goto out_free;
init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
+ ap_queue_message(zq->queue, &ap_msg);
rc = wait_for_completion_interruptible(&work);
if (rc == 0) {
rc = ap_msg.rc;
if (rc == 0)
- rc = convert_response(zdev, &ap_msg, crt->outputdata,
+ rc = convert_response(zq, &ap_msg, crt->outputdata,
crt->outputdatalength);
} else
/* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
+ ap_cancel_message(zq->queue, &ap_msg);
out_free:
kfree(ap_msg.message);
return rc;
@@ -518,16 +555,12 @@ static struct zcrypt_ops zcrypt_msgtype50_ops = {
.variant = MSGTYPE50_VARIANT_DEFAULT,
};
-int __init zcrypt_msgtype50_init(void)
+void __init zcrypt_msgtype50_init(void)
{
zcrypt_msgtype_register(&zcrypt_msgtype50_ops);
- return 0;
}
void __exit zcrypt_msgtype50_exit(void)
{
zcrypt_msgtype_unregister(&zcrypt_msgtype50_ops);
}
-
-module_init(zcrypt_msgtype50_init);
-module_exit(zcrypt_msgtype50_exit);
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.h b/drivers/s390/crypto/zcrypt_msgtype50.h
index 0a66e4aeeb50..5cc280318ee7 100644
--- a/drivers/s390/crypto/zcrypt_msgtype50.h
+++ b/drivers/s390/crypto/zcrypt_msgtype50.h
@@ -35,7 +35,10 @@
#define MSGTYPE_ADJUSTMENT 0x08 /*type04 extension (not needed in type50)*/
-int zcrypt_msgtype50_init(void);
+unsigned int get_rsa_modex_fc(struct ica_rsa_modexpo *, int *);
+unsigned int get_rsa_crt_fc(struct ica_rsa_modexpo_crt *, int *);
+
+void zcrypt_msgtype50_init(void);
void zcrypt_msgtype50_exit(void);
#endif /* _ZCRYPT_MSGTYPE50_H_ */
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index 21959719daef..e5563ffeb839 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -60,9 +60,6 @@ MODULE_DESCRIPTION("Cryptographic Coprocessor (message type 6), " \
"Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
-static void zcrypt_msgtype6_receive(struct ap_device *, struct ap_message *,
- struct ap_message *);
-
/**
* CPRB
* Note that all shorts, ints and longs are little-endian.
@@ -149,16 +146,122 @@ static struct CPRBX static_cprbx = {
.func_id = {0x54, 0x32},
};
+int speed_idx_cca(int req_type)
+{
+ switch (req_type) {
+ case 0x4142:
+ case 0x4149:
+ case 0x414D:
+ case 0x4341:
+ case 0x4344:
+ case 0x4354:
+ case 0x4358:
+ case 0x444B:
+ case 0x4558:
+ case 0x4643:
+ case 0x4651:
+ case 0x4C47:
+ case 0x4C4B:
+ case 0x4C51:
+ case 0x4F48:
+ case 0x504F:
+ case 0x5053:
+ case 0x5058:
+ case 0x5343:
+ case 0x5344:
+ case 0x5345:
+ case 0x5350:
+ return LOW;
+ case 0x414B:
+ case 0x4345:
+ case 0x4349:
+ case 0x434D:
+ case 0x4847:
+ case 0x4849:
+ case 0x484D:
+ case 0x4850:
+ case 0x4851:
+ case 0x4954:
+ case 0x4958:
+ case 0x4B43:
+ case 0x4B44:
+ case 0x4B45:
+ case 0x4B47:
+ case 0x4B48:
+ case 0x4B49:
+ case 0x4B4E:
+ case 0x4B50:
+ case 0x4B52:
+ case 0x4B54:
+ case 0x4B58:
+ case 0x4D50:
+ case 0x4D53:
+ case 0x4D56:
+ case 0x4D58:
+ case 0x5044:
+ case 0x5045:
+ case 0x5046:
+ case 0x5047:
+ case 0x5049:
+ case 0x504B:
+ case 0x504D:
+ case 0x5254:
+ case 0x5347:
+ case 0x5349:
+ case 0x534B:
+ case 0x534D:
+ case 0x5356:
+ case 0x5358:
+ case 0x5443:
+ case 0x544B:
+ case 0x5647:
+ return HIGH;
+ default:
+ return MEDIUM;
+ }
+}
+
+int speed_idx_ep11(int req_type)
+{
+ switch (req_type) {
+ case 1:
+ case 2:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ case 40:
+ return LOW;
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 26:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ return HIGH;
+ default:
+ return MEDIUM;
+ }
+}
+
+
/**
* Convert a ICAMEX message to a type6 MEX message.
*
- * @zdev: crypto device pointer
+ * @zq: 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,
+static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_queue *zq,
struct ap_message *ap_msg,
struct ica_rsa_modexpo *mex)
{
@@ -173,11 +276,6 @@ static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_device *zdev,
.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;
@@ -204,11 +302,10 @@ static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_device *zdev,
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.domain = AP_QID_QUEUE(zq->queue->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->fr = static_pke_fnr;
msg->cprbx.req_parml = size - sizeof(msg->hdr) - sizeof(msg->cprbx);
@@ -219,13 +316,13 @@ static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_device *zdev,
/**
* Convert a ICACRT message to a type6 CRT message.
*
- * @zdev: crypto device pointer
+ * @zq: 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,
+static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_queue *zq,
struct ap_message *ap_msg,
struct ica_rsa_modexpo_crt *crt)
{
@@ -241,11 +338,6 @@ static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev,
.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;
@@ -272,12 +364,11 @@ static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev,
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.domain = AP_QID_QUEUE(zq->queue->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;
+ msg->fr = static_pkd_fnr;
ap_msg->length = size;
return 0;
@@ -286,7 +377,7 @@ static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev,
/**
* Convert a XCRB message to a type6 CPRB message.
*
- * @zdev: crypto device pointer
+ * @zq: crypto device pointer
* @ap_msg: pointer to AP message
* @xcRB: pointer to user input data
*
@@ -297,9 +388,10 @@ struct type86_fmt2_msg {
struct type86_fmt2_ext fmt2;
} __packed;
-static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_xcRB *xcRB)
+static int XCRB_msg_to_type6CPRB_msgX(struct ap_message *ap_msg,
+ struct ica_xcRB *xcRB,
+ unsigned int *fcode,
+ unsigned short **dom)
{
static struct type6_hdr static_type6_hdrX = {
.type = 0x06,
@@ -379,6 +471,9 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
memcpy(msg->hdr.function_code, function_code,
sizeof(msg->hdr.function_code));
+ *fcode = (msg->hdr.function_code[0] << 8) | msg->hdr.function_code[1];
+ *dom = (unsigned short *)&msg->cprbx.domain;
+
if (memcmp(function_code, "US", 2) == 0)
ap_msg->special = 1;
else
@@ -389,15 +484,15 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
copy_from_user(req_data, xcRB->request_data_address,
xcRB->request_data_length))
return -EFAULT;
+
return 0;
}
-static int xcrb_msg_to_type6_ep11cprb_msgx(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ep11_urb *xcRB)
+static int xcrb_msg_to_type6_ep11cprb_msgx(struct ap_message *ap_msg,
+ struct ep11_urb *xcRB,
+ unsigned int *fcode)
{
unsigned int lfmt;
-
static struct type6_hdr static_type6_ep11_hdr = {
.type = 0x06,
.rqid = {0x00, 0x01},
@@ -421,7 +516,7 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(struct zcrypt_device *zdev,
unsigned char dom_tag; /* fixed value 0x4 */
unsigned char dom_len; /* fixed value 0x4 */
unsigned int dom_val; /* domain id */
- } __packed * payload_hdr;
+ } __packed * payload_hdr = NULL;
if (CEIL4(xcRB->req_len) < xcRB->req_len)
return -EINVAL; /* overflow after alignment*/
@@ -450,43 +545,30 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(struct zcrypt_device *zdev,
return -EFAULT;
}
- /*
- The target domain field within the cprb body/payload block will be
- replaced by the usage domain for non-management commands only.
- Therefore we check the first bit of the 'flags' parameter for
- management command indication.
- 0 - non management command
- 1 - management command
- */
- if (!((msg->cprbx.flags & 0x80) == 0x80)) {
- msg->cprbx.target_id = (unsigned int)
- AP_QID_QUEUE(zdev->ap_dev->qid);
-
- if ((msg->pld_lenfmt & 0x80) == 0x80) { /*ext.len.fmt 2 or 3*/
- switch (msg->pld_lenfmt & 0x03) {
- case 1:
- lfmt = 2;
- break;
- case 2:
- lfmt = 3;
- break;
- default:
- return -EINVAL;
- }
- } else {
- lfmt = 1; /* length format #1 */
- }
- payload_hdr = (struct pld_hdr *)((&(msg->pld_lenfmt))+lfmt);
- payload_hdr->dom_val = (unsigned int)
- AP_QID_QUEUE(zdev->ap_dev->qid);
+ if ((msg->pld_lenfmt & 0x80) == 0x80) { /*ext.len.fmt 2 or 3*/
+ switch (msg->pld_lenfmt & 0x03) {
+ case 1:
+ lfmt = 2;
+ break;
+ case 2:
+ lfmt = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ lfmt = 1; /* length format #1 */
}
+ payload_hdr = (struct pld_hdr *)((&(msg->pld_lenfmt))+lfmt);
+ *fcode = payload_hdr->func_val & 0xFFFF;
+
return 0;
}
/**
* Copy results from a type 86 ICA reply message back to user space.
*
- * @zdev: crypto device pointer
+ * @zq: crypto device pointer
* @reply: reply AP message.
* @data: pointer to user output data
* @length: size of user output data
@@ -508,7 +590,7 @@ struct type86_ep11_reply {
struct ep11_cprb cprbx;
} __packed;
-static int convert_type86_ica(struct zcrypt_device *zdev,
+static int convert_type86_ica(struct zcrypt_queue *zq,
struct ap_message *reply,
char __user *outputdata,
unsigned int outputdatalength)
@@ -556,26 +638,37 @@ static int convert_type86_ica(struct zcrypt_device *zdev,
service_rc = msg->cprbx.ccp_rtcode;
if (unlikely(service_rc != 0)) {
service_rs = msg->cprbx.ccp_rscode;
- if (service_rc == 8 && service_rs == 66)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 65)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 770)
+ if ((service_rc == 8 && service_rs == 66) ||
+ (service_rc == 8 && service_rs == 65) ||
+ (service_rc == 8 && service_rs == 72) ||
+ (service_rc == 8 && service_rs == 770) ||
+ (service_rc == 12 && service_rs == 769)) {
+ ZCRYPT_DBF(DBF_DEBUG,
+ "device=%02x.%04x rc/rs=%d/%d => rc=EINVAL\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid),
+ (int) service_rc, (int) service_rs);
return -EINVAL;
+ }
if (service_rc == 8 && service_rs == 783) {
- zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD;
+ zq->zcard->min_mod_size =
+ PCIXCC_MIN_MOD_SIZE_OLD;
+ ZCRYPT_DBF(DBF_DEBUG,
+ "device=%02x.%04x rc/rs=%d/%d => rc=EAGAIN\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid),
+ (int) service_rc, (int) service_rs);
return -EAGAIN;
}
- if (service_rc == 12 && service_rs == 769)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 72)
- return -EINVAL;
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- AP_QID_DEVICE(zdev->ap_dev->qid));
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
- AP_QID_DEVICE(zdev->ap_dev->qid), zdev->online,
- msg->hdr.reply_code);
+ zq->online = 0;
+ pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid));
+ ZCRYPT_DBF(DBF_ERR,
+ "device=%02x.%04x rc/rs=%d/%d => online=0 rc=EAGAIN\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid),
+ (int) service_rc, (int) service_rs);
return -EAGAIN; /* repeat the request on a different device. */
}
data = msg->text;
@@ -611,13 +704,13 @@ static int convert_type86_ica(struct zcrypt_device *zdev,
/**
* Copy results from a type 86 XCRB reply message back to user space.
*
- * @zdev: crypto device pointer
+ * @zq: 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,
+static int convert_type86_xcrb(struct zcrypt_queue *zq,
struct ap_message *reply,
struct ica_xcRB *xcRB)
{
@@ -642,13 +735,13 @@ static int convert_type86_xcrb(struct zcrypt_device *zdev,
/**
* Copy results from a type 86 EP11 XCRB reply message back to user space.
*
- * @zdev: crypto device pointer
+ * @zq: crypto device pointer
* @reply: reply AP message.
* @xcRB: pointer to EP11 user request block
*
* Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
*/
-static int convert_type86_ep11_xcrb(struct zcrypt_device *zdev,
+static int convert_type86_ep11_xcrb(struct zcrypt_queue *zq,
struct ap_message *reply,
struct ep11_urb *xcRB)
{
@@ -666,7 +759,7 @@ static int convert_type86_ep11_xcrb(struct zcrypt_device *zdev,
return 0;
}
-static int convert_type86_rng(struct zcrypt_device *zdev,
+static int convert_type86_rng(struct zcrypt_queue *zq,
struct ap_message *reply,
char *buffer)
{
@@ -683,104 +776,113 @@ static int convert_type86_rng(struct zcrypt_device *zdev,
return msg->fmt2.count2;
}
-static int convert_response_ica(struct zcrypt_device *zdev,
+static int convert_response_ica(struct zcrypt_queue *zq,
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]) {
+ switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
- return convert_error(zdev, reply);
+ return convert_error(zq, reply);
case TYPE86_RSP_CODE:
if (msg->cprbx.ccp_rtcode &&
(msg->cprbx.ccp_rscode == 0x14f) &&
(outputdatalength > 256)) {
- if (zdev->max_exp_bit_length <= 17) {
- zdev->max_exp_bit_length = 17;
+ if (zq->zcard->max_exp_bit_length <= 17) {
+ zq->zcard->max_exp_bit_length = 17;
return -EAGAIN;
} else
return -EINVAL;
}
if (msg->hdr.reply_code)
- return convert_error(zdev, reply);
+ return convert_error(zq, reply);
if (msg->cprbx.cprb_ver_id == 0x02)
- return convert_type86_ica(zdev, reply,
+ return convert_type86_ica(zq, reply,
outputdata, outputdatalength);
/* Fall through, no break, incorrect cprb version is an unknown
* response */
default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- AP_QID_DEVICE(zdev->ap_dev->qid));
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%dfail",
- AP_QID_DEVICE(zdev->ap_dev->qid), zdev->online);
+ zq->online = 0;
+ pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid));
+ ZCRYPT_DBF(DBF_ERR,
+ "device=%02x.%04x rtype=0x%02x => online=0 rc=EAGAIN\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid),
+ (int) msg->hdr.type);
return -EAGAIN; /* repeat the request on a different device. */
}
}
-static int convert_response_xcrb(struct zcrypt_device *zdev,
+static int convert_response_xcrb(struct zcrypt_queue *zq,
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]) {
+ switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
- return convert_error(zdev, reply);
+ return convert_error(zq, reply);
case TYPE86_RSP_CODE:
if (msg->hdr.reply_code) {
memcpy(&(xcRB->status), msg->fmt2.apfs, sizeof(u32));
- return convert_error(zdev, reply);
+ return convert_error(zq, reply);
}
if (msg->cprbx.cprb_ver_id == 0x02)
- return convert_type86_xcrb(zdev, reply, xcRB);
+ return convert_type86_xcrb(zq, reply, xcRB);
/* Fall through, no break, incorrect cprb version is an unknown
* response */
default: /* Unknown response type, this should NEVER EVER happen */
xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- AP_QID_DEVICE(zdev->ap_dev->qid));
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%dfail",
- AP_QID_DEVICE(zdev->ap_dev->qid), zdev->online);
+ zq->online = 0;
+ pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid));
+ ZCRYPT_DBF(DBF_ERR,
+ "device=%02x.%04x rtype=0x%02x => online=0 rc=EAGAIN\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid),
+ (int) msg->hdr.type);
return -EAGAIN; /* repeat the request on a different device. */
}
}
-static int convert_response_ep11_xcrb(struct zcrypt_device *zdev,
+static int convert_response_ep11_xcrb(struct zcrypt_queue *zq,
struct ap_message *reply, struct ep11_urb *xcRB)
{
struct type86_ep11_reply *msg = reply->message;
- /* Response type byte is the second byte in the response. */
- switch (((unsigned char *)reply->message)[1]) {
+ switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE87_RSP_CODE:
- return convert_error(zdev, reply);
+ return convert_error(zq, reply);
case TYPE86_RSP_CODE:
if (msg->hdr.reply_code)
- return convert_error(zdev, reply);
+ return convert_error(zq, reply);
if (msg->cprbx.cprb_ver_id == 0x04)
- return convert_type86_ep11_xcrb(zdev, reply, xcRB);
+ return convert_type86_ep11_xcrb(zq, reply, xcRB);
/* Fall through, no break, incorrect cprb version is an unknown resp.*/
default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- AP_QID_DEVICE(zdev->ap_dev->qid));
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%dfail",
- AP_QID_DEVICE(zdev->ap_dev->qid), zdev->online);
+ zq->online = 0;
+ pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid));
+ ZCRYPT_DBF(DBF_ERR,
+ "device=%02x.%04x rtype=0x%02x => online=0 rc=EAGAIN\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid),
+ (int) msg->hdr.type);
return -EAGAIN; /* repeat the request on a different device. */
}
}
-static int convert_response_rng(struct zcrypt_device *zdev,
+static int convert_response_rng(struct zcrypt_queue *zq,
struct ap_message *reply,
char *data)
{
@@ -794,15 +896,19 @@ static int convert_response_rng(struct zcrypt_device *zdev,
if (msg->hdr.reply_code)
return -EINVAL;
if (msg->cprbx.cprb_ver_id == 0x02)
- return convert_type86_rng(zdev, reply, data);
+ return convert_type86_rng(zq, reply, data);
/* Fall through, no break, incorrect cprb version is an unknown
* response */
default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- AP_QID_DEVICE(zdev->ap_dev->qid));
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%dfail",
- AP_QID_DEVICE(zdev->ap_dev->qid), zdev->online);
+ zq->online = 0;
+ pr_err("Cryptographic device %02x.%04x failed and was set offline\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid));
+ ZCRYPT_DBF(DBF_ERR,
+ "device=%02x.%04x rtype=0x%02x => online=0 rc=EAGAIN\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid),
+ (int) msg->hdr.type);
return -EAGAIN; /* repeat the request on a different device. */
}
}
@@ -811,11 +917,11 @@ static int convert_response_rng(struct zcrypt_device *zdev,
* 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
+ * @aq: pointer to the AP queue
* @msg: pointer to the AP message
* @reply: pointer to the AP reply message
*/
-static void zcrypt_msgtype6_receive(struct ap_device *ap_dev,
+static void zcrypt_msgtype6_receive(struct ap_queue *aq,
struct ap_message *msg,
struct ap_message *reply)
{
@@ -860,11 +966,11 @@ out:
* 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
+ * @aq: pointer to the AP queue
* @msg: pointer to the AP message
* @reply: pointer to the AP reply message
*/
-static void zcrypt_msgtype6_receive_ep11(struct ap_device *ap_dev,
+static void zcrypt_msgtype6_receive_ep11(struct ap_queue *aq,
struct ap_message *msg,
struct ap_message *reply)
{
@@ -904,11 +1010,11 @@ 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
+ * @zq: pointer to zcrypt_queue structure that identifies the
* PCIXCC/CEX2C device to the request distributor
* @mex: pointer to the modexpo request buffer
*/
-static long zcrypt_msgtype6_modexpo(struct zcrypt_device *zdev,
+static long zcrypt_msgtype6_modexpo(struct zcrypt_queue *zq,
struct ica_rsa_modexpo *mex)
{
struct ap_message ap_msg;
@@ -925,21 +1031,21 @@ static long zcrypt_msgtype6_modexpo(struct zcrypt_device *zdev,
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);
+ rc = ICAMEX_msg_to_type6MEX_msgX(zq, &ap_msg, mex);
if (rc)
goto out_free;
init_completion(&resp_type.work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
+ ap_queue_message(zq->queue, &ap_msg);
rc = wait_for_completion_interruptible(&resp_type.work);
if (rc == 0) {
rc = ap_msg.rc;
if (rc == 0)
- rc = convert_response_ica(zdev, &ap_msg,
+ rc = convert_response_ica(zq, &ap_msg,
mex->outputdata,
mex->outputdatalength);
} else
/* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
+ ap_cancel_message(zq->queue, &ap_msg);
out_free:
free_page((unsigned long) ap_msg.message);
return rc;
@@ -948,11 +1054,11 @@ out_free:
/**
* 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
+ * @zq: pointer to zcrypt_queue structure that identifies the
* PCIXCC/CEX2C device to the request distributor
* @crt: pointer to the modexpoc_crt request buffer
*/
-static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_device *zdev,
+static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_queue *zq,
struct ica_rsa_modexpo_crt *crt)
{
struct ap_message ap_msg;
@@ -969,148 +1075,258 @@ static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_device *zdev,
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);
+ rc = ICACRT_msg_to_type6CRT_msgX(zq, &ap_msg, crt);
if (rc)
goto out_free;
init_completion(&resp_type.work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
+ ap_queue_message(zq->queue, &ap_msg);
rc = wait_for_completion_interruptible(&resp_type.work);
if (rc == 0) {
rc = ap_msg.rc;
if (rc == 0)
- rc = convert_response_ica(zdev, &ap_msg,
+ rc = convert_response_ica(zq, &ap_msg,
crt->outputdata,
crt->outputdatalength);
- } else
+ } else {
/* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
+ ap_cancel_message(zq->queue, &ap_msg);
+ }
out_free:
free_page((unsigned long) ap_msg.message);
return rc;
}
+unsigned int get_cprb_fc(struct ica_xcRB *xcRB,
+ struct ap_message *ap_msg,
+ unsigned int *func_code, unsigned short **dom)
+{
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_XCRB,
+ };
+ int rc;
+
+ ap_init_message(ap_msg);
+ ap_msg->message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
+ if (!ap_msg->message)
+ return -ENOMEM;
+ ap_msg->receive = zcrypt_msgtype6_receive;
+ ap_msg->psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg->private = kmalloc(sizeof(resp_type), GFP_KERNEL);
+ if (!ap_msg->private) {
+ kzfree(ap_msg->message);
+ return -ENOMEM;
+ }
+ memcpy(ap_msg->private, &resp_type, sizeof(resp_type));
+ rc = XCRB_msg_to_type6CPRB_msgX(ap_msg, xcRB, func_code, dom);
+ if (rc) {
+ kzfree(ap_msg->message);
+ kzfree(ap_msg->private);
+ }
+ 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
+ * @zq: pointer to zcrypt_queue structure that identifies the
* PCIXCC/CEX2C device to the request distributor
* @xcRB: pointer to the send_cprb request buffer
*/
-static long zcrypt_msgtype6_send_cprb(struct zcrypt_device *zdev,
- struct ica_xcRB *xcRB)
+static long zcrypt_msgtype6_send_cprb(struct zcrypt_queue *zq,
+ struct ica_xcRB *xcRB,
+ struct ap_message *ap_msg)
{
- struct ap_message ap_msg;
- struct response_type resp_type = {
- .type = PCIXCC_RESPONSE_TYPE_XCRB,
- };
int rc;
+ struct response_type *rtype = (struct response_type *)(ap_msg->private);
- ap_init_message(&ap_msg);
- ap_msg.message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_msgtype6_receive;
- 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(&resp_type.work);
+ init_completion(&rtype->work);
+ ap_queue_message(zq->queue, ap_msg);
+ rc = wait_for_completion_interruptible(&rtype->work);
if (rc == 0) {
- rc = ap_msg.rc;
+ rc = ap_msg->rc;
if (rc == 0)
- rc = convert_response_xcrb(zdev, &ap_msg, xcRB);
+ rc = convert_response_xcrb(zq, ap_msg, xcRB);
} else
/* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- kzfree(ap_msg.message);
+ ap_cancel_message(zq->queue, ap_msg);
+
+ kzfree(ap_msg->message);
+ kzfree(ap_msg->private);
+ return rc;
+}
+
+unsigned int get_ep11cprb_fc(struct ep11_urb *xcrb,
+ struct ap_message *ap_msg,
+ unsigned int *func_code)
+{
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_EP11,
+ };
+ int rc;
+
+ ap_init_message(ap_msg);
+ ap_msg->message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
+ if (!ap_msg->message)
+ return -ENOMEM;
+ ap_msg->receive = zcrypt_msgtype6_receive_ep11;
+ ap_msg->psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg->private = kmalloc(sizeof(resp_type), GFP_KERNEL);
+ if (!ap_msg->private) {
+ kzfree(ap_msg->message);
+ return -ENOMEM;
+ }
+ memcpy(ap_msg->private, &resp_type, sizeof(resp_type));
+ rc = xcrb_msg_to_type6_ep11cprb_msgx(ap_msg, xcrb, func_code);
+ if (rc) {
+ kzfree(ap_msg->message);
+ kzfree(ap_msg->private);
+ }
return rc;
}
/**
* The request distributor calls this function if it picked the CEX4P
* device to handle a send_ep11_cprb request.
- * @zdev: pointer to zcrypt_device structure that identifies the
+ * @zq: pointer to zcrypt_queue structure that identifies the
* CEX4P device to the request distributor
* @xcRB: pointer to the ep11 user request block
*/
-static long zcrypt_msgtype6_send_ep11_cprb(struct zcrypt_device *zdev,
- struct ep11_urb *xcrb)
+static long zcrypt_msgtype6_send_ep11_cprb(struct zcrypt_queue *zq,
+ struct ep11_urb *xcrb,
+ struct ap_message *ap_msg)
{
- struct ap_message ap_msg;
- struct response_type resp_type = {
- .type = PCIXCC_RESPONSE_TYPE_EP11,
- };
int rc;
+ unsigned int lfmt;
+ struct response_type *rtype = (struct response_type *)(ap_msg->private);
+ struct {
+ struct type6_hdr hdr;
+ struct ep11_cprb cprbx;
+ unsigned char pld_tag; /* fixed value 0x30 */
+ unsigned char pld_lenfmt; /* payload length format */
+ } __packed * msg = ap_msg->message;
+ struct pld_hdr {
+ unsigned char func_tag; /* fixed value 0x4 */
+ unsigned char func_len; /* fixed value 0x4 */
+ unsigned int func_val; /* function ID */
+ unsigned char dom_tag; /* fixed value 0x4 */
+ unsigned char dom_len; /* fixed value 0x4 */
+ unsigned int dom_val; /* domain id */
+ } __packed * payload_hdr = NULL;
- ap_init_message(&ap_msg);
- ap_msg.message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_msgtype6_receive_ep11;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &resp_type;
- rc = xcrb_msg_to_type6_ep11cprb_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(&resp_type.work);
+
+ /**
+ * The target domain field within the cprb body/payload block will be
+ * replaced by the usage domain for non-management commands only.
+ * Therefore we check the first bit of the 'flags' parameter for
+ * management command indication.
+ * 0 - non management command
+ * 1 - management command
+ */
+ if (!((msg->cprbx.flags & 0x80) == 0x80)) {
+ msg->cprbx.target_id = (unsigned int)
+ AP_QID_QUEUE(zq->queue->qid);
+
+ if ((msg->pld_lenfmt & 0x80) == 0x80) { /*ext.len.fmt 2 or 3*/
+ switch (msg->pld_lenfmt & 0x03) {
+ case 1:
+ lfmt = 2;
+ break;
+ case 2:
+ lfmt = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ lfmt = 1; /* length format #1 */
+ }
+ payload_hdr = (struct pld_hdr *)((&(msg->pld_lenfmt))+lfmt);
+ payload_hdr->dom_val = (unsigned int)
+ AP_QID_QUEUE(zq->queue->qid);
+ }
+
+ init_completion(&rtype->work);
+ ap_queue_message(zq->queue, ap_msg);
+ rc = wait_for_completion_interruptible(&rtype->work);
if (rc == 0) {
- rc = ap_msg.rc;
+ rc = ap_msg->rc;
if (rc == 0)
- rc = convert_response_ep11_xcrb(zdev, &ap_msg, xcrb);
+ rc = convert_response_ep11_xcrb(zq, ap_msg, xcrb);
} else
/* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
+ ap_cancel_message(zq->queue, ap_msg);
-out_free:
- kzfree(ap_msg.message);
+ kzfree(ap_msg->message);
+ kzfree(ap_msg->private);
return rc;
}
+unsigned int get_rng_fc(struct ap_message *ap_msg, int *func_code,
+ unsigned int *domain)
+{
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_XCRB,
+ };
+
+ ap_init_message(ap_msg);
+ ap_msg->message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
+ if (!ap_msg->message)
+ return -ENOMEM;
+ ap_msg->receive = zcrypt_msgtype6_receive;
+ ap_msg->psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg->private = kmalloc(sizeof(resp_type), GFP_KERNEL);
+ if (!ap_msg->private) {
+ kzfree(ap_msg->message);
+ return -ENOMEM;
+ }
+ memcpy(ap_msg->private, &resp_type, sizeof(resp_type));
+
+ rng_type6CPRB_msgX(ap_msg, ZCRYPT_RNG_BUFFER_SIZE, domain);
+
+ *func_code = HWRNG;
+ return 0;
+}
+
/**
* The request distributor calls this function if it picked the PCIXCC/CEX2C
* device to generate random data.
- * @zdev: pointer to zcrypt_device structure that identifies the
+ * @zq: pointer to zcrypt_queue structure that identifies the
* PCIXCC/CEX2C device to the request distributor
* @buffer: pointer to a memory page to return random data
*/
-
-static long zcrypt_msgtype6_rng(struct zcrypt_device *zdev,
- char *buffer)
+static long zcrypt_msgtype6_rng(struct zcrypt_queue *zq,
+ char *buffer, struct ap_message *ap_msg)
{
- struct ap_message ap_msg;
- struct response_type resp_type = {
- .type = PCIXCC_RESPONSE_TYPE_XCRB,
- };
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ char function_code[2];
+ short int rule_length;
+ char rule[8];
+ short int verb_length;
+ short int key_length;
+ } __packed * msg = ap_msg->message;
+ struct response_type *rtype = (struct response_type *)(ap_msg->private);
int rc;
- ap_init_message(&ap_msg);
- ap_msg.message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_msgtype6_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &resp_type;
- rng_type6CPRB_msgX(zdev->ap_dev, &ap_msg, ZCRYPT_RNG_BUFFER_SIZE);
- init_completion(&resp_type.work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&resp_type.work);
+ msg->cprbx.domain = AP_QID_QUEUE(zq->queue->qid);
+
+ init_completion(&rtype->work);
+ ap_queue_message(zq->queue, ap_msg);
+ rc = wait_for_completion_interruptible(&rtype->work);
if (rc == 0) {
- rc = ap_msg.rc;
+ rc = ap_msg->rc;
if (rc == 0)
- rc = convert_response_rng(zdev, &ap_msg, buffer);
+ rc = convert_response_rng(zq, ap_msg, buffer);
} else
/* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
- kfree(ap_msg.message);
+ ap_cancel_message(zq->queue, ap_msg);
+
+ kzfree(ap_msg->message);
+ kzfree(ap_msg->private);
return rc;
}
@@ -1145,12 +1361,11 @@ static struct zcrypt_ops zcrypt_msgtype6_ep11_ops = {
.send_ep11_cprb = zcrypt_msgtype6_send_ep11_cprb,
};
-int __init zcrypt_msgtype6_init(void)
+void __init zcrypt_msgtype6_init(void)
{
zcrypt_msgtype_register(&zcrypt_msgtype6_norng_ops);
zcrypt_msgtype_register(&zcrypt_msgtype6_ops);
zcrypt_msgtype_register(&zcrypt_msgtype6_ep11_ops);
- return 0;
}
void __exit zcrypt_msgtype6_exit(void)
@@ -1159,6 +1374,3 @@ void __exit zcrypt_msgtype6_exit(void)
zcrypt_msgtype_unregister(&zcrypt_msgtype6_ops);
zcrypt_msgtype_unregister(&zcrypt_msgtype6_ep11_ops);
}
-
-module_init(zcrypt_msgtype6_init);
-module_exit(zcrypt_msgtype6_exit);
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.h b/drivers/s390/crypto/zcrypt_msgtype6.h
index 207247570623..7a0d5b57821f 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.h
+++ b/drivers/s390/crypto/zcrypt_msgtype6.h
@@ -116,15 +116,28 @@ struct type86_fmt2_ext {
unsigned int offset4; /* 0x00000000 */
} __packed;
+unsigned int get_cprb_fc(struct ica_xcRB *, struct ap_message *,
+ unsigned int *, unsigned short **);
+unsigned int get_ep11cprb_fc(struct ep11_urb *, struct ap_message *,
+ unsigned int *);
+unsigned int get_rng_fc(struct ap_message *, int *, unsigned int *);
+
+#define LOW 10
+#define MEDIUM 100
+#define HIGH 500
+
+int speed_idx_cca(int);
+int speed_idx_ep11(int);
+
/**
* Prepare a type6 CPRB message for random number generation
*
* @ap_dev: AP device pointer
* @ap_msg: pointer to AP message
*/
-static inline void rng_type6CPRB_msgX(struct ap_device *ap_dev,
- struct ap_message *ap_msg,
- unsigned random_number_length)
+static inline void rng_type6CPRB_msgX(struct ap_message *ap_msg,
+ unsigned int random_number_length,
+ unsigned int *domain)
{
struct {
struct type6_hdr hdr;
@@ -156,16 +169,16 @@ static inline void rng_type6CPRB_msgX(struct ap_device *ap_dev,
msg->hdr.FromCardLen2 = random_number_length,
msg->cprbx = local_cprbx;
msg->cprbx.rpl_datal = random_number_length,
- msg->cprbx.domain = AP_QID_QUEUE(ap_dev->qid);
memcpy(msg->function_code, msg->hdr.function_code, 0x02);
msg->rule_length = 0x0a;
memcpy(msg->rule, "RANDOM ", 8);
msg->verb_length = 0x02;
msg->key_length = 0x02;
ap_msg->length = sizeof(*msg);
+ *domain = (unsigned short)msg->cprbx.domain;
}
-int zcrypt_msgtype6_init(void);
+void zcrypt_msgtype6_init(void);
void zcrypt_msgtype6_exit(void);
#endif /* _ZCRYPT_MSGTYPE6_H_ */
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index df8f0c4dacb7..600604782b65 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -31,7 +31,8 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/atomic.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
+#include <linux/mod_devicetable.h>
#include "ap_bus.h"
#include "zcrypt_api.h"
@@ -46,11 +47,6 @@
#define CEX3C_MIN_MOD_SIZE PCIXCC_MIN_MOD_SIZE
#define CEX3C_MAX_MOD_SIZE 512 /* 4096 bits */
-#define PCIXCC_MCL2_SPEED_RATING 7870
-#define PCIXCC_MCL3_SPEED_RATING 7870
-#define CEX2C_SPEED_RATING 7000
-#define CEX3C_SPEED_RATING 6500
-
#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 */
@@ -67,142 +63,34 @@ struct response_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) },
- { AP_DEVICE(AP_DEVICE_TYPE_CEX3C) },
- { /* end of list */ },
-};
-
-MODULE_DEVICE_TABLE(ap, zcrypt_pcixcc_ids);
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("PCIXCC Cryptographic Coprocessor device driver, " \
"Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
-static int zcrypt_pcixcc_probe(struct ap_device *ap_dev);
-static void zcrypt_pcixcc_remove(struct ap_device *ap_dev);
-
-static struct ap_driver zcrypt_pcixcc_driver = {
- .probe = zcrypt_pcixcc_probe,
- .remove = zcrypt_pcixcc_remove,
- .ids = zcrypt_pcixcc_ids,
- .request_timeout = PCIXCC_CLEANUP_TIME,
+static struct ap_device_id zcrypt_pcixcc_card_ids[] = {
+ { .dev_type = AP_DEVICE_TYPE_PCIXCC,
+ .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
+ { .dev_type = AP_DEVICE_TYPE_CEX2C,
+ .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
+ { .dev_type = AP_DEVICE_TYPE_CEX3C,
+ .match_flags = AP_DEVICE_ID_MATCH_CARD_TYPE },
+ { /* end of list */ },
};
-/**
- * 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;
+MODULE_DEVICE_TABLE(ap, zcrypt_pcixcc_card_ids);
- 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++) {
- msleep(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;
- }
+static struct ap_device_id zcrypt_pcixcc_queue_ids[] = {
+ { .dev_type = AP_DEVICE_TYPE_PCIXCC,
+ .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+ { .dev_type = AP_DEVICE_TYPE_CEX2C,
+ .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+ { .dev_type = AP_DEVICE_TYPE_CEX3C,
+ .match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE },
+ { /* end of list */ },
+};
- 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;
-}
+MODULE_DEVICE_TABLE(ap, zcrypt_pcixcc_queue_ids);
/**
* Large random number detection function. Its sends a message to a pcixcc
@@ -211,15 +99,25 @@ out_free:
*
* Returns 1 if large random numbers are supported, 0 if not and < 0 on error.
*/
-static int zcrypt_pcixcc_rng_supported(struct ap_device *ap_dev)
+static int zcrypt_pcixcc_rng_supported(struct ap_queue *aq)
{
struct ap_message ap_msg;
unsigned long long psmid;
+ unsigned int domain;
struct {
struct type86_hdr hdr;
struct type86_fmt2_ext fmt2;
struct CPRBX cprbx;
} __attribute__((packed)) *reply;
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ char function_code[2];
+ short int rule_length;
+ char rule[8];
+ short int verb_length;
+ short int key_length;
+ } __packed * msg;
int rc, i;
ap_init_message(&ap_msg);
@@ -227,8 +125,12 @@ static int zcrypt_pcixcc_rng_supported(struct ap_device *ap_dev)
if (!ap_msg.message)
return -ENOMEM;
- rng_type6CPRB_msgX(ap_dev, &ap_msg, 4);
- rc = ap_send(ap_dev->qid, 0x0102030405060708ULL, ap_msg.message,
+ rng_type6CPRB_msgX(&ap_msg, 4, &domain);
+
+ msg = ap_msg.message;
+ msg->cprbx.domain = AP_QID_QUEUE(aq->qid);
+
+ rc = ap_send(aq->qid, 0x0102030405060708ULL, ap_msg.message,
ap_msg.length);
if (rc)
goto out_free;
@@ -236,7 +138,7 @@ static int zcrypt_pcixcc_rng_supported(struct ap_device *ap_dev)
/* Wait for the test message to complete. */
for (i = 0; i < 2 * HZ; i++) {
msleep(1000 / HZ);
- rc = ap_recv(ap_dev->qid, &psmid, ap_msg.message, 4096);
+ rc = ap_recv(aq->qid, &psmid, ap_msg.message, 4096);
if (rc == 0 && psmid == 0x0102030405060708ULL)
break;
}
@@ -258,110 +160,168 @@ out_free:
}
/**
- * 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.
+ * Probe function for PCIXCC/CEX2C card devices. 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 card device.
*/
-static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
+static int zcrypt_pcixcc_card_probe(struct ap_device *ap_dev)
{
- struct zcrypt_device *zdev;
+ /*
+ * Normalized speed ratings per crypto adapter
+ * MEX_1k, MEX_2k, MEX_4k, CRT_1k, CRT_2k, CRT_4k, RNG, SECKEY
+ */
+ static const int CEX2C_SPEED_IDX[] = {
+ 1000, 1400, 2400, 1100, 1500, 2600, 100, 12};
+ static const int CEX3C_SPEED_IDX[] = {
+ 500, 700, 1400, 550, 800, 1500, 80, 10};
+
+ struct ap_card *ac = to_ap_card(&ap_dev->device);
+ struct zcrypt_card *zc;
int rc = 0;
- zdev = zcrypt_device_alloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE);
- if (!zdev)
+ zc = zcrypt_card_alloc();
+ if (!zc)
return -ENOMEM;
- zdev->ap_dev = ap_dev;
- zdev->online = 1;
- switch (ap_dev->device_type) {
- case 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;
- zdev->max_exp_bit_length = PCIXCC_MAX_MOD_SIZE;
- } else {
- zdev->type_string = "PCIXCC_MCL3";
- zdev->speed_rating = PCIXCC_MCL3_SPEED_RATING;
- zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE;
- zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE;
- zdev->max_exp_bit_length = PCIXCC_MAX_MOD_SIZE;
- }
- break;
+ zc->card = ac;
+ ac->private = zc;
+ switch (ac->ap_dev.device_type) {
case AP_DEVICE_TYPE_CEX2C:
- 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;
- zdev->max_exp_bit_length = PCIXCC_MAX_MOD_SIZE;
+ zc->user_space_type = ZCRYPT_CEX2C;
+ zc->type_string = "CEX2C";
+ memcpy(zc->speed_rating, CEX2C_SPEED_IDX,
+ sizeof(CEX2C_SPEED_IDX));
+ zc->min_mod_size = PCIXCC_MIN_MOD_SIZE;
+ zc->max_mod_size = PCIXCC_MAX_MOD_SIZE;
+ zc->max_exp_bit_length = PCIXCC_MAX_MOD_SIZE;
break;
case AP_DEVICE_TYPE_CEX3C:
- zdev->user_space_type = ZCRYPT_CEX3C;
- zdev->type_string = "CEX3C";
- zdev->speed_rating = CEX3C_SPEED_RATING;
- zdev->min_mod_size = CEX3C_MIN_MOD_SIZE;
- zdev->max_mod_size = CEX3C_MAX_MOD_SIZE;
- zdev->max_exp_bit_length = CEX3C_MAX_MOD_SIZE;
+ zc->user_space_type = ZCRYPT_CEX3C;
+ zc->type_string = "CEX3C";
+ memcpy(zc->speed_rating, CEX3C_SPEED_IDX,
+ sizeof(CEX3C_SPEED_IDX));
+ zc->min_mod_size = CEX3C_MIN_MOD_SIZE;
+ zc->max_mod_size = CEX3C_MAX_MOD_SIZE;
+ zc->max_exp_bit_length = CEX3C_MAX_MOD_SIZE;
break;
default:
- goto out_free;
+ zcrypt_card_free(zc);
+ return -ENODEV;
}
+ zc->online = 1;
+
+ rc = zcrypt_card_register(zc);
+ if (rc) {
+ ac->private = NULL;
+ zcrypt_card_free(zc);
+ }
+
+ return rc;
+}
- rc = zcrypt_pcixcc_rng_supported(ap_dev);
+/**
+ * This is called to remove the PCIXCC/CEX2C card driver information
+ * if an AP card device is removed.
+ */
+static void zcrypt_pcixcc_card_remove(struct ap_device *ap_dev)
+{
+ struct zcrypt_card *zc = to_ap_card(&ap_dev->device)->private;
+
+ if (zc)
+ zcrypt_card_unregister(zc);
+}
+
+static struct ap_driver zcrypt_pcixcc_card_driver = {
+ .probe = zcrypt_pcixcc_card_probe,
+ .remove = zcrypt_pcixcc_card_remove,
+ .ids = zcrypt_pcixcc_card_ids,
+};
+
+/**
+ * Probe function for PCIXCC/CEX2C queue devices. 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 card device.
+ */
+static int zcrypt_pcixcc_queue_probe(struct ap_device *ap_dev)
+{
+ struct ap_queue *aq = to_ap_queue(&ap_dev->device);
+ struct zcrypt_queue *zq;
+ int rc;
+
+ zq = zcrypt_queue_alloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE);
+ if (!zq)
+ return -ENOMEM;
+ zq->queue = aq;
+ zq->online = 1;
+ atomic_set(&zq->load, 0);
+ rc = zcrypt_pcixcc_rng_supported(aq);
if (rc < 0) {
- zcrypt_device_free(zdev);
+ zcrypt_queue_free(zq);
return rc;
}
if (rc)
- zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
- MSGTYPE06_VARIANT_DEFAULT);
+ zq->ops = zcrypt_msgtype(MSGTYPE06_NAME,
+ MSGTYPE06_VARIANT_DEFAULT);
else
- zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
- MSGTYPE06_VARIANT_NORNG);
- ap_device_init_reply(ap_dev, &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_msgtype_release(zdev->ops);
- zcrypt_device_free(zdev);
+ zq->ops = zcrypt_msgtype(MSGTYPE06_NAME,
+ MSGTYPE06_VARIANT_NORNG);
+ ap_queue_init_reply(aq, &zq->reply);
+ aq->request_timeout = PCIXCC_CLEANUP_TIME,
+ aq->private = zq;
+ rc = zcrypt_queue_register(zq);
+ if (rc) {
+ aq->private = NULL;
+ zcrypt_queue_free(zq);
+ }
return rc;
}
/**
- * This is called to remove the extended PCIXCC/CEX2C driver information
- * if an AP device is removed.
+ * This is called to remove the PCIXCC/CEX2C queue driver information
+ * if an AP queue device is removed.
*/
-static void zcrypt_pcixcc_remove(struct ap_device *ap_dev)
+static void zcrypt_pcixcc_queue_remove(struct ap_device *ap_dev)
{
- struct zcrypt_device *zdev = ap_dev->private;
- struct zcrypt_ops *zops = zdev->ops;
+ struct ap_queue *aq = to_ap_queue(&ap_dev->device);
+ struct zcrypt_queue *zq = aq->private;
- zcrypt_device_unregister(zdev);
- zcrypt_msgtype_release(zops);
+ ap_queue_remove(aq);
+ if (zq)
+ zcrypt_queue_unregister(zq);
}
+static struct ap_driver zcrypt_pcixcc_queue_driver = {
+ .probe = zcrypt_pcixcc_queue_probe,
+ .remove = zcrypt_pcixcc_queue_remove,
+ .suspend = ap_queue_suspend,
+ .resume = ap_queue_resume,
+ .ids = zcrypt_pcixcc_queue_ids,
+};
+
int __init zcrypt_pcixcc_init(void)
{
- return ap_driver_register(&zcrypt_pcixcc_driver, THIS_MODULE, "pcixcc");
+ int rc;
+
+ rc = ap_driver_register(&zcrypt_pcixcc_card_driver,
+ THIS_MODULE, "pcixcccard");
+ if (rc)
+ return rc;
+
+ rc = ap_driver_register(&zcrypt_pcixcc_queue_driver,
+ THIS_MODULE, "pcixccqueue");
+ if (rc)
+ ap_driver_unregister(&zcrypt_pcixcc_card_driver);
+
+ return rc;
}
void zcrypt_pcixcc_exit(void)
{
- ap_driver_unregister(&zcrypt_pcixcc_driver);
+ ap_driver_unregister(&zcrypt_pcixcc_queue_driver);
+ ap_driver_unregister(&zcrypt_pcixcc_card_driver);
}
module_init(zcrypt_pcixcc_init);
diff --git a/drivers/s390/crypto/zcrypt_queue.c b/drivers/s390/crypto/zcrypt_queue.c
new file mode 100644
index 000000000000..a303f3b2c328
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_queue.c
@@ -0,0 +1,226 @@
+/*
+ * zcrypt 2.1.0
+ *
+ * Copyright IBM Corp. 2001, 2012
+ * 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>
+ * MSGTYPE restruct: Holger Dengler <hd@linux.vnet.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.
+ */
+
+#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/seq_file.h>
+#include <linux/compat.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+#include <linux/hw_random.h>
+#include <linux/debugfs.h>
+#include <asm/debug.h>
+
+#include "zcrypt_debug.h"
+#include "zcrypt_api.h"
+
+#include "zcrypt_msgtype6.h"
+#include "zcrypt_msgtype50.h"
+
+/*
+ * Device attributes common for all crypto queue devices.
+ */
+
+static ssize_t zcrypt_queue_online_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct zcrypt_queue *zq = to_ap_queue(dev)->private;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", zq->online);
+}
+
+static ssize_t zcrypt_queue_online_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct zcrypt_queue *zq = to_ap_queue(dev)->private;
+ struct zcrypt_card *zc = zq->zcard;
+ int online;
+
+ if (sscanf(buf, "%d\n", &online) != 1 || online < 0 || online > 1)
+ return -EINVAL;
+
+ if (online && !zc->online)
+ return -EINVAL;
+ zq->online = online;
+
+ ZCRYPT_DBF(DBF_INFO, "queue=%02x.%04x online=%d\n",
+ AP_QID_CARD(zq->queue->qid),
+ AP_QID_QUEUE(zq->queue->qid),
+ online);
+
+ if (!online)
+ ap_flush_queue(zq->queue);
+ return count;
+}
+
+static DEVICE_ATTR(online, 0644, zcrypt_queue_online_show,
+ zcrypt_queue_online_store);
+
+static struct attribute *zcrypt_queue_attrs[] = {
+ &dev_attr_online.attr,
+ NULL,
+};
+
+static struct attribute_group zcrypt_queue_attr_group = {
+ .attrs = zcrypt_queue_attrs,
+};
+
+void zcrypt_queue_force_online(struct zcrypt_queue *zq, int online)
+{
+ zq->online = online;
+ if (!online)
+ ap_flush_queue(zq->queue);
+}
+
+struct zcrypt_queue *zcrypt_queue_alloc(size_t max_response_size)
+{
+ struct zcrypt_queue *zq;
+
+ zq = kzalloc(sizeof(struct zcrypt_queue), GFP_KERNEL);
+ if (!zq)
+ return NULL;
+ zq->reply.message = kmalloc(max_response_size, GFP_KERNEL);
+ if (!zq->reply.message)
+ goto out_free;
+ zq->reply.length = max_response_size;
+ INIT_LIST_HEAD(&zq->list);
+ kref_init(&zq->refcount);
+ return zq;
+
+out_free:
+ kfree(zq);
+ return NULL;
+}
+EXPORT_SYMBOL(zcrypt_queue_alloc);
+
+void zcrypt_queue_free(struct zcrypt_queue *zq)
+{
+ kfree(zq->reply.message);
+ kfree(zq);
+}
+EXPORT_SYMBOL(zcrypt_queue_free);
+
+static void zcrypt_queue_release(struct kref *kref)
+{
+ struct zcrypt_queue *zq =
+ container_of(kref, struct zcrypt_queue, refcount);
+ zcrypt_queue_free(zq);
+}
+
+void zcrypt_queue_get(struct zcrypt_queue *zq)
+{
+ kref_get(&zq->refcount);
+}
+EXPORT_SYMBOL(zcrypt_queue_get);
+
+int zcrypt_queue_put(struct zcrypt_queue *zq)
+{
+ return kref_put(&zq->refcount, zcrypt_queue_release);
+}
+EXPORT_SYMBOL(zcrypt_queue_put);
+
+/**
+ * zcrypt_queue_register() - Register a crypto queue device.
+ * @zq: Pointer to a crypto queue device
+ *
+ * Register a crypto queue device. Returns 0 if successful.
+ */
+int zcrypt_queue_register(struct zcrypt_queue *zq)
+{
+ struct zcrypt_card *zc;
+ int rc;
+
+ spin_lock(&zcrypt_list_lock);
+ zc = zq->queue->card->private;
+ zcrypt_card_get(zc);
+ zq->zcard = zc;
+ zq->online = 1; /* New devices are online by default. */
+
+ ZCRYPT_DBF(DBF_INFO, "queue=%02x.%04x register online=1\n",
+ AP_QID_CARD(zq->queue->qid), AP_QID_QUEUE(zq->queue->qid));
+
+ list_add_tail(&zq->list, &zc->zqueues);
+ zcrypt_device_count++;
+ spin_unlock(&zcrypt_list_lock);
+
+ rc = sysfs_create_group(&zq->queue->ap_dev.device.kobj,
+ &zcrypt_queue_attr_group);
+ if (rc)
+ goto out;
+ get_device(&zq->queue->ap_dev.device);
+
+ if (zq->ops->rng) {
+ rc = zcrypt_rng_device_add();
+ if (rc)
+ goto out_unregister;
+ }
+ return 0;
+
+out_unregister:
+ sysfs_remove_group(&zq->queue->ap_dev.device.kobj,
+ &zcrypt_queue_attr_group);
+ put_device(&zq->queue->ap_dev.device);
+out:
+ spin_lock(&zcrypt_list_lock);
+ list_del_init(&zq->list);
+ spin_unlock(&zcrypt_list_lock);
+ zcrypt_card_put(zc);
+ return rc;
+}
+EXPORT_SYMBOL(zcrypt_queue_register);
+
+/**
+ * zcrypt_queue_unregister(): Unregister a crypto queue device.
+ * @zq: Pointer to crypto queue device
+ *
+ * Unregister a crypto queue device.
+ */
+void zcrypt_queue_unregister(struct zcrypt_queue *zq)
+{
+ struct zcrypt_card *zc;
+
+ ZCRYPT_DBF(DBF_INFO, "queue=%02x.%04x unregister\n",
+ AP_QID_CARD(zq->queue->qid), AP_QID_QUEUE(zq->queue->qid));
+
+ zc = zq->zcard;
+ spin_lock(&zcrypt_list_lock);
+ list_del_init(&zq->list);
+ zcrypt_device_count--;
+ spin_unlock(&zcrypt_list_lock);
+ zcrypt_card_put(zc);
+ if (zq->ops->rng)
+ zcrypt_rng_device_remove();
+ sysfs_remove_group(&zq->queue->ap_dev.device.kobj,
+ &zcrypt_queue_attr_group);
+ put_device(&zq->queue->ap_dev.device);
+ zcrypt_queue_put(zq);
+}
+EXPORT_SYMBOL(zcrypt_queue_unregister);
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index ad17fc5883f6..ac65f12bcd43 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -1032,9 +1032,6 @@ static int ctcm_change_mtu(struct net_device *dev, int new_mtu)
struct ctcm_priv *priv;
int max_bufsize;
- if (new_mtu < 576 || new_mtu > 65527)
- return -EINVAL;
-
priv = dev->ml_priv;
max_bufsize = priv->channel[CTCM_READ]->max_bufsize;
@@ -1123,6 +1120,8 @@ void static ctcm_dev_setup(struct net_device *dev)
dev->type = ARPHRD_SLIP;
dev->tx_queue_len = 100;
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+ dev->min_mtu = 576;
+ dev->max_mtu = 65527;
}
/*
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index 251db0a02e73..211b31d9f157 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -1888,7 +1888,7 @@ lcs_stop_device(struct net_device *dev)
rc = lcs_stopcard(card);
if (rc)
dev_err(&card->dev->dev,
- " Shutting down the LCS device failed\n ");
+ " Shutting down the LCS device failed\n");
return rc;
}
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index b0e8ffdf864b..3f85b97ab8d2 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -62,7 +62,7 @@
#include <net/dst.h>
#include <asm/io.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/ebcdic.h>
#include <net/iucv/iucv.h>
@@ -302,8 +302,7 @@ static char *netiucv_printuser(struct iucv_connection *conn)
if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) {
tmp_uid[8] = '\0';
tmp_udat[16] = '\0';
- memcpy(tmp_uid, conn->userid, 8);
- memcpy(tmp_uid, netiucv_printname(tmp_uid, 8), 8);
+ memcpy(tmp_uid, netiucv_printname(conn->userid, 8), 8);
memcpy(tmp_udat, conn->userdata, 16);
EBCASC(tmp_udat, 16);
memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16);
@@ -1429,27 +1428,6 @@ static struct net_device_stats *netiucv_stats (struct net_device * dev)
return &priv->stats;
}
-/**
- * netiucv_change_mtu
- * @dev: Pointer to interface struct.
- * @new_mtu: The new MTU to use for this interface.
- *
- * Sets MTU of an interface.
- *
- * Returns 0 on success, -EINVAL if MTU is out of valid range.
- * (valid range is 576 .. NETIUCV_MTU_MAX).
- */
-static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
-{
- IUCV_DBF_TEXT(trace, 3, __func__);
- if (new_mtu < 576 || new_mtu > NETIUCV_MTU_MAX) {
- IUCV_DBF_TEXT(setup, 2, "given MTU out of valid range\n");
- return -EINVAL;
- }
- dev->mtu = new_mtu;
- return 0;
-}
-
/*
* attributes in sysfs
*/
@@ -1564,21 +1542,21 @@ static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
{
struct netiucv_priv *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->conn->netdev;
- char *e;
- int bs1;
+ unsigned int bs1;
+ int rc;
IUCV_DBF_TEXT(trace, 3, __func__);
if (count >= 39)
return -EINVAL;
- bs1 = simple_strtoul(buf, &e, 0);
+ rc = kstrtouint(buf, 0, &bs1);
- if (e && (!isspace(*e))) {
- IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %02x\n",
- *e);
+ if (rc == -EINVAL) {
+ IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %s\n",
+ buf);
return -EINVAL;
}
- if (bs1 > NETIUCV_BUFSIZE_MAX) {
+ if ((rc == -ERANGE) || (bs1 > NETIUCV_BUFSIZE_MAX)) {
IUCV_DBF_TEXT_(setup, 2,
"buffer_write: buffer size %d too large\n",
bs1);
@@ -1987,12 +1965,13 @@ static const struct net_device_ops netiucv_netdev_ops = {
.ndo_stop = netiucv_close,
.ndo_get_stats = netiucv_stats,
.ndo_start_xmit = netiucv_tx,
- .ndo_change_mtu = netiucv_change_mtu,
};
static void netiucv_setup_netdevice(struct net_device *dev)
{
dev->mtu = NETIUCV_MTU_DEFAULT;
+ dev->min_mtu = 576;
+ dev->max_mtu = NETIUCV_MTU_MAX;
dev->destructor = netiucv_free_netdevice;
dev->hard_header_len = NETIUCV_HDRLEN;
dev->addr_len = 0;
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 20cf29613043..e33558313834 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -4202,10 +4202,6 @@ int qeth_change_mtu(struct net_device *dev, int new_mtu)
sprintf(dbf_text, "%8x", new_mtu);
QETH_CARD_TEXT(card, 4, dbf_text);
- if (new_mtu < 64)
- return -EINVAL;
- if (new_mtu > 65535)
- return -EINVAL;
if ((!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) &&
(!qeth_mtu_is_valid(card, new_mtu)))
return -EINVAL;
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index bb27058fa9f0..9c921c2833f1 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -1107,6 +1107,8 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
card->dev->ml_priv = card;
card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
card->dev->mtu = card->info.initial_mtu;
+ card->dev->min_mtu = 64;
+ card->dev->max_mtu = ETH_MAX_MTU;
card->dev->netdev_ops = &qeth_l2_netdev_ops;
card->dev->ethtool_ops =
(card->info.type != QETH_CARD_TYPE_OSN) ?
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 272d9e7419be..ac37d050e765 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -3140,6 +3140,8 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
card->dev->ml_priv = card;
card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
card->dev->mtu = card->info.initial_mtu;
+ card->dev->min_mtu = 64;
+ card->dev->max_mtu = ETH_MAX_MTU;
card->dev->ethtool_ops = &qeth_l3_ethtool_ops;
card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_CTAG_RX |
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index c00ac4650dce..bcc8f3dfd4c4 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -310,7 +310,7 @@ static int zfcp_setup_adapter_work_queue(struct zfcp_adapter *adapter)
snprintf(name, sizeof(name), "zfcp_q_%s",
dev_name(&adapter->ccw_device->dev));
- adapter->work_queue = create_singlethread_workqueue(name);
+ adapter->work_queue = alloc_ordered_workqueue(name, WQ_MEM_RECLAIM);
if (adapter->work_queue)
return 0;
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 5d7fbe4e907e..d5bf36ec8a75 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -3,7 +3,7 @@
*
* Debug traces for zfcp.
*
- * Copyright IBM Corp. 2002, 2013
+ * Copyright IBM Corp. 2002, 2016
*/
#define KMSG_COMPONENT "zfcp"
@@ -65,7 +65,7 @@ void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area,
* @tag: tag indicating which kind of unsolicited status has been received
* @req: request for which a response was received
*/
-void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req)
+void zfcp_dbf_hba_fsf_res(char *tag, int level, struct zfcp_fsf_req *req)
{
struct zfcp_dbf *dbf = req->adapter->dbf;
struct fsf_qtcb_prefix *q_pref = &req->qtcb->prefix;
@@ -85,6 +85,8 @@ void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req)
rec->u.res.req_issued = req->issued;
rec->u.res.prot_status = q_pref->prot_status;
rec->u.res.fsf_status = q_head->fsf_status;
+ rec->u.res.port_handle = q_head->port_handle;
+ rec->u.res.lun_handle = q_head->lun_handle;
memcpy(rec->u.res.prot_status_qual, &q_pref->prot_status_qual,
FSF_PROT_STATUS_QUAL_SIZE);
@@ -97,7 +99,7 @@ void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req)
rec->pl_len, "fsf_res", req->req_id);
}
- debug_event(dbf->hba, 1, rec, sizeof(*rec));
+ debug_event(dbf->hba, level, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->hba_lock, flags);
}
@@ -241,7 +243,8 @@ static void zfcp_dbf_set_common(struct zfcp_dbf_rec *rec,
if (sdev) {
rec->lun_status = atomic_read(&sdev_to_zfcp(sdev)->status);
rec->lun = zfcp_scsi_dev_lun(sdev);
- }
+ } else
+ rec->lun = ZFCP_DBF_INVALID_LUN;
}
/**
@@ -286,11 +289,12 @@ void zfcp_dbf_rec_trig(char *tag, struct zfcp_adapter *adapter,
/**
- * zfcp_dbf_rec_run - trace event related to running recovery
+ * zfcp_dbf_rec_run_lvl - trace event related to running recovery
+ * @level: trace level to be used for event
* @tag: identifier for event
* @erp: erp_action running
*/
-void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
+void zfcp_dbf_rec_run_lvl(int level, char *tag, struct zfcp_erp_action *erp)
{
struct zfcp_dbf *dbf = erp->adapter->dbf;
struct zfcp_dbf_rec *rec = &dbf->rec_buf;
@@ -316,17 +320,62 @@ void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
else
rec->u.run.rec_count = atomic_read(&erp->adapter->erp_counter);
+ debug_event(dbf->rec, level, rec, sizeof(*rec));
+ spin_unlock_irqrestore(&dbf->rec_lock, flags);
+}
+
+/**
+ * zfcp_dbf_rec_run - trace event related to running recovery
+ * @tag: identifier for event
+ * @erp: erp_action running
+ */
+void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
+{
+ zfcp_dbf_rec_run_lvl(1, tag, erp);
+}
+
+/**
+ * zfcp_dbf_rec_run_wka - trace wka port event with info like running recovery
+ * @tag: identifier for event
+ * @wka_port: well known address port
+ * @req_id: request ID to correlate with potential HBA trace record
+ */
+void zfcp_dbf_rec_run_wka(char *tag, struct zfcp_fc_wka_port *wka_port,
+ u64 req_id)
+{
+ struct zfcp_dbf *dbf = wka_port->adapter->dbf;
+ struct zfcp_dbf_rec *rec = &dbf->rec_buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dbf->rec_lock, flags);
+ memset(rec, 0, sizeof(*rec));
+
+ rec->id = ZFCP_DBF_REC_RUN;
+ memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN);
+ rec->port_status = wka_port->status;
+ rec->d_id = wka_port->d_id;
+ rec->lun = ZFCP_DBF_INVALID_LUN;
+
+ rec->u.run.fsf_req_id = req_id;
+ rec->u.run.rec_status = ~0;
+ rec->u.run.rec_step = ~0;
+ rec->u.run.rec_action = ~0;
+ rec->u.run.rec_count = ~0;
+
debug_event(dbf->rec, 1, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
static inline
-void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len,
- u64 req_id, u32 d_id)
+void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf,
+ char *paytag, struct scatterlist *sg, u8 id, u16 len,
+ u64 req_id, u32 d_id, u16 cap_len)
{
struct zfcp_dbf_san *rec = &dbf->san_buf;
u16 rec_len;
unsigned long flags;
+ struct zfcp_dbf_pay *payload = &dbf->pay_buf;
+ u16 pay_sum = 0;
spin_lock_irqsave(&dbf->san_lock, flags);
memset(rec, 0, sizeof(*rec));
@@ -334,10 +383,41 @@ void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len,
rec->id = id;
rec->fsf_req_id = req_id;
rec->d_id = d_id;
- rec_len = min(len, (u16)ZFCP_DBF_SAN_MAX_PAYLOAD);
- memcpy(rec->payload, data, rec_len);
memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN);
+ rec->pl_len = len; /* full length even if we cap pay below */
+ if (!sg)
+ goto out;
+ rec_len = min_t(unsigned int, sg->length, ZFCP_DBF_SAN_MAX_PAYLOAD);
+ memcpy(rec->payload, sg_virt(sg), rec_len); /* part of 1st sg entry */
+ if (len <= rec_len)
+ goto out; /* skip pay record if full content in rec->payload */
+
+ /* if (len > rec_len):
+ * dump data up to cap_len ignoring small duplicate in rec->payload
+ */
+ spin_lock(&dbf->pay_lock);
+ memset(payload, 0, sizeof(*payload));
+ memcpy(payload->area, paytag, ZFCP_DBF_TAG_LEN);
+ payload->fsf_req_id = req_id;
+ payload->counter = 0;
+ for (; sg && pay_sum < cap_len; sg = sg_next(sg)) {
+ u16 pay_len, offset = 0;
+
+ while (offset < sg->length && pay_sum < cap_len) {
+ pay_len = min((u16)ZFCP_DBF_PAY_MAX_REC,
+ (u16)(sg->length - offset));
+ /* cap_len <= pay_sum < cap_len+ZFCP_DBF_PAY_MAX_REC */
+ memcpy(payload->data, sg_virt(sg) + offset, pay_len);
+ debug_event(dbf->pay, 1, payload,
+ zfcp_dbf_plen(pay_len));
+ payload->counter++;
+ offset += pay_len;
+ pay_sum += pay_len;
+ }
+ }
+ spin_unlock(&dbf->pay_lock);
+out:
debug_event(dbf->san, 1, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->san_lock, flags);
}
@@ -354,9 +434,62 @@ void zfcp_dbf_san_req(char *tag, struct zfcp_fsf_req *fsf, u32 d_id)
struct zfcp_fsf_ct_els *ct_els = fsf->data;
u16 length;
- length = (u16)(ct_els->req->length + FC_CT_HDR_LEN);
- zfcp_dbf_san(tag, dbf, sg_virt(ct_els->req), ZFCP_DBF_SAN_REQ, length,
- fsf->req_id, d_id);
+ length = (u16)zfcp_qdio_real_bytes(ct_els->req);
+ zfcp_dbf_san(tag, dbf, "san_req", ct_els->req, ZFCP_DBF_SAN_REQ,
+ length, fsf->req_id, d_id, length);
+}
+
+static u16 zfcp_dbf_san_res_cap_len_if_gpn_ft(char *tag,
+ struct zfcp_fsf_req *fsf,
+ u16 len)
+{
+ struct zfcp_fsf_ct_els *ct_els = fsf->data;
+ struct fc_ct_hdr *reqh = sg_virt(ct_els->req);
+ struct fc_ns_gid_ft *reqn = (struct fc_ns_gid_ft *)(reqh + 1);
+ struct scatterlist *resp_entry = ct_els->resp;
+ struct fc_gpn_ft_resp *acc;
+ int max_entries, x, last = 0;
+
+ if (!(memcmp(tag, "fsscth2", 7) == 0
+ && ct_els->d_id == FC_FID_DIR_SERV
+ && reqh->ct_rev == FC_CT_REV
+ && reqh->ct_in_id[0] == 0
+ && reqh->ct_in_id[1] == 0
+ && reqh->ct_in_id[2] == 0
+ && reqh->ct_fs_type == FC_FST_DIR
+ && reqh->ct_fs_subtype == FC_NS_SUBTYPE
+ && reqh->ct_options == 0
+ && reqh->_ct_resvd1 == 0
+ && reqh->ct_cmd == FC_NS_GPN_FT
+ /* reqh->ct_mr_size can vary so do not match but read below */
+ && reqh->_ct_resvd2 == 0
+ && reqh->ct_reason == 0
+ && reqh->ct_explan == 0
+ && reqh->ct_vendor == 0
+ && reqn->fn_resvd == 0
+ && reqn->fn_domain_id_scope == 0
+ && reqn->fn_area_id_scope == 0
+ && reqn->fn_fc4_type == FC_TYPE_FCP))
+ return len; /* not GPN_FT response so do not cap */
+
+ acc = sg_virt(resp_entry);
+ max_entries = (reqh->ct_mr_size * 4 / sizeof(struct fc_gpn_ft_resp))
+ + 1 /* zfcp_fc_scan_ports: bytes correct, entries off-by-one
+ * to account for header as 1st pseudo "entry" */;
+
+ /* the basic CT_IU preamble is the same size as one entry in the GPN_FT
+ * response, allowing us to skip special handling for it - just skip it
+ */
+ for (x = 1; x < max_entries && !last; x++) {
+ if (x % (ZFCP_FC_GPN_FT_ENT_PAGE + 1))
+ acc++;
+ else
+ acc = sg_virt(++resp_entry);
+
+ last = acc->fp_flags & FC_NS_FID_LAST;
+ }
+ len = min(len, (u16)(x * sizeof(struct fc_gpn_ft_resp)));
+ return len; /* cap after last entry */
}
/**
@@ -370,9 +503,10 @@ void zfcp_dbf_san_res(char *tag, struct zfcp_fsf_req *fsf)
struct zfcp_fsf_ct_els *ct_els = fsf->data;
u16 length;
- length = (u16)(ct_els->resp->length + FC_CT_HDR_LEN);
- zfcp_dbf_san(tag, dbf, sg_virt(ct_els->resp), ZFCP_DBF_SAN_RES, length,
- fsf->req_id, 0);
+ length = (u16)zfcp_qdio_real_bytes(ct_els->resp);
+ zfcp_dbf_san(tag, dbf, "san_res", ct_els->resp, ZFCP_DBF_SAN_RES,
+ length, fsf->req_id, ct_els->d_id,
+ zfcp_dbf_san_res_cap_len_if_gpn_ft(tag, fsf, length));
}
/**
@@ -386,11 +520,13 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf)
struct fsf_status_read_buffer *srb =
(struct fsf_status_read_buffer *) fsf->data;
u16 length;
+ struct scatterlist sg;
length = (u16)(srb->length -
offsetof(struct fsf_status_read_buffer, payload));
- zfcp_dbf_san(tag, dbf, srb->payload.data, ZFCP_DBF_SAN_ELS, length,
- fsf->req_id, ntoh24(srb->d_id));
+ sg_init_one(&sg, srb->payload.data, length);
+ zfcp_dbf_san(tag, dbf, "san_els", &sg, ZFCP_DBF_SAN_ELS, length,
+ fsf->req_id, ntoh24(srb->d_id), length);
}
/**
@@ -399,7 +535,8 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf)
* @sc: pointer to struct scsi_cmnd
* @fsf: pointer to struct zfcp_fsf_req
*/
-void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf)
+void zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *sc,
+ struct zfcp_fsf_req *fsf)
{
struct zfcp_adapter *adapter =
(struct zfcp_adapter *) sc->device->host->hostdata[0];
@@ -442,7 +579,7 @@ void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf)
}
}
- debug_event(dbf->scsi, 1, rec, sizeof(*rec));
+ debug_event(dbf->scsi, level, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->scsi_lock, flags);
}
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 0be3d48681ae..db186d44cfaf 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -2,7 +2,7 @@
* zfcp device driver
* debug feature declarations
*
- * Copyright IBM Corp. 2008, 2010
+ * Copyright IBM Corp. 2008, 2016
*/
#ifndef ZFCP_DBF_H
@@ -17,6 +17,11 @@
#define ZFCP_DBF_INVALID_LUN 0xFFFFFFFFFFFFFFFFull
+enum zfcp_dbf_pseudo_erp_act_type {
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD = 0xff,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL = 0xfe,
+};
+
/**
* struct zfcp_dbf_rec_trigger - trace record for triggered recovery action
* @ready: number of ready recovery actions
@@ -110,6 +115,7 @@ struct zfcp_dbf_san {
u32 d_id;
#define ZFCP_DBF_SAN_MAX_PAYLOAD (FC_CT_HDR_LEN + 32)
char payload[ZFCP_DBF_SAN_MAX_PAYLOAD];
+ u16 pl_len;
} __packed;
/**
@@ -126,6 +132,8 @@ struct zfcp_dbf_hba_res {
u8 prot_status_qual[FSF_PROT_STATUS_QUAL_SIZE];
u32 fsf_status;
u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
+ u32 port_handle;
+ u32 lun_handle;
} __packed;
/**
@@ -275,11 +283,35 @@ struct zfcp_dbf {
struct zfcp_dbf_scsi scsi_buf;
};
+/**
+ * zfcp_dbf_hba_fsf_resp_suppress - true if we should not trace by default
+ * @req: request that has been completed
+ *
+ * Returns true if FCP response with only benign residual under count.
+ */
+static inline
+bool zfcp_dbf_hba_fsf_resp_suppress(struct zfcp_fsf_req *req)
+{
+ struct fsf_qtcb *qtcb = req->qtcb;
+ u32 fsf_stat = qtcb->header.fsf_status;
+ struct fcp_resp *fcp_rsp;
+ u8 rsp_flags, fr_status;
+
+ if (qtcb->prefix.qtcb_type != FSF_IO_COMMAND)
+ return false; /* not an FCP response */
+ fcp_rsp = (struct fcp_resp *)&qtcb->bottom.io.fcp_rsp;
+ rsp_flags = fcp_rsp->fr_flags;
+ fr_status = fcp_rsp->fr_status;
+ return (fsf_stat == FSF_FCP_RSP_AVAILABLE) &&
+ (rsp_flags == FCP_RESID_UNDER) &&
+ (fr_status == SAM_STAT_GOOD);
+}
+
static inline
void zfcp_dbf_hba_fsf_resp(char *tag, int level, struct zfcp_fsf_req *req)
{
if (debug_level_enabled(req->adapter->dbf->hba, level))
- zfcp_dbf_hba_fsf_res(tag, req);
+ zfcp_dbf_hba_fsf_res(tag, level, req);
}
/**
@@ -296,7 +328,9 @@ void zfcp_dbf_hba_fsf_response(struct zfcp_fsf_req *req)
zfcp_dbf_hba_fsf_resp("fs_perr", 1, req);
} else if (qtcb->header.fsf_status != FSF_GOOD) {
- zfcp_dbf_hba_fsf_resp("fs_ferr", 1, req);
+ zfcp_dbf_hba_fsf_resp("fs_ferr",
+ zfcp_dbf_hba_fsf_resp_suppress(req)
+ ? 5 : 1, req);
} else if ((req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) ||
(req->fsf_command == FSF_QTCB_OPEN_LUN)) {
@@ -318,7 +352,7 @@ void _zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *scmd,
scmd->device->host->hostdata[0];
if (debug_level_enabled(adapter->dbf->scsi, level))
- zfcp_dbf_scsi(tag, scmd, req);
+ zfcp_dbf_scsi(tag, level, scmd, req);
}
/**
@@ -380,4 +414,15 @@ void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag)
_zfcp_dbf_scsi(tmp_tag, 1, scmnd, NULL);
}
+/**
+ * zfcp_dbf_scsi_nullcmnd() - trace NULLify of SCSI command in dev/tgt-reset.
+ * @scmnd: SCSI command that was NULLified.
+ * @fsf_req: request that owned @scmnd.
+ */
+static inline void zfcp_dbf_scsi_nullcmnd(struct scsi_cmnd *scmnd,
+ struct zfcp_fsf_req *fsf_req)
+{
+ _zfcp_dbf_scsi("scfc__1", 3, scmnd, fsf_req);
+}
+
#endif /* ZFCP_DBF_H */
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 3fb410977014..7ccfce559034 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -3,7 +3,7 @@
*
* Error Recovery Procedures (ERP).
*
- * Copyright IBM Corp. 2002, 2010
+ * Copyright IBM Corp. 2002, 2016
*/
#define KMSG_COMPONENT "zfcp"
@@ -1204,6 +1204,62 @@ static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
}
}
+/**
+ * zfcp_erp_try_rport_unblock - unblock rport if no more/new recovery
+ * @port: zfcp_port whose fc_rport we should try to unblock
+ */
+static void zfcp_erp_try_rport_unblock(struct zfcp_port *port)
+{
+ unsigned long flags;
+ struct zfcp_adapter *adapter = port->adapter;
+ int port_status;
+ struct Scsi_Host *shost = adapter->scsi_host;
+ struct scsi_device *sdev;
+
+ write_lock_irqsave(&adapter->erp_lock, flags);
+ port_status = atomic_read(&port->status);
+ if ((port_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 ||
+ (port_status & (ZFCP_STATUS_COMMON_ERP_INUSE |
+ ZFCP_STATUS_COMMON_ERP_FAILED)) != 0) {
+ /* new ERP of severity >= port triggered elsewhere meanwhile or
+ * local link down (adapter erp_failed but not clear unblock)
+ */
+ zfcp_dbf_rec_run_lvl(4, "ertru_p", &port->erp_action);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+ return;
+ }
+ spin_lock(shost->host_lock);
+ __shost_for_each_device(sdev, shost) {
+ struct zfcp_scsi_dev *zsdev = sdev_to_zfcp(sdev);
+ int lun_status;
+
+ if (zsdev->port != port)
+ continue;
+ /* LUN under port of interest */
+ lun_status = atomic_read(&zsdev->status);
+ if ((lun_status & ZFCP_STATUS_COMMON_ERP_FAILED) != 0)
+ continue; /* unblock rport despite failed LUNs */
+ /* LUN recovery not given up yet [maybe follow-up pending] */
+ if ((lun_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 ||
+ (lun_status & ZFCP_STATUS_COMMON_ERP_INUSE) != 0) {
+ /* LUN blocked:
+ * not yet unblocked [LUN recovery pending]
+ * or meanwhile blocked [new LUN recovery triggered]
+ */
+ zfcp_dbf_rec_run_lvl(4, "ertru_l", &zsdev->erp_action);
+ spin_unlock(shost->host_lock);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+ return;
+ }
+ }
+ /* now port has no child or all children have completed recovery,
+ * and no ERP of severity >= port was meanwhile triggered elsewhere
+ */
+ zfcp_scsi_schedule_rport_register(port);
+ spin_unlock(shost->host_lock);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
{
struct zfcp_adapter *adapter = act->adapter;
@@ -1214,11 +1270,18 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
case ZFCP_ERP_ACTION_REOPEN_LUN:
if (!(act->status & ZFCP_STATUS_ERP_NO_REF))
scsi_device_put(sdev);
+ zfcp_erp_try_rport_unblock(port);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT:
- if (result == ZFCP_ERP_SUCCEEDED)
- zfcp_scsi_schedule_rport_register(port);
+ /* This switch case might also happen after a forced reopen
+ * was successfully done and thus overwritten with a new
+ * non-forced reopen at `ersfs_2'. In this case, we must not
+ * do the clean-up of the non-forced version.
+ */
+ if (act->step != ZFCP_ERP_STEP_UNINITIALIZED)
+ if (result == ZFCP_ERP_SUCCEEDED)
+ zfcp_erp_try_rport_unblock(port);
/* fall through */
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
put_device(&port->dev);
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 5b500652572b..9afdbc32b23f 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -3,7 +3,7 @@
*
* External function declarations.
*
- * Copyright IBM Corp. 2002, 2010
+ * Copyright IBM Corp. 2002, 2016
*/
#ifndef ZFCP_EXT_H
@@ -35,8 +35,11 @@ extern void zfcp_dbf_adapter_unregister(struct zfcp_adapter *);
extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *,
struct zfcp_port *, struct scsi_device *, u8, u8);
extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *);
+extern void zfcp_dbf_rec_run_lvl(int level, char *tag,
+ struct zfcp_erp_action *erp);
+extern void zfcp_dbf_rec_run_wka(char *, struct zfcp_fc_wka_port *, u64);
extern void zfcp_dbf_hba_fsf_uss(char *, struct zfcp_fsf_req *);
-extern void zfcp_dbf_hba_fsf_res(char *, struct zfcp_fsf_req *);
+extern void zfcp_dbf_hba_fsf_res(char *, int, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_def_err(struct zfcp_adapter *, u64, u16, void **);
@@ -44,7 +47,8 @@ extern void zfcp_dbf_hba_basic(char *, struct zfcp_adapter *);
extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32);
extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *);
extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *);
-extern void zfcp_dbf_scsi(char *, struct scsi_cmnd *, struct zfcp_fsf_req *);
+extern void zfcp_dbf_scsi(char *, int, struct scsi_cmnd *,
+ struct zfcp_fsf_req *);
/* zfcp_erp.c */
extern void zfcp_erp_set_adapter_status(struct zfcp_adapter *, u32);
@@ -82,8 +86,8 @@ extern void zfcp_fc_link_test_work(struct work_struct *);
extern void zfcp_fc_wka_ports_force_offline(struct zfcp_fc_wka_ports *);
extern int zfcp_fc_gs_setup(struct zfcp_adapter *);
extern void zfcp_fc_gs_destroy(struct zfcp_adapter *);
-extern int zfcp_fc_exec_bsg_job(struct fc_bsg_job *);
-extern int zfcp_fc_timeout_bsg_job(struct fc_bsg_job *);
+extern int zfcp_fc_exec_bsg_job(struct bsg_job *);
+extern int zfcp_fc_timeout_bsg_job(struct bsg_job *);
extern void zfcp_fc_sym_name_update(struct work_struct *);
extern unsigned int zfcp_fc_port_scan_backoff(void);
extern void zfcp_fc_conditional_port_scan(struct zfcp_adapter *);
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index 237688af179b..7331eea67435 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/utsname.h>
#include <linux/random.h>
+#include <linux/bsg-lib.h>
#include <scsi/fc/fc_els.h>
#include <scsi/libfc.h>
#include "zfcp_ext.h"
@@ -885,26 +886,30 @@ out_free:
static void zfcp_fc_ct_els_job_handler(void *data)
{
- struct fc_bsg_job *job = data;
+ struct bsg_job *job = data;
struct zfcp_fsf_ct_els *zfcp_ct_els = job->dd_data;
struct fc_bsg_reply *jr = job->reply;
jr->reply_payload_rcv_len = job->reply_payload.payload_len;
jr->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
jr->result = zfcp_ct_els->status ? -EIO : 0;
- job->job_done(job);
+ bsg_job_done(job, jr->result, jr->reply_payload_rcv_len);
}
-static struct zfcp_fc_wka_port *zfcp_fc_job_wka_port(struct fc_bsg_job *job)
+static struct zfcp_fc_wka_port *zfcp_fc_job_wka_port(struct bsg_job *job)
{
u32 preamble_word1;
u8 gs_type;
struct zfcp_adapter *adapter;
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_rport *rport = fc_bsg_to_rport(job);
+ struct Scsi_Host *shost;
- preamble_word1 = job->request->rqst_data.r_ct.preamble_word1;
+ preamble_word1 = bsg_request->rqst_data.r_ct.preamble_word1;
gs_type = (preamble_word1 & 0xff000000) >> 24;
- adapter = (struct zfcp_adapter *) job->shost->hostdata[0];
+ shost = rport ? rport_to_shost(rport) : fc_bsg_to_shost(job);
+ adapter = (struct zfcp_adapter *) shost->hostdata[0];
switch (gs_type) {
case FC_FST_ALIAS:
@@ -924,7 +929,7 @@ static struct zfcp_fc_wka_port *zfcp_fc_job_wka_port(struct fc_bsg_job *job)
static void zfcp_fc_ct_job_handler(void *data)
{
- struct fc_bsg_job *job = data;
+ struct bsg_job *job = data;
struct zfcp_fc_wka_port *wka_port;
wka_port = zfcp_fc_job_wka_port(job);
@@ -933,11 +938,12 @@ static void zfcp_fc_ct_job_handler(void *data)
zfcp_fc_ct_els_job_handler(data);
}
-static int zfcp_fc_exec_els_job(struct fc_bsg_job *job,
+static int zfcp_fc_exec_els_job(struct bsg_job *job,
struct zfcp_adapter *adapter)
{
struct zfcp_fsf_ct_els *els = job->dd_data;
- struct fc_rport *rport = job->rport;
+ struct fc_rport *rport = fc_bsg_to_rport(job);
+ struct fc_bsg_request *bsg_request = job->request;
struct zfcp_port *port;
u32 d_id;
@@ -949,13 +955,13 @@ static int zfcp_fc_exec_els_job(struct fc_bsg_job *job,
d_id = port->d_id;
put_device(&port->dev);
} else
- d_id = ntoh24(job->request->rqst_data.h_els.port_id);
+ d_id = ntoh24(bsg_request->rqst_data.h_els.port_id);
els->handler = zfcp_fc_ct_els_job_handler;
return zfcp_fsf_send_els(adapter, d_id, els, job->req->timeout / HZ);
}
-static int zfcp_fc_exec_ct_job(struct fc_bsg_job *job,
+static int zfcp_fc_exec_ct_job(struct bsg_job *job,
struct zfcp_adapter *adapter)
{
int ret;
@@ -978,13 +984,15 @@ static int zfcp_fc_exec_ct_job(struct fc_bsg_job *job,
return ret;
}
-int zfcp_fc_exec_bsg_job(struct fc_bsg_job *job)
+int zfcp_fc_exec_bsg_job(struct bsg_job *job)
{
struct Scsi_Host *shost;
struct zfcp_adapter *adapter;
struct zfcp_fsf_ct_els *ct_els = job->dd_data;
+ struct fc_bsg_request *bsg_request = job->request;
+ struct fc_rport *rport = fc_bsg_to_rport(job);
- shost = job->rport ? rport_to_shost(job->rport) : job->shost;
+ shost = rport ? rport_to_shost(rport) : fc_bsg_to_shost(job);
adapter = (struct zfcp_adapter *)shost->hostdata[0];
if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN))
@@ -994,7 +1002,7 @@ int zfcp_fc_exec_bsg_job(struct fc_bsg_job *job)
ct_els->resp = job->reply_payload.sg_list;
ct_els->handler_data = job;
- switch (job->request->msgcode) {
+ switch (bsg_request->msgcode) {
case FC_BSG_RPT_ELS:
case FC_BSG_HST_ELS_NOLOGIN:
return zfcp_fc_exec_els_job(job, adapter);
@@ -1006,7 +1014,7 @@ int zfcp_fc_exec_bsg_job(struct fc_bsg_job *job)
}
}
-int zfcp_fc_timeout_bsg_job(struct fc_bsg_job *job)
+int zfcp_fc_timeout_bsg_job(struct bsg_job *job)
{
/* hardware tracks timeout, reset bsg timeout to not interfere */
return -EAGAIN;
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 522a633c866a..75f820ca17b7 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -3,7 +3,7 @@
*
* Implementation of FSF commands.
*
- * Copyright IBM Corp. 2002, 2013
+ * Copyright IBM Corp. 2002, 2015
*/
#define KMSG_COMPONENT "zfcp"
@@ -508,7 +508,10 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
fc_host_port_type(shost) = FC_PORTTYPE_PTP;
break;
case FSF_TOPO_FABRIC:
- fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+ if (bottom->connection_features & FSF_FEATURE_NPIV_MODE)
+ fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
+ else
+ fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
break;
case FSF_TOPO_AL:
fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
@@ -613,7 +616,6 @@ static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req)
if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) {
fc_host_permanent_port_name(shost) = bottom->wwpn;
- fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
} else
fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
fc_host_maxframe_size(shost) = bottom->maximum_frame_size;
@@ -982,8 +984,12 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
if (zfcp_adapter_multi_buffer_active(adapter)) {
if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_req))
return -EIO;
+ qtcb->bottom.support.req_buf_length =
+ zfcp_qdio_real_bytes(sg_req);
if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_resp))
return -EIO;
+ qtcb->bottom.support.resp_buf_length =
+ zfcp_qdio_real_bytes(sg_resp);
zfcp_qdio_set_data_div(qdio, &req->qdio_req,
zfcp_qdio_sbale_count(sg_req));
@@ -1073,6 +1079,7 @@ int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port,
req->handler = zfcp_fsf_send_ct_handler;
req->qtcb->header.port_handle = wka_port->handle;
+ ct->d_id = wka_port->d_id;
req->data = ct;
zfcp_dbf_san_req("fssct_1", req, wka_port->d_id);
@@ -1169,6 +1176,7 @@ int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id,
hton24(req->qtcb->bottom.support.d_id, d_id);
req->handler = zfcp_fsf_send_els_handler;
+ els->d_id = d_id;
req->data = els;
zfcp_dbf_san_req("fssels1", req, d_id);
@@ -1575,7 +1583,7 @@ out:
int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port)
{
struct zfcp_qdio *qdio = wka_port->adapter->qdio;
- struct zfcp_fsf_req *req;
+ struct zfcp_fsf_req *req = NULL;
int retval = -EIO;
spin_lock_irq(&qdio->req_q_lock);
@@ -1604,6 +1612,8 @@ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port)
zfcp_fsf_req_free(req);
out:
spin_unlock_irq(&qdio->req_q_lock);
+ if (req && !IS_ERR(req))
+ zfcp_dbf_rec_run_wka("fsowp_1", wka_port, req->req_id);
return retval;
}
@@ -1628,7 +1638,7 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req)
int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port)
{
struct zfcp_qdio *qdio = wka_port->adapter->qdio;
- struct zfcp_fsf_req *req;
+ struct zfcp_fsf_req *req = NULL;
int retval = -EIO;
spin_lock_irq(&qdio->req_q_lock);
@@ -1657,6 +1667,8 @@ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port)
zfcp_fsf_req_free(req);
out:
spin_unlock_irq(&qdio->req_q_lock);
+ if (req && !IS_ERR(req))
+ zfcp_dbf_rec_run_wka("fscwp_1", wka_port, req->req_id);
return retval;
}
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index 57ae3ae1046d..ea3c76ac0de1 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -3,7 +3,7 @@
*
* Interface to the FSF support functions.
*
- * Copyright IBM Corp. 2002, 2010
+ * Copyright IBM Corp. 2002, 2016
*/
#ifndef FSF_H
@@ -78,6 +78,7 @@
#define FSF_APP_TAG_CHECK_FAILURE 0x00000082
#define FSF_REF_TAG_CHECK_FAILURE 0x00000083
#define FSF_ADAPTER_STATUS_AVAILABLE 0x000000AD
+#define FSF_FCP_RSP_AVAILABLE 0x000000AF
#define FSF_UNKNOWN_COMMAND 0x000000E2
#define FSF_UNKNOWN_OP_SUBTYPE 0x000000E3
#define FSF_INVALID_COMMAND_OPTION 0x000000E5
@@ -436,6 +437,7 @@ struct zfcp_blk_drv_data {
* @handler_data: data passed to handler function
* @port: Optional pointer to port for zfcp internal ELS (only test link ADISC)
* @status: used to pass error status to calling function
+ * @d_id: Destination ID of either open WKA port for CT or of D_ID for ELS
*/
struct zfcp_fsf_ct_els {
struct scatterlist *req;
@@ -444,6 +446,7 @@ struct zfcp_fsf_ct_els {
void *handler_data;
struct zfcp_port *port;
int status;
+ u32 d_id;
};
#endif /* FSF_H */
diff --git a/drivers/s390/scsi/zfcp_reqlist.h b/drivers/s390/scsi/zfcp_reqlist.h
index 7c2c6194dfca..703fce59befe 100644
--- a/drivers/s390/scsi/zfcp_reqlist.h
+++ b/drivers/s390/scsi/zfcp_reqlist.h
@@ -4,7 +4,7 @@
* Data structure and helper functions for tracking pending FSF
* requests.
*
- * Copyright IBM Corp. 2009
+ * Copyright IBM Corp. 2009, 2016
*/
#ifndef ZFCP_REQLIST_H
@@ -180,4 +180,32 @@ static inline void zfcp_reqlist_move(struct zfcp_reqlist *rl,
spin_unlock_irqrestore(&rl->lock, flags);
}
+/**
+ * zfcp_reqlist_apply_for_all() - apply a function to every request.
+ * @rl: the requestlist that contains the target requests.
+ * @f: the function to apply to each request; the first parameter of the
+ * function will be the target-request; the second parameter is the same
+ * pointer as given with the argument @data.
+ * @data: freely chosen argument; passed through to @f as second parameter.
+ *
+ * Uses :c:macro:`list_for_each_entry` to iterate over the lists in the hash-
+ * table (not a 'safe' variant, so don't modify the list).
+ *
+ * Holds @rl->lock over the entire request-iteration.
+ */
+static inline void
+zfcp_reqlist_apply_for_all(struct zfcp_reqlist *rl,
+ void (*f)(struct zfcp_fsf_req *, void *), void *data)
+{
+ struct zfcp_fsf_req *req;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&rl->lock, flags);
+ for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++)
+ list_for_each_entry(req, &rl->buckets[i], list)
+ f(req, data);
+ spin_unlock_irqrestore(&rl->lock, flags);
+}
+
#endif /* ZFCP_REQLIST_H */
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index b3c6ff49103b..07ffdbb5107f 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -3,7 +3,7 @@
*
* Interface to Linux SCSI midlayer.
*
- * Copyright IBM Corp. 2002, 2013
+ * Copyright IBM Corp. 2002, 2016
*/
#define KMSG_COMPONENT "zfcp"
@@ -88,9 +88,7 @@ int zfcp_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scpnt)
}
if (unlikely(!(status & ZFCP_STATUS_COMMON_UNBLOCKED))) {
- /* This could be either
- * open LUN pending: this is temporary, will result in
- * open LUN or ERP_FAILED, so retry command
+ /* This could be
* call to rport_delete pending: mimic retry from
* fc_remote_port_chkready until rport is BLOCKED
*/
@@ -209,6 +207,57 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
return retval;
}
+struct zfcp_scsi_req_filter {
+ u8 tmf_scope;
+ u32 lun_handle;
+ u32 port_handle;
+};
+
+static void zfcp_scsi_forget_cmnd(struct zfcp_fsf_req *old_req, void *data)
+{
+ struct zfcp_scsi_req_filter *filter =
+ (struct zfcp_scsi_req_filter *)data;
+
+ /* already aborted - prevent side-effects - or not a SCSI command */
+ if (old_req->data == NULL || old_req->fsf_command != FSF_QTCB_FCP_CMND)
+ return;
+
+ /* (tmf_scope == FCP_TMF_TGT_RESET || tmf_scope == FCP_TMF_LUN_RESET) */
+ if (old_req->qtcb->header.port_handle != filter->port_handle)
+ return;
+
+ if (filter->tmf_scope == FCP_TMF_LUN_RESET &&
+ old_req->qtcb->header.lun_handle != filter->lun_handle)
+ return;
+
+ zfcp_dbf_scsi_nullcmnd((struct scsi_cmnd *)old_req->data, old_req);
+ old_req->data = NULL;
+}
+
+static void zfcp_scsi_forget_cmnds(struct zfcp_scsi_dev *zsdev, u8 tm_flags)
+{
+ struct zfcp_adapter *adapter = zsdev->port->adapter;
+ struct zfcp_scsi_req_filter filter = {
+ .tmf_scope = FCP_TMF_TGT_RESET,
+ .port_handle = zsdev->port->handle,
+ };
+ unsigned long flags;
+
+ if (tm_flags == FCP_TMF_LUN_RESET) {
+ filter.tmf_scope = FCP_TMF_LUN_RESET;
+ filter.lun_handle = zsdev->lun_handle;
+ }
+
+ /*
+ * abort_lock secures against other processings - in the abort-function
+ * and normal cmnd-handler - of (struct zfcp_fsf_req *)->data
+ */
+ write_lock_irqsave(&adapter->abort_lock, flags);
+ zfcp_reqlist_apply_for_all(adapter->req_list, zfcp_scsi_forget_cmnd,
+ &filter);
+ write_unlock_irqrestore(&adapter->abort_lock, flags);
+}
+
static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
{
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device);
@@ -241,8 +290,10 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags);
retval = FAILED;
- } else
+ } else {
zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags);
+ zfcp_scsi_forget_cmnds(zfcp_sdev, tm_flags);
+ }
zfcp_fsf_req_free(fsf_req);
return retval;
@@ -556,6 +607,9 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port)
ids.port_id = port->d_id;
ids.roles = FC_RPORT_ROLE_FCP_TARGET;
+ zfcp_dbf_rec_trig("scpaddy", port->adapter, port, NULL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD);
rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
if (!rport) {
dev_err(&port->adapter->ccw_device->dev,
@@ -577,6 +631,9 @@ static void zfcp_scsi_rport_block(struct zfcp_port *port)
struct fc_rport *rport = port->rport;
if (rport) {
+ zfcp_dbf_rec_trig("scpdely", port->adapter, port, NULL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL);
fc_remote_port_delete(rport);
port->rport = NULL;
}
diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c
index 8688ad4c825f..070c4da95f48 100644
--- a/drivers/s390/virtio/virtio_ccw.c
+++ b/drivers/s390/virtio/virtio_ccw.c
@@ -24,7 +24,7 @@
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/bitops.h>
-#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/io.h>
#include <linux/kvm_para.h>
#include <linux/notifier.h>
@@ -145,6 +145,7 @@ static struct airq_info *airq_areas[MAX_AIRQ_AREAS];
#define CCW_CMD_WRITE_CONF 0x21
#define CCW_CMD_WRITE_STATUS 0x31
#define CCW_CMD_READ_VQ_CONF 0x32
+#define CCW_CMD_READ_STATUS 0x72
#define CCW_CMD_SET_IND_ADAPTER 0x73
#define CCW_CMD_SET_VIRTIO_REV 0x83
@@ -160,6 +161,7 @@ static struct airq_info *airq_areas[MAX_AIRQ_AREAS];
#define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000
#define VIRTIO_CCW_DOING_SET_IND_ADAPTER 0x08000000
#define VIRTIO_CCW_DOING_SET_VIRTIO_REV 0x10000000
+#define VIRTIO_CCW_DOING_READ_STATUS 0x20000000
#define VIRTIO_CCW_INTPARM_MASK 0xffff0000
static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev)
@@ -235,16 +237,6 @@ static struct airq_info *new_airq_info(void)
return info;
}
-static void destroy_airq_info(struct airq_info *info)
-{
- if (!info)
- return;
-
- unregister_adapter_interrupt(&info->airq);
- airq_iv_release(info->aiv);
- kfree(info);
-}
-
static unsigned long get_airq_indicator(struct virtqueue *vqs[], int nvqs,
u64 *first, void **airq_info)
{
@@ -462,7 +454,7 @@ static void virtio_ccw_del_vq(struct virtqueue *vq, struct ccw1 *ccw)
* This may happen on device detach.
*/
if (ret && (ret != -ENODEV))
- dev_warn(&vq->vdev->dev, "Error %d while deleting queue %d",
+ dev_warn(&vq->vdev->dev, "Error %d while deleting queue %d\n",
ret, index);
vring_del_virtqueue(vq);
@@ -902,6 +894,28 @@ out_free:
static u8 virtio_ccw_get_status(struct virtio_device *vdev)
{
struct virtio_ccw_device *vcdev = to_vc_device(vdev);
+ u8 old_status = *vcdev->status;
+ struct ccw1 *ccw;
+
+ if (vcdev->revision < 1)
+ return *vcdev->status;
+
+ ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
+ if (!ccw)
+ return old_status;
+
+ ccw->cmd_code = CCW_CMD_READ_STATUS;
+ ccw->flags = 0;
+ ccw->count = sizeof(*vcdev->status);
+ ccw->cda = (__u32)(unsigned long)vcdev->status;
+ ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_STATUS);
+/*
+ * If the channel program failed (should only happen if the device
+ * was hotunplugged, and then we clean up via the machine check
+ * handler anyway), vcdev->status was not overwritten and we just
+ * return the old status, which is fine.
+*/
+ kfree(ccw);
return *vcdev->status;
}
@@ -930,7 +944,7 @@ static void virtio_ccw_set_status(struct virtio_device *vdev, u8 status)
kfree(ccw);
}
-static struct virtio_config_ops virtio_ccw_config_ops = {
+static const struct virtio_config_ops virtio_ccw_config_ops = {
.get_features = virtio_ccw_get_features,
.finalize_features = virtio_ccw_finalize_features,
.get = virtio_ccw_get_config,
@@ -997,6 +1011,7 @@ static void virtio_ccw_check_activity(struct virtio_ccw_device *vcdev,
case VIRTIO_CCW_DOING_READ_CONFIG:
case VIRTIO_CCW_DOING_WRITE_CONFIG:
case VIRTIO_CCW_DOING_WRITE_STATUS:
+ case VIRTIO_CCW_DOING_READ_STATUS:
case VIRTIO_CCW_DOING_SET_VQ:
case VIRTIO_CCW_DOING_SET_IND:
case VIRTIO_CCW_DOING_SET_CONF_IND:
@@ -1294,7 +1309,6 @@ static struct ccw_device_id virtio_ids[] = {
{ CCW_DEVICE(0x3832, 0) },
{},
};
-MODULE_DEVICE_TABLE(ccw, virtio_ids);
static struct ccw_driver virtio_ccw_driver = {
.driver = {
@@ -1406,14 +1420,4 @@ static int __init virtio_ccw_init(void)
no_auto_parse();
return ccw_driver_register(&virtio_ccw_driver);
}
-module_init(virtio_ccw_init);
-
-static void __exit virtio_ccw_exit(void)
-{
- int i;
-
- ccw_driver_unregister(&virtio_ccw_driver);
- for (i = 0; i < MAX_AIRQ_AREAS; i++)
- destroy_airq_info(airq_areas[i]);
-}
-module_exit(virtio_ccw_exit);
+device_initcall(virtio_ccw_init);