summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/s390
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/s390')
-rw-r--r--kernel/drivers/s390/Makefile2
-rw-r--r--kernel/drivers/s390/block/dasd.c55
-rw-r--r--kernel/drivers/s390/block/dasd_alias.c37
-rw-r--r--kernel/drivers/s390/block/dasd_diag.c9
-rw-r--r--kernel/drivers/s390/block/dasd_eckd.c380
-rw-r--r--kernel/drivers/s390/block/dasd_eckd.h11
-rw-r--r--kernel/drivers/s390/block/dasd_genhd.c19
-rw-r--r--kernel/drivers/s390/block/dasd_int.h1
-rw-r--r--kernel/drivers/s390/block/dcssblk.c36
-rw-r--r--kernel/drivers/s390/block/xpram.c10
-rw-r--r--kernel/drivers/s390/char/con3270.c4
-rw-r--r--kernel/drivers/s390/char/ctrlchar.c16
-rw-r--r--kernel/drivers/s390/char/ctrlchar.h12
-rw-r--r--kernel/drivers/s390/char/diag_ftp.c6
-rw-r--r--kernel/drivers/s390/char/keyboard.c13
-rw-r--r--kernel/drivers/s390/char/monreader.c6
-rw-r--r--kernel/drivers/s390/char/sclp.c8
-rw-r--r--kernel/drivers/s390/char/sclp.h15
-rw-r--r--kernel/drivers/s390/char/sclp_cmd.c68
-rw-r--r--kernel/drivers/s390/char/sclp_early.c120
-rw-r--r--kernel/drivers/s390/char/sclp_rw.c136
-rw-r--r--kernel/drivers/s390/char/sclp_rw.h17
-rw-r--r--kernel/drivers/s390/char/sclp_sdias.c3
-rw-r--r--kernel/drivers/s390/char/sclp_tty.c8
-rw-r--r--kernel/drivers/s390/char/sclp_vt220.c52
-rw-r--r--kernel/drivers/s390/char/tty3270.c4
-rw-r--r--kernel/drivers/s390/char/vmlogrdr.c8
-rw-r--r--kernel/drivers/s390/char/zcore.c12
-rw-r--r--kernel/drivers/s390/cio/chsc.c202
-rw-r--r--kernel/drivers/s390/cio/chsc.h15
-rw-r--r--kernel/drivers/s390/cio/cio.c47
-rw-r--r--kernel/drivers/s390/cio/cmf.c220
-rw-r--r--kernel/drivers/s390/cio/css.c7
-rw-r--r--kernel/drivers/s390/cio/device.c4
-rw-r--r--kernel/drivers/s390/cio/device.h6
-rw-r--r--kernel/drivers/s390/cio/device_fsm.c38
-rw-r--r--kernel/drivers/s390/cio/device_ops.c107
-rw-r--r--kernel/drivers/s390/cio/device_pgid.c70
-rw-r--r--kernel/drivers/s390/cio/eadm_sch.c3
-rw-r--r--kernel/drivers/s390/cio/qdio_main.c4
-rw-r--r--kernel/drivers/s390/crypto/Makefile7
-rw-r--r--kernel/drivers/s390/crypto/ap_bus.c1932
-rw-r--r--kernel/drivers/s390/crypto/ap_bus.h64
-rw-r--r--kernel/drivers/s390/crypto/zcrypt_api.c30
-rw-r--r--kernel/drivers/s390/crypto/zcrypt_api.h1
-rw-r--r--kernel/drivers/s390/crypto/zcrypt_cca_key.h2
-rw-r--r--kernel/drivers/s390/crypto/zcrypt_cex4.c2
-rw-r--r--kernel/drivers/s390/crypto/zcrypt_msgtype50.c29
-rw-r--r--kernel/drivers/s390/crypto/zcrypt_msgtype6.c62
-rw-r--r--kernel/drivers/s390/crypto/zcrypt_pcica.c420
-rw-r--r--kernel/drivers/s390/crypto/zcrypt_pcica.h115
-rw-r--r--kernel/drivers/s390/crypto/zcrypt_pcicc.c627
-rw-r--r--kernel/drivers/s390/crypto/zcrypt_pcicc.h174
-rw-r--r--kernel/drivers/s390/crypto/zcrypt_pcixcc.c2
-rw-r--r--kernel/drivers/s390/net/lcs.c2
-rw-r--r--kernel/drivers/s390/net/netiucv.c21
-rw-r--r--kernel/drivers/s390/net/qeth_core.h32
-rw-r--r--kernel/drivers/s390/net/qeth_core_main.c244
-rw-r--r--kernel/drivers/s390/net/qeth_core_mpc.c3
-rw-r--r--kernel/drivers/s390/net/qeth_core_mpc.h3
-rw-r--r--kernel/drivers/s390/net/qeth_core_sys.c43
-rw-r--r--kernel/drivers/s390/net/qeth_l2_main.c355
-rw-r--r--kernel/drivers/s390/net/qeth_l2_sys.c76
-rw-r--r--kernel/drivers/s390/net/qeth_l3_main.c170
-rw-r--r--kernel/drivers/s390/net/smsgiucv.c5
-rw-r--r--kernel/drivers/s390/scsi/zfcp_aux.c2
-rw-r--r--kernel/drivers/s390/scsi/zfcp_erp.c62
-rw-r--r--kernel/drivers/s390/scsi/zfcp_fc.c8
-rw-r--r--kernel/drivers/s390/scsi/zfcp_fsf.c28
-rw-r--r--kernel/drivers/s390/scsi/zfcp_qdio.c14
-rw-r--r--kernel/drivers/s390/scsi/zfcp_scsi.c1
-rw-r--r--kernel/drivers/s390/virtio/Makefile (renamed from kernel/drivers/s390/kvm/Makefile)0
-rw-r--r--kernel/drivers/s390/virtio/kvm_virtio.c (renamed from kernel/drivers/s390/kvm/kvm_virtio.c)4
-rw-r--r--kernel/drivers/s390/virtio/virtio_ccw.c (renamed from kernel/drivers/s390/kvm/virtio_ccw.c)87
74 files changed, 2744 insertions, 3674 deletions
diff --git a/kernel/drivers/s390/Makefile b/kernel/drivers/s390/Makefile
index 95bccfd3f..e5225ad9c 100644
--- a/kernel/drivers/s390/Makefile
+++ b/kernel/drivers/s390/Makefile
@@ -2,7 +2,7 @@
# Makefile for the S/390 specific device drivers
#
-obj-y += cio/ block/ char/ crypto/ net/ scsi/ kvm/
+obj-y += cio/ block/ char/ crypto/ net/ scsi/ virtio/
drivers-y += drivers/s390/built-in.o
diff --git a/kernel/drivers/s390/block/dasd.c b/kernel/drivers/s390/block/dasd.c
index 57fd66357..4abfbdb28 100644
--- a/kernel/drivers/s390/block/dasd.c
+++ b/kernel/drivers/s390/block/dasd.c
@@ -38,6 +38,8 @@
*/
#define DASD_CHANQ_MAX_SIZE 4
+#define DASD_DIAG_MOD "dasd_diag_mod"
+
/*
* SECTION: exported variables of dasd.c
*/
@@ -1861,6 +1863,33 @@ static void __dasd_device_check_expire(struct dasd_device *device)
}
/*
+ * return 1 when device is not eligible for IO
+ */
+static int __dasd_device_is_unusable(struct dasd_device *device,
+ struct dasd_ccw_req *cqr)
+{
+ int mask = ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM);
+
+ if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
+ /* dasd is being set offline. */
+ return 1;
+ }
+ if (device->stopped) {
+ if (device->stopped & mask) {
+ /* stopped and CQR will not change that. */
+ return 1;
+ }
+ if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) {
+ /* CQR is not able to change device to
+ * operational. */
+ return 1;
+ }
+ /* CQR required to get device operational. */
+ }
+ return 0;
+}
+
+/*
* Take a look at the first request on the ccw queue and check
* if it needs to be started.
*/
@@ -1874,13 +1903,8 @@ static void __dasd_device_start_head(struct dasd_device *device)
cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
if (cqr->status != DASD_CQR_QUEUED)
return;
- /* when device is stopped, return request to previous layer
- * exception: only the disconnect or unresumed bits are set and the
- * cqr is a path verification request
- */
- if (device->stopped &&
- !(!(device->stopped & ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM))
- && test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))) {
+ /* if device is not usable return request to upper layer */
+ if (__dasd_device_is_unusable(device, cqr)) {
cqr->intrc = -EAGAIN;
cqr->status = DASD_CQR_CLEARED;
dasd_schedule_device_bh(device);
@@ -3006,6 +3030,8 @@ 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);
@@ -3300,6 +3326,21 @@ int dasd_generic_set_online(struct ccw_device *cdev,
discipline = base_discipline;
if (device->features & DASD_FEATURE_USEDIAG) {
if (!dasd_diag_discipline_pointer) {
+ /* Try to load the required module. */
+ rc = request_module(DASD_DIAG_MOD);
+ if (rc) {
+ pr_warn("%s Setting the DASD online failed "
+ "because the required module %s "
+ "could not be loaded (rc=%d)\n",
+ dev_name(&cdev->dev), DASD_DIAG_MOD,
+ rc);
+ dasd_delete_device(device);
+ return -ENODEV;
+ }
+ }
+ /* Module init could have failed, so check again here after
+ * request_module(). */
+ if (!dasd_diag_discipline_pointer) {
pr_warn("%s Setting the DASD online failed because of missing DIAG discipline\n",
dev_name(&cdev->dev));
dasd_delete_device(device);
diff --git a/kernel/drivers/s390/block/dasd_alias.c b/kernel/drivers/s390/block/dasd_alias.c
index a2597e683..286782c60 100644
--- a/kernel/drivers/s390/block/dasd_alias.c
+++ b/kernel/drivers/s390/block/dasd_alias.c
@@ -58,7 +58,7 @@ static struct alias_server *_find_server(struct dasd_uid *uid)
&& !strncmp(pos->uid.serial, uid->serial,
sizeof(uid->serial)))
return pos;
- };
+ }
return NULL;
}
@@ -69,7 +69,7 @@ static struct alias_lcu *_find_lcu(struct alias_server *server,
list_for_each_entry(pos, &server->lculist, lcu) {
if (pos->uid.ssid == uid->ssid)
return pos;
- };
+ }
return NULL;
}
@@ -97,7 +97,7 @@ static struct alias_pav_group *_find_group(struct alias_lcu *lcu,
if (pos->uid.base_unit_addr == search_unit_addr &&
!strncmp(pos->uid.vduit, uid->vduit, sizeof(uid->vduit)))
return pos;
- };
+ }
return NULL;
}
@@ -264,8 +264,10 @@ void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device)
spin_unlock_irqrestore(&lcu->lock, flags);
cancel_work_sync(&lcu->suc_data.worker);
spin_lock_irqsave(&lcu->lock, flags);
- if (device == lcu->suc_data.device)
+ if (device == lcu->suc_data.device) {
+ dasd_put_device(device);
lcu->suc_data.device = NULL;
+ }
}
was_pending = 0;
if (device == lcu->ruac_data.device) {
@@ -273,8 +275,10 @@ void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device)
was_pending = 1;
cancel_delayed_work_sync(&lcu->ruac_data.dwork);
spin_lock_irqsave(&lcu->lock, flags);
- if (device == lcu->ruac_data.device)
+ if (device == lcu->ruac_data.device) {
+ dasd_put_device(device);
lcu->ruac_data.device = NULL;
+ }
}
private->lcu = NULL;
spin_unlock_irqrestore(&lcu->lock, flags);
@@ -549,8 +553,10 @@ static void lcu_update_work(struct work_struct *work)
if ((rc && (rc != -EOPNOTSUPP)) || (lcu->flags & NEED_UAC_UPDATE)) {
DBF_DEV_EVENT(DBF_WARNING, device, "could not update"
" alias data in lcu (rc = %d), retry later", rc);
- schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ);
+ if (!schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ))
+ dasd_put_device(device);
} else {
+ dasd_put_device(device);
lcu->ruac_data.device = NULL;
lcu->flags &= ~UPDATE_PENDING;
}
@@ -593,8 +599,10 @@ static int _schedule_lcu_update(struct alias_lcu *lcu,
*/
if (!usedev)
return -EINVAL;
+ dasd_get_device(usedev);
lcu->ruac_data.device = usedev;
- schedule_delayed_work(&lcu->ruac_data.dwork, 0);
+ if (!schedule_delayed_work(&lcu->ruac_data.dwork, 0))
+ dasd_put_device(usedev);
return 0;
}
@@ -699,7 +707,8 @@ struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device)
struct dasd_device, alias_list);
spin_unlock_irqrestore(&lcu->lock, flags);
alias_priv = (struct dasd_eckd_private *) alias_device->private;
- if ((alias_priv->count < private->count) && !alias_device->stopped)
+ if ((alias_priv->count < private->count) && !alias_device->stopped &&
+ !test_bit(DASD_FLAG_OFFLINE, &alias_device->flags))
return alias_device;
else
return NULL;
@@ -722,7 +731,7 @@ static int reset_summary_unit_check(struct alias_lcu *lcu,
ASCEBC((char *) &cqr->magic, 4);
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_RSCK;
- ccw->flags = 0 ;
+ ccw->flags = CCW_FLAG_SLI;
ccw->count = 16;
ccw->cda = (__u32)(addr_t) cqr->data;
((char *)cqr->data)[0] = reason;
@@ -823,8 +832,11 @@ static void flush_all_alias_devices_on_lcu(struct alias_lcu *lcu)
* were waiting for the flush
*/
if (device == list_first_entry(&active,
- struct dasd_device, alias_list))
+ struct dasd_device, alias_list)) {
list_move(&device->alias_list, &lcu->active_devices);
+ private = (struct dasd_eckd_private *) device->private;
+ private->pavgroup = NULL;
+ }
}
spin_unlock_irqrestore(&lcu->lock, flags);
}
@@ -926,6 +938,7 @@ static void summary_unit_check_handling_work(struct work_struct *work)
/* 3. read new alias configuration */
_schedule_lcu_update(lcu, device);
lcu->suc_data.device = NULL;
+ dasd_put_device(device);
spin_unlock_irqrestore(&lcu->lock, flags);
}
@@ -985,6 +998,8 @@ void dasd_alias_handle_summary_unit_check(struct dasd_device *device,
}
lcu->suc_data.reason = reason;
lcu->suc_data.device = device;
+ dasd_get_device(device);
spin_unlock(&lcu->lock);
- schedule_work(&lcu->suc_data.worker);
+ if (!schedule_work(&lcu->suc_data.worker))
+ dasd_put_device(device);
};
diff --git a/kernel/drivers/s390/block/dasd_diag.c b/kernel/drivers/s390/block/dasd_diag.c
index c062f1620..277b5c8c8 100644
--- a/kernel/drivers/s390/block/dasd_diag.c
+++ b/kernel/drivers/s390/block/dasd_diag.c
@@ -21,6 +21,7 @@
#include <asm/dasd.h>
#include <asm/debug.h>
+#include <asm/diag.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -66,7 +67,7 @@ static const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 */
* and function code cmd.
* In case of an exception return 3. Otherwise return result of bitwise OR of
* resulting condition code and DIAG return code. */
-static inline int dia250(void *iob, int cmd)
+static inline int __dia250(void *iob, int cmd)
{
register unsigned long reg2 asm ("2") = (unsigned long) iob;
typedef union {
@@ -89,6 +90,12 @@ static inline int dia250(void *iob, int cmd)
return rc;
}
+static inline int dia250(void *iob, int cmd)
+{
+ diag_stat_inc(DIAG_STAT_X250);
+ return __dia250(iob, cmd);
+}
+
/* Initialize block I/O to DIAG device using the specified blocksize and
* block offset. On success, return zero and set end_block to contain the
* number of blocks on the device minus the specified offset. Return non-zero
diff --git a/kernel/drivers/s390/block/dasd_eckd.c b/kernel/drivers/s390/block/dasd_eckd.c
index 6215f6455..9083247f5 100644
--- a/kernel/drivers/s390/block/dasd_eckd.c
+++ b/kernel/drivers/s390/block/dasd_eckd.c
@@ -1032,11 +1032,26 @@ static unsigned char dasd_eckd_path_access(void *conf_data, int conf_len)
return 0;
}
+static void dasd_eckd_clear_conf_data(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ int i;
+
+ private = (struct dasd_eckd_private *) device->private;
+ 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;
+ }
+}
+
+
static int dasd_eckd_read_conf(struct dasd_device *device)
{
void *conf_data;
int conf_len, conf_data_saved;
- int rc, path_err;
+ int rc, path_err, pos;
__u8 lpm, opm;
struct dasd_eckd_private *private, path_private;
struct dasd_path *path_data;
@@ -1070,7 +1085,8 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
}
/* save first valid configuration data */
if (!conf_data_saved) {
- kfree(private->conf_data);
+ /* initially clear previously stored conf_data */
+ dasd_eckd_clear_conf_data(device);
private->conf_data = conf_data;
private->conf_len = conf_len;
if (dasd_eckd_identify_conf_parts(private)) {
@@ -1079,6 +1095,10 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
kfree(conf_data);
continue;
}
+ pos = pathmask_to_pos(lpm);
+ /* store per path conf_data */
+ private->path_conf_data[pos] =
+ (struct dasd_conf_data *) conf_data;
/*
* build device UID that other path data
* can be compared to it
@@ -1095,7 +1115,6 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
kfree(conf_data);
continue;
}
-
if (dasd_eckd_compare_path_uid(
device, &path_private)) {
uid = &path_private.uid;
@@ -1137,7 +1156,10 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
path_data->cablepm |= lpm;
continue;
}
-
+ pos = pathmask_to_pos(lpm);
+ /* store per path conf_data */
+ private->path_conf_data[pos] =
+ (struct dasd_conf_data *) conf_data;
path_private.conf_data = NULL;
path_private.conf_len = 0;
}
@@ -1149,7 +1171,12 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
path_data->ppm |= lpm;
break;
}
- path_data->opm |= lpm;
+ if (!path_data->opm) {
+ path_data->opm = lpm;
+ dasd_generic_path_operational(device);
+ } else {
+ path_data->opm |= lpm;
+ }
/*
* if the path is used
* it should not be in one of the negative lists
@@ -1157,9 +1184,6 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
path_data->cablepm &= ~lpm;
path_data->hpfpm &= ~lpm;
path_data->cuirpm &= ~lpm;
-
- if (conf_data != private->conf_data)
- kfree(conf_data);
}
return path_err;
@@ -1259,7 +1283,11 @@ static void do_path_verification_work(struct work_struct *work)
schedule_work(work);
return;
}
-
+ /* check if path verification already running and delay if so */
+ if (test_and_set_bit(DASD_FLAG_PATH_VERIFY, &device->flags)) {
+ schedule_work(work);
+ return;
+ }
opm = 0;
npm = 0;
ppm = 0;
@@ -1402,7 +1430,7 @@ static void do_path_verification_work(struct work_struct *work)
device->path_data.hpfpm |= hpfpm;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
}
-
+ clear_bit(DASD_FLAG_PATH_VERIFY, &device->flags);
dasd_put_device(device);
if (data->isglobal)
mutex_unlock(&dasd_path_verification_mutex);
@@ -1810,6 +1838,7 @@ out_err1:
static void dasd_eckd_uncheck_device(struct dasd_device *device)
{
struct dasd_eckd_private *private;
+ int i;
private = (struct dasd_eckd_private *) device->private;
dasd_alias_disconnect_device_from_lcu(device);
@@ -1818,6 +1847,15 @@ static void dasd_eckd_uncheck_device(struct dasd_device *device)
private->vdsneq = NULL;
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] ==
+ private->conf_data) {
+ private->conf_data = NULL;
+ private->conf_len = 0;
+ }
+ private->path_conf_data[i] = NULL;
+ }
kfree(private->conf_data);
private->conf_data = NULL;
}
@@ -3968,7 +4006,7 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp)
rc = -EFAULT;
if (copy_from_user(&usrparm, argp, sizeof(usrparm)))
goto out;
- if (is_compat_task() || sizeof(long) == 4) {
+ if (is_compat_task()) {
/* Make sure pointers are sane even on 31 bit. */
rc = -EINVAL;
if ((usrparm.psf_data >> 32) != 0)
@@ -4402,7 +4440,12 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
private = (struct dasd_eckd_private *) device->private;
/* Read Configuration Data */
- dasd_eckd_read_conf(device);
+ rc = dasd_eckd_read_conf(device);
+ if (rc) {
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
+ "Read configuration data failed, rc=%d", rc);
+ goto out_err;
+ }
dasd_eckd_get_uid(device, &temp_uid);
/* Generate device unique id */
@@ -4418,13 +4461,18 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
/* register lcu with alias handling, enable PAV if this is a new lcu */
rc = dasd_alias_make_device_known_to_lcu(device);
if (rc)
- return rc;
+ goto out_err;
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr_flags);
dasd_eckd_validate_server(device, cqr_flags);
/* RE-Read Configuration Data */
- dasd_eckd_read_conf(device);
+ rc = dasd_eckd_read_conf(device);
+ if (rc) {
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
+ "Read configuration data failed, rc=%d", rc);
+ goto out_err2;
+ }
/* Read Feature Codes */
dasd_eckd_read_features(device);
@@ -4435,7 +4483,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
if (rc) {
DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
"Read device characteristic failed, rc=%d", rc);
- goto out_err;
+ goto out_err2;
}
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
memcpy(&private->rdc_data, &temp_rdc_data, sizeof(temp_rdc_data));
@@ -4446,6 +4494,8 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
return 0;
+out_err2:
+ dasd_alias_disconnect_device_from_lcu(device);
out_err:
return -1;
}
@@ -4525,12 +4575,13 @@ static int dasd_eckd_read_message_buffer(struct dasd_device *device,
cqr->startdev = device;
cqr->memdev = device;
cqr->block = NULL;
- cqr->retries = 256;
cqr->expires = 10 * HZ;
-
- /* we need to check for messages on exactly this path */
set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
- cqr->lpm = lpum;
+ /* dasd_sleep_on_immediatly does not do complex error
+ * recovery so clear erp flag and set retry counter to
+ * do basic erp */
+ clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+ cqr->retries = 256;
/* Prepare for Read Subsystem Data */
prssdp = (struct dasd_psf_prssd_data *) cqr->data;
@@ -4605,10 +4656,10 @@ dasd_eckd_psf_cuir_response(struct dasd_device *device, int response,
psf_cuir->message_id = message_id;
psf_cuir->cssid = sch_id.cssid;
psf_cuir->ssid = sch_id.ssid;
-
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_PSF;
ccw->cda = (__u32)(addr_t)psf_cuir;
+ ccw->flags = CCW_FLAG_SLI;
ccw->count = sizeof(struct dasd_psf_cuir_response);
cqr->startdev = device;
@@ -4618,6 +4669,7 @@ dasd_eckd_psf_cuir_response(struct dasd_device *device, int response,
cqr->expires = 10*HZ;
cqr->buildclk = get_tod_clock();
cqr->status = DASD_CQR_FILLED;
+ set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
rc = dasd_sleep_on(cqr);
@@ -4625,118 +4677,252 @@ dasd_eckd_psf_cuir_response(struct dasd_device *device, int response,
return rc;
}
-static int dasd_eckd_cuir_change_state(struct dasd_device *device, __u8 lpum)
+/*
+ * return configuration data that is referenced by record selector
+ * if a record selector is specified or per default return the
+ * conf_data pointer for the path specified by lpum
+ */
+static struct dasd_conf_data *dasd_eckd_get_ref_conf(struct dasd_device *device,
+ __u8 lpum,
+ struct dasd_cuir_message *cuir)
{
- unsigned long flags;
- __u8 tbcpm;
+ struct dasd_eckd_private *private;
+ struct dasd_conf_data *conf_data;
+ int path, pos;
- spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- tbcpm = device->path_data.opm & ~lpum;
- if (tbcpm) {
- device->path_data.opm = tbcpm;
- device->path_data.cuirpm |= lpum;
+ private = (struct dasd_eckd_private *) device->private;
+ if (cuir->record_selector == 0)
+ goto out;
+ for (path = 0x80, pos = 0; path; path >>= 1, pos++) {
+ conf_data = private->path_conf_data[pos];
+ if (conf_data->gneq.record_selector ==
+ cuir->record_selector)
+ return conf_data;
}
- spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
- return tbcpm ? 0 : PSF_CUIR_LAST_PATH;
+out:
+ return private->path_conf_data[pathmask_to_pos(lpum)];
}
/*
- * walk through all devices and quiesce them
- * if it is the last path return error
+ * This function determines the scope of a reconfiguration request by
+ * analysing the path and device selection data provided in the CUIR request.
+ * Returns a path mask containing CUIR affected paths for the give device.
+ *
+ * If the CUIR request does not contain the required information return the
+ * path mask of the path the attention message for the CUIR request was reveived
+ * on.
+ */
+static int dasd_eckd_cuir_scope(struct dasd_device *device, __u8 lpum,
+ struct dasd_cuir_message *cuir)
+{
+ struct dasd_conf_data *ref_conf_data;
+ unsigned long bitmask = 0, mask = 0;
+ struct dasd_eckd_private *private;
+ struct dasd_conf_data *conf_data;
+ unsigned int pos, path;
+ char *ref_gneq, *gneq;
+ char *ref_ned, *ned;
+ int tbcpm = 0;
+
+ /* if CUIR request does not specify the scope use the path
+ the attention message was presented on */
+ if (!cuir->ned_map ||
+ !(cuir->neq_map[0] | cuir->neq_map[1] | cuir->neq_map[2]))
+ return lpum;
+
+ private = (struct dasd_eckd_private *) device->private;
+ /* get reference conf data */
+ ref_conf_data = dasd_eckd_get_ref_conf(device, lpum, cuir);
+ /* reference ned is determined by ned_map field */
+ pos = 8 - ffs(cuir->ned_map);
+ ref_ned = (char *)&ref_conf_data->neds[pos];
+ ref_gneq = (char *)&ref_conf_data->gneq;
+ /* transfer 24 bit neq_map to mask */
+ mask = cuir->neq_map[2];
+ mask |= cuir->neq_map[1] << 8;
+ mask |= cuir->neq_map[0] << 16;
+
+ for (path = 0x80; path; path >>= 1) {
+ /* initialise data per path */
+ bitmask = mask;
+ pos = pathmask_to_pos(path);
+ conf_data = private->path_conf_data[pos];
+ pos = 8 - ffs(cuir->ned_map);
+ ned = (char *) &conf_data->neds[pos];
+ /* compare reference ned and per path ned */
+ if (memcmp(ref_ned, ned, sizeof(*ned)) != 0)
+ continue;
+ gneq = (char *)&conf_data->gneq;
+ /* compare reference gneq and per_path gneq under
+ 24 bit mask where mask bit 0 equals byte 7 of
+ the gneq and mask bit 24 equals byte 31 */
+ while (bitmask) {
+ pos = ffs(bitmask) - 1;
+ if (memcmp(&ref_gneq[31 - pos], &gneq[31 - pos], 1)
+ != 0)
+ break;
+ clear_bit(pos, &bitmask);
+ }
+ if (bitmask)
+ continue;
+ /* device and path match the reference values
+ add path to CUIR scope */
+ tbcpm |= path;
+ }
+ return tbcpm;
+}
+
+static void dasd_eckd_cuir_notify_user(struct dasd_device *device,
+ unsigned long paths,
+ struct subchannel_id sch_id, int action)
+{
+ struct channel_path_desc *desc;
+ int pos;
+
+ while (paths) {
+ /* get position of bit in mask */
+ pos = ffs(paths) - 1;
+ /* 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);
+ 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);
+ }
+}
+
+static int dasd_eckd_cuir_remove_path(struct dasd_device *device, __u8 lpum,
+ struct dasd_cuir_message *cuir)
+{
+ unsigned long tbcpm;
+
+ tbcpm = dasd_eckd_cuir_scope(device, lpum, cuir);
+ /* nothing to do if path is not in use */
+ if (!(device->path_data.opm & tbcpm))
+ return 0;
+ if (!(device->path_data.opm & ~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;
+ return tbcpm;
+}
+
+/*
+ * walk through all devices and build a path mask to quiesce them
+ * return an error if the last path to a device would be removed
*
* if only part of the devices are quiesced and an error
* occurs no onlining necessary, the storage server will
* notify the already set offline devices again
*/
static int dasd_eckd_cuir_quiesce(struct dasd_device *device, __u8 lpum,
- struct channel_path_desc *desc,
- struct subchannel_id sch_id)
+ struct subchannel_id sch_id,
+ struct dasd_cuir_message *cuir)
{
struct alias_pav_group *pavgroup, *tempgroup;
struct dasd_eckd_private *private;
struct dasd_device *dev, *n;
- int rc;
+ unsigned long paths = 0;
+ unsigned long flags;
+ int tbcpm;
private = (struct dasd_eckd_private *) device->private;
- rc = 0;
-
/* active devices */
- list_for_each_entry_safe(dev, n,
- &private->lcu->active_devices,
+ list_for_each_entry_safe(dev, n, &private->lcu->active_devices,
alias_list) {
- rc = dasd_eckd_cuir_change_state(dev, lpum);
- if (rc)
- goto out;
+ spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
+ tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
+ spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags);
+ if (tbcpm < 0)
+ goto out_err;
+ paths |= tbcpm;
}
-
/* inactive devices */
- list_for_each_entry_safe(dev, n,
- &private->lcu->inactive_devices,
+ list_for_each_entry_safe(dev, n, &private->lcu->inactive_devices,
alias_list) {
- rc = dasd_eckd_cuir_change_state(dev, lpum);
- if (rc)
- goto out;
+ spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
+ tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
+ spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags);
+ if (tbcpm < 0)
+ goto out_err;
+ paths |= tbcpm;
}
-
/* devices in PAV groups */
list_for_each_entry_safe(pavgroup, tempgroup,
&private->lcu->grouplist, group) {
list_for_each_entry_safe(dev, n, &pavgroup->baselist,
alias_list) {
- rc = dasd_eckd_cuir_change_state(dev, lpum);
- if (rc)
- goto out;
+ spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
+ tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
+ spin_unlock_irqrestore(
+ get_ccwdev_lock(dev->cdev), flags);
+ if (tbcpm < 0)
+ goto out_err;
+ paths |= tbcpm;
}
list_for_each_entry_safe(dev, n, &pavgroup->aliaslist,
alias_list) {
- rc = dasd_eckd_cuir_change_state(dev, lpum);
- if (rc)
- goto out;
+ spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags);
+ tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir);
+ spin_unlock_irqrestore(
+ get_ccwdev_lock(dev->cdev), flags);
+ if (tbcpm < 0)
+ goto out_err;
+ paths |= tbcpm;
}
}
-
- pr_warn("Service on the storage server caused path %x.%02x to go offline",
- sch_id.cssid, desc ? desc->chpid : 0);
- rc = PSF_CUIR_COMPLETED;
-out:
- return rc;
+ /* notify user about all paths affected by CUIR action */
+ dasd_eckd_cuir_notify_user(device, paths, sch_id, CUIR_QUIESCE);
+ return 0;
+out_err:
+ return tbcpm;
}
static int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum,
- struct channel_path_desc *desc,
- struct subchannel_id sch_id)
+ struct subchannel_id sch_id,
+ struct dasd_cuir_message *cuir)
{
struct alias_pav_group *pavgroup, *tempgroup;
struct dasd_eckd_private *private;
struct dasd_device *dev, *n;
+ unsigned long paths = 0;
+ int tbcpm;
- pr_info("Path %x.%02x is back online after service on the storage server",
- sch_id.cssid, desc ? desc->chpid : 0);
private = (struct dasd_eckd_private *) device->private;
-
/*
* the path may have been added through a generic path event before
* only trigger path verification if the path is not already in use
*/
-
list_for_each_entry_safe(dev, n,
&private->lcu->active_devices,
alias_list) {
- if (!(dev->path_data.opm & lpum)) {
- dev->path_data.tbvpm |= lpum;
+ tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
+ paths |= tbcpm;
+ if (!(dev->path_data.opm & tbcpm)) {
+ dev->path_data.tbvpm |= tbcpm;
dasd_schedule_device_bh(dev);
}
}
-
list_for_each_entry_safe(dev, n,
&private->lcu->inactive_devices,
alias_list) {
- if (!(dev->path_data.opm & lpum)) {
- dev->path_data.tbvpm |= lpum;
+ tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
+ paths |= tbcpm;
+ if (!(dev->path_data.opm & tbcpm)) {
+ dev->path_data.tbvpm |= tbcpm;
dasd_schedule_device_bh(dev);
}
}
-
/* devices in PAV groups */
list_for_each_entry_safe(pavgroup, tempgroup,
&private->lcu->grouplist,
@@ -4744,21 +4930,27 @@ static int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum,
list_for_each_entry_safe(dev, n,
&pavgroup->baselist,
alias_list) {
- if (!(dev->path_data.opm & lpum)) {
- dev->path_data.tbvpm |= lpum;
+ tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
+ paths |= tbcpm;
+ if (!(dev->path_data.opm & tbcpm)) {
+ dev->path_data.tbvpm |= tbcpm;
dasd_schedule_device_bh(dev);
}
}
list_for_each_entry_safe(dev, n,
&pavgroup->aliaslist,
alias_list) {
- if (!(dev->path_data.opm & lpum)) {
- dev->path_data.tbvpm |= lpum;
+ tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir);
+ paths |= tbcpm;
+ if (!(dev->path_data.opm & tbcpm)) {
+ dev->path_data.tbvpm |= tbcpm;
dasd_schedule_device_bh(dev);
}
}
}
- return PSF_CUIR_COMPLETED;
+ /* notify user about all paths affected by CUIR action */
+ dasd_eckd_cuir_notify_user(device, paths, sch_id, CUIR_RESUME);
+ return 0;
}
static void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages,
@@ -4768,27 +4960,37 @@ static void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages,
struct channel_path_desc *desc;
struct subchannel_id sch_id;
int pos, response;
- ccw_device_get_schid(device->cdev, &sch_id);
- /* get position of path in mask */
- pos = 8 - ffs(lpum);
- /* get channel path descriptor from this position */
+ 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 */
- response = dasd_eckd_cuir_quiesce(device, lpum, desc, sch_id);
+ if (dasd_eckd_cuir_quiesce(device, lpum, sch_id, cuir))
+ response = PSF_CUIR_LAST_PATH;
+ else
+ response = PSF_CUIR_COMPLETED;
} else if (cuir->code == CUIR_RESUME) {
/* resume */
- response = dasd_eckd_cuir_resume(device, lpum, desc, sch_id);
+ dasd_eckd_cuir_resume(device, lpum, sch_id, cuir);
+ response = PSF_CUIR_COMPLETED;
} else
response = PSF_CUIR_NOT_SUPPORTED;
- dasd_eckd_psf_cuir_response(device, response, cuir->message_id,
- desc, sch_id);
-
+ dasd_eckd_psf_cuir_response(device, response,
+ cuir->message_id, desc, sch_id);
+ 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);
}
static void dasd_eckd_check_attention_work(struct work_struct *work)
@@ -4800,22 +5002,18 @@ static void dasd_eckd_check_attention_work(struct work_struct *work)
data = container_of(work, struct check_attention_work_data, worker);
device = data->device;
-
messages = kzalloc(sizeof(*messages), GFP_KERNEL);
if (!messages) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"Could not allocate attention message buffer");
goto out;
}
-
rc = dasd_eckd_read_message_buffer(device, messages, data->lpum);
if (rc)
goto out;
-
if (messages->length == ATTENTION_LENGTH_CUIR &&
messages->format == ATTENTION_FORMAT_CUIR)
dasd_eckd_handle_cuir(device, messages, data->lpum);
-
out:
dasd_put_device(device);
kfree(messages);
diff --git a/kernel/drivers/s390/block/dasd_eckd.h b/kernel/drivers/s390/block/dasd_eckd.h
index ddab7df36..f8f91ee65 100644
--- a/kernel/drivers/s390/block/dasd_eckd.h
+++ b/kernel/drivers/s390/block/dasd_eckd.h
@@ -355,7 +355,8 @@ struct dasd_gneq {
__u8 identifier:2;
__u8 reserved:6;
} __attribute__ ((packed)) flags;
- __u8 reserved[5];
+ __u8 record_selector;
+ __u8 reserved[4];
struct {
__u8 value:2;
__u8 number:6;
@@ -492,10 +493,18 @@ struct alias_pav_group {
struct dasd_device *next;
};
+struct dasd_conf_data {
+ struct dasd_ned neds[5];
+ u8 reserved[64];
+ struct dasd_gneq gneq;
+} __packed;
+
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/kernel/drivers/s390/block/dasd_genhd.c b/kernel/drivers/s390/block/dasd_genhd.c
index 90f39f79f..ef1d9fb06 100644
--- a/kernel/drivers/s390/block/dasd_genhd.c
+++ b/kernel/drivers/s390/block/dasd_genhd.c
@@ -99,9 +99,8 @@ void dasd_gendisk_free(struct dasd_block *block)
int dasd_scan_partitions(struct dasd_block *block)
{
struct block_device *bdev;
- int retry, rc;
+ int rc;
- retry = 5;
bdev = bdget_disk(block->gdp, 0);
if (!bdev) {
DBF_DEV_EVENT(DBF_ERR, block->base, "%s",
@@ -116,19 +115,11 @@ int dasd_scan_partitions(struct dasd_block *block)
rc);
return -ENODEV;
}
- /*
- * See fs/partition/check.c:register_disk,rescan_partitions
- * Can't call rescan_partitions directly. Use ioctl.
- */
- rc = ioctl_by_bdev(bdev, BLKRRPART, 0);
- while (rc == -EBUSY && retry > 0) {
- schedule();
- rc = ioctl_by_bdev(bdev, BLKRRPART, 0);
- retry--;
+
+ rc = blkdev_reread_part(bdev);
+ if (rc)
DBF_DEV_EVENT(DBF_ERR, block->base,
- "scan partitions error, retry %d rc %d",
- retry, rc);
- }
+ "scan partitions error, rc %d", rc);
/*
* Since the matching blkdev_put call to the blkdev_get in
diff --git a/kernel/drivers/s390/block/dasd_int.h b/kernel/drivers/s390/block/dasd_int.h
index 227e3dea3..4aed5ed70 100644
--- a/kernel/drivers/s390/block/dasd_int.h
+++ b/kernel/drivers/s390/block/dasd_int.h
@@ -534,6 +534,7 @@ struct dasd_attention_data {
#define DASD_FLAG_SAFE_OFFLINE 10 /* safe offline processing requested*/
#define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */
#define DASD_FLAG_ABORTALL 12 /* Abort all noretry requests */
+#define DASD_FLAG_PATH_VERIFY 13 /* Path verification worker running */
#define DASD_SLEEPON_START_TAG ((void *) 1)
#define DASD_SLEEPON_END_TAG ((void *) 2)
diff --git a/kernel/drivers/s390/block/dcssblk.c b/kernel/drivers/s390/block/dcssblk.c
index da212813f..94a8f4ab5 100644
--- a/kernel/drivers/s390/block/dcssblk.c
+++ b/kernel/drivers/s390/block/dcssblk.c
@@ -27,9 +27,10 @@
static int dcssblk_open(struct block_device *bdev, fmode_t mode);
static void dcssblk_release(struct gendisk *disk, fmode_t mode);
-static void dcssblk_make_request(struct request_queue *q, struct bio *bio);
+static blk_qc_t dcssblk_make_request(struct request_queue *q,
+ struct bio *bio);
static long dcssblk_direct_access(struct block_device *bdev, sector_t secnum,
- void **kaddr, unsigned long *pfn, long size);
+ void __pmem **kaddr, unsigned long *pfn);
static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0";
@@ -548,10 +549,10 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
*/
num_of_segments = 0;
for (i = 0; (i < count && (buf[i] != '\0') && (buf[i] != '\n')); i++) {
- for (j = i; (buf[j] != ':') &&
+ for (j = i; j < count &&
+ (buf[j] != ':') &&
(buf[j] != '\0') &&
- (buf[j] != '\n') &&
- j < count; j++) {
+ (buf[j] != '\n'); j++) {
local_buf[j-i] = toupper(buf[j]);
}
local_buf[j-i] = '\0';
@@ -723,7 +724,7 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch
/*
* parse input
*/
- for (i = 0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i < count); i++) {
+ for (i = 0; (i < count && (*(buf+i)!='\0') && (*(buf+i)!='\n')); i++) {
local_buf[i] = toupper(buf[i]);
}
local_buf[i] = '\0';
@@ -815,7 +816,7 @@ dcssblk_release(struct gendisk *disk, fmode_t mode)
up_write(&dcssblk_devices_sem);
}
-static void
+static blk_qc_t
dcssblk_make_request(struct request_queue *q, struct bio *bio)
{
struct dcssblk_dev_info *dev_info;
@@ -826,6 +827,8 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
unsigned long source_addr;
unsigned long bytes_done;
+ blk_queue_split(q, &bio, q->bio_split);
+
bytes_done = 0;
dev_info = bio->bi_bdev->bd_disk->private_data;
if (dev_info == NULL)
@@ -871,26 +874,29 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
}
bytes_done += bvec.bv_len;
}
- bio_endio(bio, 0);
- return;
+ bio_endio(bio);
+ return BLK_QC_T_NONE;
fail:
bio_io_error(bio);
+ return BLK_QC_T_NONE;
}
static long
dcssblk_direct_access (struct block_device *bdev, sector_t secnum,
- void **kaddr, unsigned long *pfn, long size)
+ void __pmem **kaddr, unsigned long *pfn)
{
struct dcssblk_dev_info *dev_info;
unsigned long offset, dev_sz;
+ void *addr;
dev_info = bdev->bd_disk->private_data;
if (!dev_info)
return -ENODEV;
dev_sz = dev_info->end - dev_info->start;
offset = secnum * 512;
- *kaddr = (void *) (dev_info->start + offset);
- *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT;
+ addr = (void *) (dev_info->start + offset);
+ *pfn = virt_to_phys(addr) >> PAGE_SHIFT;
+ *kaddr = (void __pmem *) addr;
return dev_sz - offset;
}
@@ -904,10 +910,10 @@ dcssblk_check_params(void)
for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0');
i++) {
- for (j = i; (dcssblk_segments[j] != ',') &&
+ for (j = i; (j < DCSSBLK_PARM_LEN) &&
+ (dcssblk_segments[j] != ',') &&
(dcssblk_segments[j] != '\0') &&
- (dcssblk_segments[j] != '(') &&
- (j < DCSSBLK_PARM_LEN); j++)
+ (dcssblk_segments[j] != '('); j++)
{
buf[j-i] = dcssblk_segments[j];
}
diff --git a/kernel/drivers/s390/block/xpram.c b/kernel/drivers/s390/block/xpram.c
index 7d4e9397a..288f59a41 100644
--- a/kernel/drivers/s390/block/xpram.c
+++ b/kernel/drivers/s390/block/xpram.c
@@ -181,7 +181,7 @@ static unsigned long xpram_highest_page_index(void)
/*
* Block device make request function.
*/
-static void xpram_make_request(struct request_queue *q, struct bio *bio)
+static blk_qc_t xpram_make_request(struct request_queue *q, struct bio *bio)
{
xpram_device_t *xdev = bio->bi_bdev->bd_disk->private_data;
struct bio_vec bvec;
@@ -190,6 +190,8 @@ static void xpram_make_request(struct request_queue *q, struct bio *bio)
unsigned long page_addr;
unsigned long bytes;
+ blk_queue_split(q, &bio, q->bio_split);
+
if ((bio->bi_iter.bi_sector & 7) != 0 ||
(bio->bi_iter.bi_size & 4095) != 0)
/* Request is not page-aligned. */
@@ -220,11 +222,11 @@ static void xpram_make_request(struct request_queue *q, struct bio *bio)
index++;
}
}
- set_bit(BIO_UPTODATE, &bio->bi_flags);
- bio_endio(bio, 0);
- return;
+ bio_endio(bio);
+ return BLK_QC_T_NONE;
fail:
bio_io_error(bio);
+ return BLK_QC_T_NONE;
}
static int xpram_getgeo(struct block_device *bdev, struct hd_geometry *geo)
diff --git a/kernel/drivers/s390/char/con3270.c b/kernel/drivers/s390/char/con3270.c
index 75ffe9980..7c511add5 100644
--- a/kernel/drivers/s390/char/con3270.c
+++ b/kernel/drivers/s390/char/con3270.c
@@ -413,6 +413,10 @@ con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb)
else
/* Normal end. Copy residual count. */
rq->rescnt = irb->scsw.cmd.count;
+ } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
+ /* Interrupt without an outstanding request -> update all */
+ cp->update_flags = CON_UPDATE_ALL;
+ con3270_set_timer(cp, 1);
}
return RAW3270_IO_DONE;
}
diff --git a/kernel/drivers/s390/char/ctrlchar.c b/kernel/drivers/s390/char/ctrlchar.c
index 8de2deb17..f7d92584b 100644
--- a/kernel/drivers/s390/char/ctrlchar.c
+++ b/kernel/drivers/s390/char/ctrlchar.c
@@ -14,15 +14,21 @@
#include "ctrlchar.h"
#ifdef CONFIG_MAGIC_SYSRQ
-static int ctrlchar_sysrq_key;
+static struct sysrq_work ctrlchar_sysrq;
static void
ctrlchar_handle_sysrq(struct work_struct *work)
{
- handle_sysrq(ctrlchar_sysrq_key);
+ struct sysrq_work *sysrq = container_of(work, struct sysrq_work, work);
+
+ handle_sysrq(sysrq->key);
}
-static DECLARE_WORK(ctrlchar_work, ctrlchar_handle_sysrq);
+void schedule_sysrq_work(struct sysrq_work *sw)
+{
+ INIT_WORK(&sw->work, ctrlchar_handle_sysrq);
+ schedule_work(&sw->work);
+}
#endif
@@ -51,8 +57,8 @@ ctrlchar_handle(const unsigned char *buf, int len, struct tty_struct *tty)
#ifdef CONFIG_MAGIC_SYSRQ
/* racy */
if (len == 3 && buf[1] == '-') {
- ctrlchar_sysrq_key = buf[2];
- schedule_work(&ctrlchar_work);
+ ctrlchar_sysrq.key = buf[2];
+ schedule_sysrq_work(&ctrlchar_sysrq);
return CTRLCHAR_SYSRQ;
}
#endif
diff --git a/kernel/drivers/s390/char/ctrlchar.h b/kernel/drivers/s390/char/ctrlchar.h
index 1a53552f4..59c2d6e55 100644
--- a/kernel/drivers/s390/char/ctrlchar.h
+++ b/kernel/drivers/s390/char/ctrlchar.h
@@ -7,6 +7,8 @@
*/
#include <linux/tty.h>
+#include <linux/sysrq.h>
+#include <linux/workqueue.h>
extern unsigned int
ctrlchar_handle(const unsigned char *buf, int len, struct tty_struct *tty);
@@ -17,3 +19,13 @@ ctrlchar_handle(const unsigned char *buf, int len, struct tty_struct *tty);
#define CTRLCHAR_SYSRQ (3 << 8)
#define CTRLCHAR_MASK (~0xffu)
+
+
+#ifdef CONFIG_MAGIC_SYSRQ
+struct sysrq_work {
+ int key;
+ struct work_struct work;
+};
+
+void schedule_sysrq_work(struct sysrq_work *sw);
+#endif
diff --git a/kernel/drivers/s390/char/diag_ftp.c b/kernel/drivers/s390/char/diag_ftp.c
index 93889632f..a5ccbf6f0 100644
--- a/kernel/drivers/s390/char/diag_ftp.c
+++ b/kernel/drivers/s390/char/diag_ftp.c
@@ -15,6 +15,7 @@
#include <linux/wait.h>
#include <linux/string.h>
#include <asm/ctl_reg.h>
+#include <asm/diag.h>
#include "hmcdrv_ftp.h"
#include "diag_ftp.h"
@@ -102,6 +103,7 @@ static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl,
{
int rc;
+ diag_stat_inc(DIAG_STAT_X2C4);
asm volatile(
" diag %[addr],%[cmd],0x2c4\n"
"0: j 2f\n"
@@ -223,7 +225,7 @@ int diag_ftp_startup(void)
if (rc)
return rc;
- ctl_set_bit(0, 63 - 22);
+ irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL);
return 0;
}
@@ -232,6 +234,6 @@ int diag_ftp_startup(void)
*/
void diag_ftp_shutdown(void)
{
- ctl_clear_bit(0, 63 - 22);
+ irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL);
unregister_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler);
}
diff --git a/kernel/drivers/s390/char/keyboard.c b/kernel/drivers/s390/char/keyboard.c
index 01463b052..ef04a9f7a 100644
--- a/kernel/drivers/s390/char/keyboard.c
+++ b/kernel/drivers/s390/char/keyboard.c
@@ -433,20 +433,23 @@ do_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry __user *u_kbs,
case KDSKBSENT:
if (!perm)
return -EPERM;
- len = strnlen_user(u_kbs->kb_string,
- sizeof(u_kbs->kb_string) - 1);
+ len = strnlen_user(u_kbs->kb_string, sizeof(u_kbs->kb_string));
if (!len)
return -EFAULT;
- if (len > sizeof(u_kbs->kb_string) - 1)
+ if (len > sizeof(u_kbs->kb_string))
return -EINVAL;
- p = kmalloc(len + 1, GFP_KERNEL);
+ p = kmalloc(len, GFP_KERNEL);
if (!p)
return -ENOMEM;
if (copy_from_user(p, u_kbs->kb_string, len)) {
kfree(p);
return -EFAULT;
}
- p[len] = 0;
+ /*
+ * Make sure the string is terminated by 0. User could have
+ * modified it between us running strnlen_user() and copying it.
+ */
+ p[len - 1] = 0;
kfree(kbd->func_table[kb_func]);
kbd->func_table[kb_func] = p;
break;
diff --git a/kernel/drivers/s390/char/monreader.c b/kernel/drivers/s390/char/monreader.c
index 0da3ae3cd..fc94bfdce 100644
--- a/kernel/drivers/s390/char/monreader.c
+++ b/kernel/drivers/s390/char/monreader.c
@@ -95,7 +95,7 @@ static void dcss_mkname(char *ascii_name, char *ebcdic_name)
if (ascii_name[i] == '\0')
break;
ebcdic_name[i] = toupper(ascii_name[i]);
- };
+ }
for (; i < 8; i++)
ebcdic_name[i] = ' ';
ASCEBC(ebcdic_name, 8);
@@ -229,7 +229,7 @@ static struct mon_msg *mon_next_message(struct mon_private *monpriv)
/******************************************************************************
* IUCV handler *
*****************************************************************************/
-static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
+static void mon_iucv_path_complete(struct iucv_path *path, u8 *ipuser)
{
struct mon_private *monpriv = path->private;
@@ -237,7 +237,7 @@ static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
wake_up(&mon_conn_wait_queue);
}
-static void mon_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
+static void mon_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
{
struct mon_private *monpriv = path->private;
diff --git a/kernel/drivers/s390/char/sclp.c b/kernel/drivers/s390/char/sclp.c
index 41ba56d2e..f58bf4c6c 100644
--- a/kernel/drivers/s390/char/sclp.c
+++ b/kernel/drivers/s390/char/sclp.c
@@ -53,7 +53,7 @@ static DECLARE_COMPLETION(sclp_request_queue_flushed);
/* Number of console pages to allocate, used by sclp_con.c and sclp_vt220.c */
int sclp_console_pages = SCLP_CONSOLE_PAGES;
/* Flag to indicate if buffer pages are dropped on buffer full condition */
-int sclp_console_drop = 0;
+int sclp_console_drop = 1;
/* Number of times the console dropped buffer pages */
unsigned long sclp_console_full;
@@ -79,8 +79,8 @@ static int __init sclp_setup_console_drop(char *str)
int drop, rc;
rc = kstrtoint(str, 0, &drop);
- if (!rc && drop)
- sclp_console_drop = 1;
+ if (!rc)
+ sclp_console_drop = drop;
return 1;
}
@@ -665,7 +665,7 @@ sclp_state_change_cb(struct evbuf_header *evbuf)
sclp_send_mask = scbuf->sclp_send_mask;
spin_unlock_irqrestore(&sclp_lock, flags);
if (scbuf->validity_sclp_active_facility_mask)
- sclp_facilities = scbuf->sclp_active_facility_mask;
+ sclp.facilities = scbuf->sclp_active_facility_mask;
sclp_dispatch_state_change();
}
diff --git a/kernel/drivers/s390/char/sclp.h b/kernel/drivers/s390/char/sclp.h
index a88069f8c..026e38990 100644
--- a/kernel/drivers/s390/char/sclp.h
+++ b/kernel/drivers/s390/char/sclp.h
@@ -100,13 +100,11 @@ struct init_sccb {
sccb_mask_t sclp_send_mask;
} __attribute__((packed));
-extern u64 sclp_facilities;
-
-#define SCLP_HAS_CHP_INFO (sclp_facilities & 0x8000000000000000ULL)
-#define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL)
-#define SCLP_HAS_CPU_INFO (sclp_facilities & 0x0800000000000000ULL)
-#define SCLP_HAS_CPU_RECONFIG (sclp_facilities & 0x0400000000000000ULL)
-#define SCLP_HAS_PCI_RECONFIG (sclp_facilities & 0x0000000040000000ULL)
+#define SCLP_HAS_CHP_INFO (sclp.facilities & 0x8000000000000000ULL)
+#define SCLP_HAS_CHP_RECONFIG (sclp.facilities & 0x2000000000000000ULL)
+#define SCLP_HAS_CPU_INFO (sclp.facilities & 0x0800000000000000ULL)
+#define SCLP_HAS_CPU_RECONFIG (sclp.facilities & 0x0400000000000000ULL)
+#define SCLP_HAS_PCI_RECONFIG (sclp.facilities & 0x0000000040000000ULL)
struct gds_subvector {
@@ -191,9 +189,6 @@ void sclp_sdias_exit(void);
extern int sclp_console_pages;
extern int sclp_console_drop;
extern unsigned long sclp_console_full;
-extern u8 sclp_fac84;
-extern unsigned long long sclp_rzm;
-extern unsigned long long sclp_rnmax;
/* useful inlines */
diff --git a/kernel/drivers/s390/char/sclp_cmd.c b/kernel/drivers/s390/char/sclp_cmd.c
index 7be782116..806239c2c 100644
--- a/kernel/drivers/s390/char/sclp_cmd.c
+++ b/kernel/drivers/s390/char/sclp_cmd.c
@@ -25,6 +25,7 @@
#include <asm/setup.h>
#include <asm/page.h>
#include <asm/sclp.h>
+#include <asm/numa.h>
#include "sclp.h"
@@ -92,8 +93,8 @@ struct read_cpu_info_sccb {
u8 reserved[4096 - 16];
} __attribute__((packed, aligned(PAGE_SIZE)));
-static void sclp_fill_cpu_info(struct sclp_cpu_info *info,
- struct read_cpu_info_sccb *sccb)
+static void sclp_fill_core_info(struct sclp_core_info *info,
+ struct read_cpu_info_sccb *sccb)
{
char *page = (char *) sccb;
@@ -101,12 +102,11 @@ static void sclp_fill_cpu_info(struct sclp_cpu_info *info,
info->configured = sccb->nr_configured;
info->standby = sccb->nr_standby;
info->combined = sccb->nr_configured + sccb->nr_standby;
- info->has_cpu_type = sclp_fac84 & 0x1;
- memcpy(&info->cpu, page + sccb->offset_configured,
- info->combined * sizeof(struct sclp_cpu_entry));
+ memcpy(&info->core, page + sccb->offset_configured,
+ info->combined * sizeof(struct sclp_core_entry));
}
-int sclp_get_cpu_info(struct sclp_cpu_info *info)
+int sclp_get_core_info(struct sclp_core_info *info)
{
int rc;
struct read_cpu_info_sccb *sccb;
@@ -127,7 +127,7 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info)
rc = -EIO;
goto out;
}
- sclp_fill_cpu_info(info, sccb);
+ sclp_fill_core_info(info, sccb);
out:
free_page((unsigned long) sccb);
return rc;
@@ -137,7 +137,7 @@ struct cpu_configure_sccb {
struct sccb_header header;
} __attribute__((packed, aligned(8)));
-static int do_cpu_configure(sclp_cmdw_t cmd)
+static int do_core_configure(sclp_cmdw_t cmd)
{
struct cpu_configure_sccb *sccb;
int rc;
@@ -171,14 +171,14 @@ out:
return rc;
}
-int sclp_cpu_configure(u8 cpu)
+int sclp_core_configure(u8 core)
{
- return do_cpu_configure(SCLP_CMDW_CONFIGURE_CPU | cpu << 8);
+ return do_core_configure(SCLP_CMDW_CONFIGURE_CPU | core << 8);
}
-int sclp_cpu_deconfigure(u8 cpu)
+int sclp_core_deconfigure(u8 core)
{
- return do_cpu_configure(SCLP_CMDW_DECONFIGURE_CPU | cpu << 8);
+ return do_core_configure(SCLP_CMDW_DECONFIGURE_CPU | core << 8);
}
#ifdef CONFIG_MEMORY_HOTPLUG
@@ -186,7 +186,7 @@ int sclp_cpu_deconfigure(u8 cpu)
static DEFINE_MUTEX(sclp_mem_mutex);
static LIST_HEAD(sclp_mem_list);
static u8 sclp_max_storage_id;
-static unsigned long sclp_storage_ids[256 / BITS_PER_LONG];
+static DECLARE_BITMAP(sclp_storage_ids, 256);
static int sclp_mem_state_changed;
struct memory_increment {
@@ -202,14 +202,14 @@ struct assign_storage_sccb {
int arch_get_memory_phys_device(unsigned long start_pfn)
{
- if (!sclp_rzm)
+ if (!sclp.rzm)
return 0;
- return PFN_PHYS(start_pfn) >> ilog2(sclp_rzm);
+ return PFN_PHYS(start_pfn) >> ilog2(sclp.rzm);
}
static unsigned long long rn2addr(u16 rn)
{
- return (unsigned long long) (rn - 1) * sclp_rzm;
+ return (unsigned long long) (rn - 1) * sclp.rzm;
}
static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
@@ -250,7 +250,7 @@ static int sclp_assign_storage(u16 rn)
if (rc)
return rc;
start = rn2addr(rn);
- storage_key_init_range(start, start + sclp_rzm);
+ storage_key_init_range(start, start + sclp.rzm);
return 0;
}
@@ -309,7 +309,7 @@ static int sclp_mem_change_state(unsigned long start, unsigned long size,
istart = rn2addr(incr->rn);
if (start + size - 1 < istart)
break;
- if (start > istart + sclp_rzm - 1)
+ if (start > istart + sclp.rzm - 1)
continue;
if (online)
rc |= sclp_assign_storage(incr->rn);
@@ -330,7 +330,7 @@ static bool contains_standby_increment(unsigned long start, unsigned long end)
istart = rn2addr(incr->rn);
if (end - 1 < istart)
continue;
- if (start > istart + sclp_rzm - 1)
+ if (start > istart + sclp.rzm - 1)
continue;
if (incr->standby)
return true;
@@ -389,11 +389,11 @@ static struct notifier_block sclp_mem_nb = {
};
static void __init align_to_block_size(unsigned long long *start,
- unsigned long long *size)
+ unsigned long long *size,
+ unsigned long long alignment)
{
- unsigned long long start_align, size_align, alignment;
+ unsigned long long start_align, size_align;
- alignment = memory_block_size_bytes();
start_align = roundup(*start, alignment);
size_align = rounddown(*start + *size, alignment) - start_align;
@@ -405,8 +405,8 @@ static void __init align_to_block_size(unsigned long long *start,
static void __init add_memory_merged(u16 rn)
{
+ unsigned long long start, size, addr, block_size;
static u16 first_rn, num;
- unsigned long long start, size;
if (rn && first_rn && (first_rn + num == rn)) {
num++;
@@ -415,7 +415,7 @@ static void __init add_memory_merged(u16 rn)
if (!first_rn)
goto skip_add;
start = rn2addr(first_rn);
- size = (unsigned long long) num * sclp_rzm;
+ size = (unsigned long long) num * sclp.rzm;
if (start >= VMEM_MAX_PHYS)
goto skip_add;
if (start + size > VMEM_MAX_PHYS)
@@ -424,9 +424,12 @@ static void __init add_memory_merged(u16 rn)
goto skip_add;
if (memory_end_set && (start + size > memory_end))
size = memory_end - start;
- align_to_block_size(&start, &size);
- if (size)
- add_memory(0, start, size);
+ block_size = memory_block_size_bytes();
+ align_to_block_size(&start, &size, block_size);
+ if (!size)
+ goto skip_add;
+ for (addr = start; addr < start + size; addr += block_size)
+ add_memory(numa_pfn_to_nid(PFN_DOWN(addr)), addr, block_size);
skip_add:
first_rn = rn;
num = 1;
@@ -465,7 +468,7 @@ static void __init insert_increment(u16 rn, int standby, int assigned)
}
if (!assigned)
new_incr->rn = last_rn + 1;
- if (new_incr->rn > sclp_rnmax) {
+ if (new_incr->rn > sclp.rnmax) {
kfree(new_incr);
return;
}
@@ -508,7 +511,7 @@ static int __init sclp_detect_standby_memory(void)
if (OLDMEM_BASE) /* No standby memory in kdump mode */
return 0;
- if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL)
+ if ((sclp.facilities & 0xe00000000000ULL) != 0xe00000000000ULL)
return 0;
rc = -ENOMEM;
sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);
@@ -550,7 +553,7 @@ static int __init sclp_detect_standby_memory(void)
}
if (rc || list_empty(&sclp_mem_list))
goto out;
- for (i = 1; i <= sclp_rnmax - assigned; i++)
+ for (i = 1; i <= sclp.rnmax - assigned; i++)
insert_increment(0, 1, 0);
rc = register_memory_notifier(&sclp_mem_nb);
if (rc)
@@ -753,8 +756,3 @@ out:
free_page((unsigned long) sccb);
return rc;
}
-
-bool sclp_has_sprp(void)
-{
- return !!(sclp_fac84 & 0x2);
-}
diff --git a/kernel/drivers/s390/char/sclp_early.c b/kernel/drivers/s390/char/sclp_early.c
index f45cd0cb1..7bc6df310 100644
--- a/kernel/drivers/s390/char/sclp_early.c
+++ b/kernel/drivers/s390/char/sclp_early.c
@@ -49,23 +49,10 @@ struct read_info_sccb {
} __packed __aligned(PAGE_SIZE);
static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata;
-static unsigned int sclp_con_has_vt220 __initdata;
-static unsigned int sclp_con_has_linemode __initdata;
-static unsigned long sclp_hsa_size;
-static unsigned int sclp_max_cpu;
static struct sclp_ipl_info sclp_ipl_info;
-static unsigned char sclp_siif;
-static unsigned char sclp_sigpif;
-static u32 sclp_ibc;
-static unsigned int sclp_mtid;
-static unsigned int sclp_mtid_cp;
-static unsigned int sclp_mtid_max;
-static unsigned int sclp_mtid_prev;
-
-u64 sclp_facilities;
-u8 sclp_fac84;
-unsigned long long sclp_rzm;
-unsigned long long sclp_rnmax;
+
+struct sclp_info sclp;
+EXPORT_SYMBOL(sclp);
static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
{
@@ -112,28 +99,29 @@ static int __init sclp_read_info_early(struct read_info_sccb *sccb)
static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
{
- struct sclp_cpu_entry *cpue;
+ struct sclp_core_entry *cpue;
u16 boot_cpu_address, cpu;
if (sclp_read_info_early(sccb))
return;
- sclp_facilities = sccb->facilities;
- sclp_fac84 = sccb->fac84;
+ sclp.facilities = sccb->facilities;
+ sclp.has_sprp = !!(sccb->fac84 & 0x02);
+ sclp.has_core_type = !!(sccb->fac84 & 0x01);
if (sccb->fac85 & 0x02)
S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP;
- sclp_rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
- sclp_rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
- sclp_rzm <<= 20;
- sclp_ibc = sccb->ibc;
+ sclp.rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
+ sclp.rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
+ sclp.rzm <<= 20;
+ sclp.ibc = sccb->ibc;
if (!sccb->hcpua) {
if (MACHINE_IS_VM)
- sclp_max_cpu = 64;
+ sclp.max_cores = 64;
else
- sclp_max_cpu = sccb->ncpurl;
+ sclp.max_cores = sccb->ncpurl;
} else {
- sclp_max_cpu = sccb->hcpua + 1;
+ sclp.max_cores = sccb->hcpua + 1;
}
boot_cpu_address = stap();
@@ -141,8 +129,8 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
for (cpu = 0; cpu < sccb->ncpurl; cpue++, cpu++) {
if (boot_cpu_address != cpue->core_id)
continue;
- sclp_siif = cpue->siif;
- sclp_sigpif = cpue->sigpif;
+ sclp.has_siif = cpue->siif;
+ sclp.has_sigpif = cpue->sigpif;
break;
}
@@ -152,68 +140,9 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
sclp_ipl_info.has_dump = 1;
memcpy(&sclp_ipl_info.loadparm, &sccb->loadparm, LOADPARM_LEN);
- sclp_mtid = (sccb->fac42 & 0x80) ? (sccb->fac42 & 31) : 0;
- sclp_mtid_cp = (sccb->fac42 & 0x80) ? (sccb->fac43 & 31) : 0;
- sclp_mtid_max = max(sclp_mtid, sclp_mtid_cp);
- sclp_mtid_prev = (sccb->fac42 & 0x80) ? (sccb->fac66 & 31) : 0;
-}
-
-bool __init sclp_has_linemode(void)
-{
- return !!sclp_con_has_linemode;
-}
-
-bool __init sclp_has_vt220(void)
-{
- return !!sclp_con_has_vt220;
-}
-
-unsigned long long sclp_get_rnmax(void)
-{
- return sclp_rnmax;
-}
-
-unsigned long long sclp_get_rzm(void)
-{
- return sclp_rzm;
-}
-
-unsigned int sclp_get_max_cpu(void)
-{
- return sclp_max_cpu;
-}
-
-int sclp_has_siif(void)
-{
- return sclp_siif;
-}
-EXPORT_SYMBOL(sclp_has_siif);
-
-int sclp_has_sigpif(void)
-{
- return sclp_sigpif;
-}
-EXPORT_SYMBOL(sclp_has_sigpif);
-
-unsigned int sclp_get_ibc(void)
-{
- return sclp_ibc;
-}
-EXPORT_SYMBOL(sclp_get_ibc);
-
-unsigned int sclp_get_mtid(u8 cpu_type)
-{
- return cpu_type ? sclp_mtid : sclp_mtid_cp;
-}
-
-unsigned int sclp_get_mtid_max(void)
-{
- return sclp_mtid_max;
-}
-
-unsigned int sclp_get_mtid_prev(void)
-{
- return sclp_mtid_prev;
+ sclp.mtid = (sccb->fac42 & 0x80) ? (sccb->fac42 & 31) : 0;
+ sclp.mtid_cp = (sccb->fac42 & 0x80) ? (sccb->fac43 & 31) : 0;
+ sclp.mtid_prev = (sccb->fac42 & 0x80) ? (sccb->fac66 & 31) : 0;
}
/*
@@ -287,11 +216,6 @@ static long __init sclp_hsa_copy_wait(struct sccb_header *sccb)
return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE;
}
-unsigned long sclp_get_hsa_size(void)
-{
- return sclp_hsa_size;
-}
-
static void __init sclp_hsa_size_detect(void *sccb)
{
long size;
@@ -314,7 +238,7 @@ static void __init sclp_hsa_size_detect(void *sccb)
if (size < 0)
return;
out:
- sclp_hsa_size = size;
+ sclp.hsa_size = size;
}
static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb)
@@ -332,10 +256,10 @@ static void __init sclp_console_detect(struct init_sccb *sccb)
return;
if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK)
- sclp_con_has_vt220 = 1;
+ sclp.has_vt220 = 1;
if (sclp_con_check_linemode(sccb))
- sclp_con_has_linemode = 1;
+ sclp.has_linemode = 1;
}
void __init sclp_early_detect(void)
diff --git a/kernel/drivers/s390/char/sclp_rw.c b/kernel/drivers/s390/char/sclp_rw.c
index 35a84af87..6010cd347 100644
--- a/kernel/drivers/s390/char/sclp_rw.c
+++ b/kernel/drivers/s390/char/sclp_rw.c
@@ -47,9 +47,9 @@ struct sclp_buffer *
sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
{
struct sclp_buffer *buffer;
- struct write_sccb *sccb;
+ struct sccb_header *sccb;
- sccb = (struct write_sccb *) page;
+ sccb = (struct sccb_header *) page;
/*
* We keep the struct sclp_buffer structure at the end
* of the sccb page.
@@ -57,24 +57,16 @@ sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
buffer = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1;
buffer->sccb = sccb;
buffer->retry_count = 0;
- buffer->mto_number = 0;
- buffer->mto_char_sum = 0;
+ buffer->messages = 0;
+ buffer->char_sum = 0;
buffer->current_line = NULL;
buffer->current_length = 0;
buffer->columns = columns;
buffer->htab = htab;
/* initialize sccb */
- memset(sccb, 0, sizeof(struct write_sccb));
- sccb->header.length = sizeof(struct write_sccb);
- sccb->msg_buf.header.length = sizeof(struct msg_buf);
- sccb->msg_buf.header.type = EVTYP_MSG;
- sccb->msg_buf.mdb.header.length = sizeof(struct mdb);
- sccb->msg_buf.mdb.header.type = 1;
- sccb->msg_buf.mdb.header.tag = 0xD4C4C240; /* ebcdic "MDB " */
- sccb->msg_buf.mdb.header.revision_code = 1;
- sccb->msg_buf.mdb.go.length = sizeof(struct go);
- sccb->msg_buf.mdb.go.type = 1;
+ memset(sccb, 0, sizeof(struct sccb_header));
+ sccb->length = sizeof(struct sccb_header);
return buffer;
}
@@ -90,37 +82,49 @@ sclp_unmake_buffer(struct sclp_buffer *buffer)
}
/*
- * Initialize a new Message Text Object (MTO) at the end of the provided buffer
- * with enough room for max_len characters. Return 0 on success.
+ * Initialize a new message the end of the provided buffer with
+ * enough room for max_len characters. Return 0 on success.
*/
static int
sclp_initialize_mto(struct sclp_buffer *buffer, int max_len)
{
- struct write_sccb *sccb;
+ struct sccb_header *sccb;
+ struct msg_buf *msg;
+ struct mdb *mdb;
+ struct go *go;
struct mto *mto;
- int mto_size;
+ int msg_size;
- /* max size of new Message Text Object including message text */
- mto_size = sizeof(struct mto) + max_len;
+ /* max size of new message including message text */
+ msg_size = sizeof(struct msg_buf) + max_len;
/* check if current buffer sccb can contain the mto */
sccb = buffer->sccb;
- if ((MAX_SCCB_ROOM - sccb->header.length) < mto_size)
+ if ((MAX_SCCB_ROOM - sccb->length) < msg_size)
return -ENOMEM;
- /* find address of new message text object */
- mto = (struct mto *)(((addr_t) sccb) + sccb->header.length);
+ msg = (struct msg_buf *)((addr_t) sccb + sccb->length);
+ memset(msg, 0, sizeof(struct msg_buf));
+ msg->header.length = sizeof(struct msg_buf);
+ msg->header.type = EVTYP_MSG;
- /*
- * fill the new Message-Text Object,
- * starting behind the former last byte of the SCCB
- */
- memset(mto, 0, sizeof(struct mto));
+ mdb = &msg->mdb;
+ mdb->header.length = sizeof(struct mdb);
+ mdb->header.type = 1;
+ mdb->header.tag = 0xD4C4C240; /* ebcdic "MDB " */
+ mdb->header.revision_code = 1;
+
+ go = &mdb->go;
+ go->length = sizeof(struct go);
+ go->type = 1;
+
+ mto = &mdb->mto;
mto->length = sizeof(struct mto);
mto->type = 4; /* message text object */
mto->line_type_flags = LNTPFLGS_ENDTEXT; /* end text */
/* set pointer to first byte after struct mto. */
+ buffer->current_msg = msg;
buffer->current_line = (char *) (mto + 1);
buffer->current_length = 0;
@@ -128,45 +132,37 @@ sclp_initialize_mto(struct sclp_buffer *buffer, int max_len)
}
/*
- * Finalize MTO initialized by sclp_initialize_mto(), updating the sizes of
- * MTO, enclosing MDB, event buffer and SCCB.
+ * Finalize message initialized by sclp_initialize_mto(),
+ * updating the sizes of MTO, enclosing MDB, event buffer and SCCB.
*/
static void
sclp_finalize_mto(struct sclp_buffer *buffer)
{
- struct write_sccb *sccb;
- struct mto *mto;
- int str_len, mto_size;
-
- str_len = buffer->current_length;
- buffer->current_line = NULL;
- buffer->current_length = 0;
-
- /* real size of new Message Text Object including message text */
- mto_size = sizeof(struct mto) + str_len;
-
- /* find address of new message text object */
- sccb = buffer->sccb;
- mto = (struct mto *)(((addr_t) sccb) + sccb->header.length);
-
- /* set size of message text object */
- mto->length = mto_size;
+ struct sccb_header *sccb;
+ struct msg_buf *msg;
/*
* update values of sizes
* (SCCB, Event(Message) Buffer, Message Data Block)
*/
- sccb->header.length += mto_size;
- sccb->msg_buf.header.length += mto_size;
- sccb->msg_buf.mdb.header.length += mto_size;
+ sccb = buffer->sccb;
+ msg = buffer->current_msg;
+ msg->header.length += buffer->current_length;
+ msg->mdb.header.length += buffer->current_length;
+ msg->mdb.mto.length += buffer->current_length;
+ sccb->length += msg->header.length;
/*
* count number of buffered messages (= number of Message Text
* Objects) and number of buffered characters
* for the SCCB currently used for buffering and at all
*/
- buffer->mto_number++;
- buffer->mto_char_sum += str_len;
+ buffer->messages++;
+ buffer->char_sum += buffer->current_length;
+
+ buffer->current_line = NULL;
+ buffer->current_length = 0;
+ buffer->current_msg = NULL;
}
/*
@@ -218,7 +214,13 @@ sclp_write(struct sclp_buffer *buffer, const unsigned char *msg, int count)
break;
case '\a': /* bell, one for several times */
/* set SCLP sound alarm bit in General Object */
- buffer->sccb->msg_buf.mdb.go.general_msg_flags |=
+ if (buffer->current_line == NULL) {
+ rc = sclp_initialize_mto(buffer,
+ buffer->columns);
+ if (rc)
+ return i_msg;
+ }
+ buffer->current_msg->mdb.go.general_msg_flags |=
GNRLMSGFLGS_SNDALRM;
break;
case '\t': /* horizontal tabulator */
@@ -309,11 +311,13 @@ sclp_write(struct sclp_buffer *buffer, const unsigned char *msg, int count)
int
sclp_buffer_space(struct sclp_buffer *buffer)
{
+ struct sccb_header *sccb;
int count;
- count = MAX_SCCB_ROOM - buffer->sccb->header.length;
+ sccb = buffer->sccb;
+ count = MAX_SCCB_ROOM - sccb->length;
if (buffer->current_line != NULL)
- count -= sizeof(struct mto) + buffer->current_length;
+ count -= sizeof(struct msg_buf) + buffer->current_length;
return count;
}
@@ -325,7 +329,7 @@ sclp_chars_in_buffer(struct sclp_buffer *buffer)
{
int count;
- count = buffer->mto_char_sum;
+ count = buffer->char_sum;
if (buffer->current_line != NULL)
count += buffer->current_length;
return count;
@@ -378,7 +382,7 @@ sclp_writedata_callback(struct sclp_req *request, void *data)
{
int rc;
struct sclp_buffer *buffer;
- struct write_sccb *sccb;
+ struct sccb_header *sccb;
buffer = (struct sclp_buffer *) data;
sccb = buffer->sccb;
@@ -389,7 +393,7 @@ sclp_writedata_callback(struct sclp_req *request, void *data)
return;
}
/* check SCLP response code and choose suitable action */
- switch (sccb->header.response_code) {
+ switch (sccb->response_code) {
case 0x0020 :
/* Normal completion, buffer processed, message(s) sent */
rc = 0;
@@ -403,7 +407,7 @@ sclp_writedata_callback(struct sclp_req *request, void *data)
/* remove processed buffers and requeue rest */
if (sclp_remove_processed((struct sccb_header *) sccb) > 0) {
/* not all buffers were processed */
- sccb->header.response_code = 0x0000;
+ sccb->response_code = 0x0000;
buffer->request.status = SCLP_REQ_FILLED;
rc = sclp_add_request(request);
if (rc == 0)
@@ -419,14 +423,14 @@ sclp_writedata_callback(struct sclp_req *request, void *data)
break;
}
/* retry request */
- sccb->header.response_code = 0x0000;
+ sccb->response_code = 0x0000;
buffer->request.status = SCLP_REQ_FILLED;
rc = sclp_add_request(request);
if (rc == 0)
return;
break;
default:
- if (sccb->header.response_code == 0x71f0)
+ if (sccb->response_code == 0x71f0)
rc = -ENOMEM;
else
rc = -EINVAL;
@@ -445,25 +449,19 @@ int
sclp_emit_buffer(struct sclp_buffer *buffer,
void (*callback)(struct sclp_buffer *, int))
{
- struct write_sccb *sccb;
-
/* add current line if there is one */
if (buffer->current_line != NULL)
sclp_finalize_mto(buffer);
/* Are there messages in the output buffer ? */
- if (buffer->mto_number == 0)
+ if (buffer->messages == 0)
return -EIO;
- sccb = buffer->sccb;
- /* Use normal write message */
- sccb->msg_buf.header.type = EVTYP_MSG;
-
buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA;
buffer->request.status = SCLP_REQ_FILLED;
buffer->request.callback = sclp_writedata_callback;
buffer->request.callback_data = buffer;
- buffer->request.sccb = sccb;
+ buffer->request.sccb = buffer->sccb;
buffer->callback = callback;
return sclp_add_request(&buffer->request);
}
diff --git a/kernel/drivers/s390/char/sclp_rw.h b/kernel/drivers/s390/char/sclp_rw.h
index 7a7bfc947..e3b029099 100644
--- a/kernel/drivers/s390/char/sclp_rw.h
+++ b/kernel/drivers/s390/char/sclp_rw.h
@@ -45,6 +45,7 @@ struct mdb_header {
struct mdb {
struct mdb_header header;
struct go go;
+ struct mto mto;
} __attribute__((packed));
struct msg_buf {
@@ -52,14 +53,9 @@ struct msg_buf {
struct mdb mdb;
} __attribute__((packed));
-struct write_sccb {
- struct sccb_header header;
- struct msg_buf msg_buf;
-} __attribute__((packed));
-
/* The number of empty mto buffers that can be contained in a single sccb. */
-#define NR_EMPTY_MTO_PER_SCCB ((PAGE_SIZE - sizeof(struct sclp_buffer) - \
- sizeof(struct write_sccb)) / sizeof(struct mto))
+#define NR_EMPTY_MSG_PER_SCCB ((PAGE_SIZE - sizeof(struct sclp_buffer) - \
+ sizeof(struct sccb_header)) / sizeof(struct msg_buf))
/*
* data structure for information about list of SCCBs (only for writing),
@@ -68,7 +64,8 @@ struct write_sccb {
struct sclp_buffer {
struct list_head list; /* list_head for sccb_info chain */
struct sclp_req request;
- struct write_sccb *sccb;
+ void *sccb;
+ struct msg_buf *current_msg;
char *current_line;
int current_length;
int retry_count;
@@ -76,8 +73,8 @@ struct sclp_buffer {
unsigned short columns;
unsigned short htab;
/* statistics about this buffer */
- unsigned int mto_char_sum; /* # chars in sccb */
- unsigned int mto_number; /* # mtos in sccb */
+ unsigned int char_sum; /* # chars in sccb */
+ unsigned int messages; /* # messages in sccb */
/* Callback that is called after reaching final status. */
void (*callback)(struct sclp_buffer *, int);
};
diff --git a/kernel/drivers/s390/char/sclp_sdias.c b/kernel/drivers/s390/char/sclp_sdias.c
index eb7cb076c..7cdd13dd7 100644
--- a/kernel/drivers/s390/char/sclp_sdias.c
+++ b/kernel/drivers/s390/char/sclp_sdias.c
@@ -21,7 +21,6 @@
#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
#define SDIAS_RETRIES 300
-#define SDIAS_SLEEP_TICKS 50
static struct debug_info *sdias_dbf;
@@ -68,7 +67,7 @@ static int sdias_sclp_send(struct sclp_req *req)
/* not initiated, wait some time and retry */
set_current_state(TASK_INTERRUPTIBLE);
TRACE("add request failed: rc = %i\n",rc);
- schedule_timeout(SDIAS_SLEEP_TICKS);
+ schedule_timeout(msecs_to_jiffies(500));
continue;
}
/* initiated, wait for completion of service call */
diff --git a/kernel/drivers/s390/char/sclp_tty.c b/kernel/drivers/s390/char/sclp_tty.c
index 003663288..3c6e174e1 100644
--- a/kernel/drivers/s390/char/sclp_tty.c
+++ b/kernel/drivers/s390/char/sclp_tty.c
@@ -84,8 +84,8 @@ sclp_tty_close(struct tty_struct *tty, struct file *filp)
* to change as output buffers get emptied, or if the output flow
* control is acted. This is not an exact number because not every
* character needs the same space in the sccb. The worst case is
- * a string of newlines. Every newlines creates a new mto which
- * needs 8 bytes.
+ * a string of newlines. Every newline creates a new message which
+ * needs 82 bytes.
*/
static int
sclp_tty_write_room (struct tty_struct *tty)
@@ -97,9 +97,9 @@ sclp_tty_write_room (struct tty_struct *tty)
spin_lock_irqsave(&sclp_tty_lock, flags);
count = 0;
if (sclp_ttybuf != NULL)
- count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct mto);
+ count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct msg_buf);
list_for_each(l, &sclp_tty_pages)
- count += NR_EMPTY_MTO_PER_SCCB;
+ count += NR_EMPTY_MSG_PER_SCCB;
spin_unlock_irqrestore(&sclp_tty_lock, flags);
return count;
}
diff --git a/kernel/drivers/s390/char/sclp_vt220.c b/kernel/drivers/s390/char/sclp_vt220.c
index ae67386c0..68d6ee7ae 100644
--- a/kernel/drivers/s390/char/sclp_vt220.c
+++ b/kernel/drivers/s390/char/sclp_vt220.c
@@ -12,6 +12,7 @@
#include <linux/wait.h>
#include <linux/timer.h>
#include <linux/kernel.h>
+#include <linux/sysrq.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
@@ -27,6 +28,7 @@
#include <asm/uaccess.h>
#include "sclp.h"
+#include "ctrlchar.h"
#define SCLP_VT220_MAJOR TTY_MAJOR
#define SCLP_VT220_MINOR 65
@@ -477,6 +479,53 @@ sclp_vt220_write(struct tty_struct *tty, const unsigned char *buf, int count)
#define SCLP_VT220_SESSION_STARTED 0x80
#define SCLP_VT220_SESSION_DATA 0x00
+#ifdef CONFIG_MAGIC_SYSRQ
+
+static int sysrq_pressed;
+static struct sysrq_work sysrq;
+
+static void sclp_vt220_reset_session(void)
+{
+ sysrq_pressed = 0;
+}
+
+static void sclp_vt220_handle_input(const char *buffer, unsigned int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ /* Handle magic sys request */
+ if (buffer[i] == ('O' ^ 0100)) { /* CTRL-O */
+ /*
+ * If pressed again, reset sysrq_pressed
+ * and flip CTRL-O character
+ */
+ sysrq_pressed = !sysrq_pressed;
+ if (sysrq_pressed)
+ continue;
+ } else if (sysrq_pressed) {
+ sysrq.key = buffer[i];
+ schedule_sysrq_work(&sysrq);
+ sysrq_pressed = 0;
+ continue;
+ }
+ tty_insert_flip_char(&sclp_vt220_port, buffer[i], 0);
+ }
+}
+
+#else
+
+static void sclp_vt220_reset_session(void)
+{
+}
+
+static void sclp_vt220_handle_input(const char *buffer, unsigned int count)
+{
+ tty_insert_flip_string(&sclp_vt220_port, buffer, count);
+}
+
+#endif
+
/*
* Called by the SCLP to report incoming event buffers.
*/
@@ -492,12 +541,13 @@ sclp_vt220_receiver_fn(struct evbuf_header *evbuf)
switch (*buffer) {
case SCLP_VT220_SESSION_ENDED:
case SCLP_VT220_SESSION_STARTED:
+ sclp_vt220_reset_session();
break;
case SCLP_VT220_SESSION_DATA:
/* Send input to line discipline */
buffer++;
count--;
- tty_insert_flip_string(&sclp_vt220_port, buffer, count);
+ sclp_vt220_handle_input(buffer, count);
tty_flip_buffer_push(&sclp_vt220_port);
break;
}
diff --git a/kernel/drivers/s390/char/tty3270.c b/kernel/drivers/s390/char/tty3270.c
index e91b89dc6..e96fc7fd9 100644
--- a/kernel/drivers/s390/char/tty3270.c
+++ b/kernel/drivers/s390/char/tty3270.c
@@ -659,6 +659,10 @@ tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
else
/* Normal end. Copy residual count. */
rq->rescnt = irb->scsw.cmd.count;
+ } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
+ /* Interrupt without an outstanding request -> update all */
+ tp->update_flags = TTY_UPDATE_ALL;
+ tty3270_set_timer(tp, 1);
}
return RAW3270_IO_DONE;
}
diff --git a/kernel/drivers/s390/char/vmlogrdr.c b/kernel/drivers/s390/char/vmlogrdr.c
index 9bb48d709..799c1524c 100644
--- a/kernel/drivers/s390/char/vmlogrdr.c
+++ b/kernel/drivers/s390/char/vmlogrdr.c
@@ -99,8 +99,8 @@ static const struct file_operations vmlogrdr_fops = {
};
-static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 ipuser[16]);
-static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 ipuser[16]);
+static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 *ipuser);
+static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 *ipuser);
static void vmlogrdr_iucv_message_pending(struct iucv_path *,
struct iucv_message *);
@@ -160,7 +160,7 @@ static struct cdev *vmlogrdr_cdev = NULL;
static int recording_class_AB;
-static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
+static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 *ipuser)
{
struct vmlogrdr_priv_t * logptr = path->private;
@@ -171,7 +171,7 @@ static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
}
-static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
+static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
{
struct vmlogrdr_priv_t * logptr = path->private;
u8 reason = (u8) ipuser[8];
diff --git a/kernel/drivers/s390/char/zcore.c b/kernel/drivers/s390/char/zcore.c
index a68fcfd1d..823f41fc4 100644
--- a/kernel/drivers/s390/char/zcore.c
+++ b/kernel/drivers/s390/char/zcore.c
@@ -154,7 +154,7 @@ static int __init init_cpu_info(enum arch_id arch)
/* get info for boot cpu from lowcore, stored in the HSA */
- sa_ext = dump_save_area_create(0);
+ sa_ext = dump_save_areas.areas[0];
if (!sa_ext)
return -ENOMEM;
if (memcpy_hsa_kernel(&sa_ext->sa, sys_info.sa_base,
@@ -330,9 +330,9 @@ static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,
mem_offs = 0;
/* Copy from HSA data */
- if (*ppos < sclp_get_hsa_size() + HEADER_SIZE) {
+ if (*ppos < sclp.hsa_size + HEADER_SIZE) {
size = min((count - hdr_count),
- (size_t) (sclp_get_hsa_size() - mem_start));
+ (size_t) (sclp.hsa_size - mem_start));
rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);
if (rc)
goto fail;
@@ -483,7 +483,7 @@ static ssize_t zcore_hsa_read(struct file *filp, char __user *buf,
static char str[18];
if (hsa_available)
- snprintf(str, sizeof(str), "%lx\n", sclp_get_hsa_size());
+ snprintf(str, sizeof(str), "%lx\n", sclp.hsa_size);
else
snprintf(str, sizeof(str), "0\n");
return simple_read_from_buffer(buf, count, ppos, str, strlen(str));
@@ -558,7 +558,7 @@ static int __init sys_info_init(enum arch_id arch, unsigned long mem_end)
static int __init check_sdias(void)
{
- if (!sclp_get_hsa_size()) {
+ if (!sclp.hsa_size) {
TRACE("Could not determine HSA size\n");
return -ENODEV;
}
@@ -619,7 +619,7 @@ static int __init zcore_reipl_init(void)
ipl_block = (void *) __get_free_page(GFP_KERNEL);
if (!ipl_block)
return -ENOMEM;
- if (ipib_info.ipib < sclp_get_hsa_size())
+ if (ipib_info.ipib < sclp.hsa_size)
rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE);
else
rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE);
diff --git a/kernel/drivers/s390/cio/chsc.c b/kernel/drivers/s390/cio/chsc.c
index e3bf885f4..a831d1859 100644
--- a/kernel/drivers/s390/cio/chsc.c
+++ b/kernel/drivers/s390/cio/chsc.c
@@ -21,6 +21,7 @@
#include <asm/chsc.h>
#include <asm/crw.h>
#include <asm/isc.h>
+#include <asm/ebcdic.h>
#include "css.h"
#include "cio.h"
@@ -272,36 +273,6 @@ static void s390_process_res_acc(struct chp_link *link)
css_schedule_reprobe();
}
-static int
-__get_chpid_from_lir(void *data)
-{
- struct lir {
- u8 iq;
- u8 ic;
- u16 sci;
- /* incident-node descriptor */
- u32 indesc[28];
- /* attached-node descriptor */
- u32 andesc[28];
- /* incident-specific information */
- u32 isinfo[28];
- } __attribute__ ((packed)) *lir;
-
- lir = data;
- if (!(lir->iq&0x80))
- /* NULL link incident record */
- return -EINVAL;
- if (!(lir->indesc[0]&0xc0000000))
- /* node descriptor not valid */
- return -EINVAL;
- if (!(lir->indesc[0]&0x10000000))
- /* don't handle device-type nodes - FIXME */
- return -EINVAL;
- /* Byte 3 contains the chpid. Could also be CTCA, but we don't care */
-
- return (u16) (lir->indesc[0]&0x000000ff);
-}
-
struct chsc_sei_nt0_area {
u8 flags;
u8 vf; /* validity flags */
@@ -341,22 +312,132 @@ struct chsc_sei {
} u;
} __packed;
+/*
+ * Node Descriptor as defined in SA22-7204, "Common I/O-Device Commands"
+ */
+
+#define ND_VALIDITY_VALID 0
+#define ND_VALIDITY_OUTDATED 1
+#define ND_VALIDITY_INVALID 2
+
+struct node_descriptor {
+ /* Flags. */
+ union {
+ struct {
+ u32 validity:3;
+ u32 reserved:5;
+ } __packed;
+ u8 byte0;
+ } __packed;
+
+ /* Node parameters. */
+ u32 params:24;
+
+ /* Node ID. */
+ char type[6];
+ char model[3];
+ char manufacturer[3];
+ char plant[2];
+ char seq[12];
+ u16 tag;
+} __packed;
+
+/*
+ * Link Incident Record as defined in SA22-7202, "ESCON I/O Interface"
+ */
+
+#define LIR_IQ_CLASS_INFO 0
+#define LIR_IQ_CLASS_DEGRADED 1
+#define LIR_IQ_CLASS_NOT_OPERATIONAL 2
+
+struct lir {
+ struct {
+ u32 null:1;
+ u32 reserved:3;
+ u32 class:2;
+ u32 reserved2:2;
+ } __packed iq;
+ u32 ic:8;
+ u32 reserved:16;
+ struct node_descriptor incident_node;
+ struct node_descriptor attached_node;
+ u8 reserved2[32];
+} __packed;
+
+#define PARAMS_LEN 10 /* PARAMS=xx,xxxxxx */
+#define NODEID_LEN 35 /* NODEID=tttttt/mdl,mmm.ppssssssssssss,xxxx */
+
+/* Copy EBCIDC text, convert to ASCII and optionally add delimiter. */
+static char *store_ebcdic(char *dest, const char *src, unsigned long len,
+ char delim)
+{
+ memcpy(dest, src, len);
+ EBCASC(dest, len);
+
+ if (delim)
+ dest[len++] = delim;
+
+ return dest + len;
+}
+
+/* Format node ID and parameters for output in LIR log message. */
+static void format_node_data(char *params, char *id, struct node_descriptor *nd)
+{
+ memset(params, 0, PARAMS_LEN);
+ memset(id, 0, NODEID_LEN);
+
+ if (nd->validity != ND_VALIDITY_VALID) {
+ strncpy(params, "n/a", PARAMS_LEN - 1);
+ strncpy(id, "n/a", NODEID_LEN - 1);
+ return;
+ }
+
+ /* PARAMS=xx,xxxxxx */
+ snprintf(params, PARAMS_LEN, "%02x,%06x", nd->byte0, nd->params);
+ /* NODEID=tttttt/mdl,mmm.ppssssssssssss,xxxx */
+ id = store_ebcdic(id, nd->type, sizeof(nd->type), '/');
+ id = store_ebcdic(id, nd->model, sizeof(nd->model), ',');
+ id = store_ebcdic(id, nd->manufacturer, sizeof(nd->manufacturer), '.');
+ id = store_ebcdic(id, nd->plant, sizeof(nd->plant), 0);
+ id = store_ebcdic(id, nd->seq, sizeof(nd->seq), ',');
+ sprintf(id, "%04X", nd->tag);
+}
+
static void chsc_process_sei_link_incident(struct chsc_sei_nt0_area *sei_area)
{
- struct chp_id chpid;
- int id;
+ struct lir *lir = (struct lir *) &sei_area->ccdf;
+ char iuparams[PARAMS_LEN], iunodeid[NODEID_LEN], auparams[PARAMS_LEN],
+ aunodeid[NODEID_LEN];
- CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n",
- sei_area->rs, sei_area->rsid);
- if (sei_area->rs != 4)
+ CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x, iq=%02x)\n",
+ sei_area->rs, sei_area->rsid, sei_area->ccdf[0]);
+
+ /* Ignore NULL Link Incident Records. */
+ if (lir->iq.null)
return;
- id = __get_chpid_from_lir(sei_area->ccdf);
- if (id < 0)
- CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
- else {
- chp_id_init(&chpid);
- chpid.id = id;
- chsc_chp_offline(chpid);
+
+ /* Inform user that a link requires maintenance actions because it has
+ * become degraded or not operational. Note that this log message is
+ * the primary intention behind a Link Incident Record. */
+
+ format_node_data(iuparams, iunodeid, &lir->incident_node);
+ format_node_data(auparams, aunodeid, &lir->attached_node);
+
+ switch (lir->iq.class) {
+ case LIR_IQ_CLASS_DEGRADED:
+ pr_warn("Link degraded: RS=%02x RSID=%04x IC=%02x "
+ "IUPARAMS=%s IUNODEID=%s AUPARAMS=%s AUNODEID=%s\n",
+ sei_area->rs, sei_area->rsid, lir->ic, iuparams,
+ iunodeid, auparams, aunodeid);
+ break;
+ case LIR_IQ_CLASS_NOT_OPERATIONAL:
+ pr_err("Link stopped: RS=%02x RSID=%04x IC=%02x "
+ "IUPARAMS=%s IUNODEID=%s AUPARAMS=%s AUNODEID=%s\n",
+ sei_area->rs, sei_area->rsid, lir->ic, iuparams,
+ iunodeid, auparams, aunodeid);
+ break;
+ default:
+ break;
}
}
@@ -999,28 +1080,10 @@ void __init chsc_init_cleanup(void)
free_page((unsigned long)sei_page);
}
-int chsc_enable_facility(int operation_code)
+int __chsc_enable_facility(struct chsc_sda_area *sda_area, int operation_code)
{
- unsigned long flags;
int ret;
- struct {
- struct chsc_header request;
- u8 reserved1:4;
- u8 format:4;
- u8 reserved2;
- u16 operation_code;
- u32 reserved3;
- u32 reserved4;
- u32 operation_data_area[252];
- struct chsc_header response;
- u32 reserved5:4;
- u32 format2:4;
- u32 reserved6:24;
- } __attribute__ ((packed)) *sda_area;
- spin_lock_irqsave(&chsc_page_lock, flags);
- memset(chsc_page, 0, PAGE_SIZE);
- sda_area = chsc_page;
sda_area->request.length = 0x0400;
sda_area->request.code = 0x0031;
sda_area->operation_code = operation_code;
@@ -1038,10 +1101,25 @@ int chsc_enable_facility(int operation_code)
default:
ret = chsc_error_from_response(sda_area->response.code);
}
+out:
+ return ret;
+}
+
+int chsc_enable_facility(int operation_code)
+{
+ struct chsc_sda_area *sda_area;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&chsc_page_lock, flags);
+ memset(chsc_page, 0, PAGE_SIZE);
+ sda_area = chsc_page;
+
+ ret = __chsc_enable_facility(sda_area, operation_code);
if (ret != 0)
CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n",
operation_code, sda_area->response.code);
-out:
+
spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
diff --git a/kernel/drivers/s390/cio/chsc.h b/kernel/drivers/s390/cio/chsc.h
index 76c9b5070..0de134c3a 100644
--- a/kernel/drivers/s390/cio/chsc.h
+++ b/kernel/drivers/s390/cio/chsc.h
@@ -115,6 +115,20 @@ struct chsc_scpd {
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed));
+struct chsc_sda_area {
+ struct chsc_header request;
+ u8 :4;
+ u8 format:4;
+ u8 :8;
+ u16 operation_code;
+ u32 :32;
+ u32 :32;
+ u32 operation_data_area[252];
+ struct chsc_header response;
+ u32 :4;
+ u32 format2:4;
+ u32 :24;
+} __packed __aligned(PAGE_SIZE);
extern int chsc_get_ssd_info(struct subchannel_id schid,
struct chsc_ssd_info *ssd);
@@ -122,6 +136,7 @@ extern int chsc_determine_css_characteristics(void);
extern int chsc_init(void);
extern void chsc_init_cleanup(void);
+int __chsc_enable_facility(struct chsc_sda_area *sda_area, int operation_code);
extern int chsc_enable_facility(int);
struct channel_subsystem;
extern int chsc_secm(struct channel_subsystem *, int);
diff --git a/kernel/drivers/s390/cio/cio.c b/kernel/drivers/s390/cio/cio.c
index 07fc5d9e7..690b8547e 100644
--- a/kernel/drivers/s390/cio/cio.c
+++ b/kernel/drivers/s390/cio/cio.c
@@ -476,26 +476,6 @@ static int cio_check_devno_blacklisted(struct subchannel *sch)
return 0;
}
-static int cio_validate_io_subchannel(struct subchannel *sch)
-{
- /* Initialization for io subchannels. */
- if (!css_sch_is_valid(&sch->schib))
- return -ENODEV;
-
- /* Devno is valid. */
- return cio_check_devno_blacklisted(sch);
-}
-
-static int cio_validate_msg_subchannel(struct subchannel *sch)
-{
- /* Initialization for message subchannels. */
- if (!css_sch_is_valid(&sch->schib))
- return -ENODEV;
-
- /* Devno is valid. */
- return cio_check_devno_blacklisted(sch);
-}
-
/**
* cio_validate_subchannel - basic validation of subchannel
* @sch: subchannel structure to be filled out
@@ -533,10 +513,11 @@ int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid)
switch (sch->st) {
case SUBCHANNEL_TYPE_IO:
- err = cio_validate_io_subchannel(sch);
- break;
case SUBCHANNEL_TYPE_MSG:
- err = cio_validate_msg_subchannel(sch);
+ if (!css_sch_is_valid(&sch->schib))
+ err = -ENODEV;
+ else
+ err = cio_check_devno_blacklisted(sch);
break;
default:
err = 0;
@@ -826,11 +807,11 @@ static atomic_t chpid_reset_count;
static void s390_reset_chpids_mcck_handler(void)
{
struct crw crw;
- struct mci *mci;
+ union mci mci;
/* Check for pending channel report word. */
- mci = (struct mci *)&S390_lowcore.mcck_interruption_code;
- if (!mci->cp)
+ mci.val = S390_lowcore.mcck_interruption_code;
+ if (!mci.cp)
return;
/* Process channel report words. */
while (stcrw(&crw) == 0) {
@@ -944,18 +925,32 @@ void reipl_ccw_dev(struct ccw_dev_id *devid)
int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
{
+ static struct chsc_sda_area sda_area __initdata;
struct subchannel_id schid;
struct schib schib;
schid = *(struct subchannel_id *)&S390_lowcore.subchannel_id;
if (!schid.one)
return -ENODEV;
+
+ if (schid.ssid) {
+ /*
+ * Firmware should have already enabled MSS but whoever started
+ * the kernel might have initiated a channel subsystem reset.
+ * Ensure that MSS is enabled.
+ */
+ memset(&sda_area, 0, sizeof(sda_area));
+ if (__chsc_enable_facility(&sda_area, CHSC_SDA_OC_MSS))
+ return -ENODEV;
+ }
if (stsch_err(schid, &schib))
return -ENODEV;
if (schib.pmcw.st != SUBCHANNEL_TYPE_IO)
return -ENODEV;
if (!schib.pmcw.dnv)
return -ENODEV;
+
+ iplinfo->ssid = schid.ssid;
iplinfo->devno = schib.pmcw.dev;
iplinfo->is_qdio = schib.pmcw.qf;
return 0;
diff --git a/kernel/drivers/s390/cio/cmf.c b/kernel/drivers/s390/cio/cmf.c
index 23054f8fa..b2afad5a5 100644
--- a/kernel/drivers/s390/cio/cmf.c
+++ b/kernel/drivers/s390/cio/cmf.c
@@ -113,7 +113,6 @@ module_param(format, bint, 0444);
* @readall: read a measurement block in a common format
* @reset: clear the data in the associated measurement block and
* reset its time stamp
- * @align: align an allocated block so that the hardware can use it
*/
struct cmb_operations {
int (*alloc) (struct ccw_device *);
@@ -122,7 +121,6 @@ struct cmb_operations {
u64 (*read) (struct ccw_device *, int);
int (*readall)(struct ccw_device *, struct cmbdata *);
void (*reset) (struct ccw_device *);
- void *(*align) (void *);
/* private: */
struct attribute_group *attr_group;
};
@@ -186,9 +184,8 @@ static inline void cmf_activate(void *area, unsigned int onoff)
static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
unsigned long address)
{
- struct subchannel *sch;
-
- sch = to_subchannel(cdev->dev.parent);
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ int ret;
sch->config.mme = mme;
sch->config.mbfc = mbfc;
@@ -198,7 +195,15 @@ static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
else
sch->config.mbi = address;
- return cio_commit_config(sch);
+ ret = cio_commit_config(sch);
+ if (!mme && ret == -ENODEV) {
+ /*
+ * The task was to disable measurement block updates but
+ * the subchannel is already gone. Report success.
+ */
+ ret = 0;
+ }
+ return ret;
}
struct set_schib_struct {
@@ -314,7 +319,7 @@ static int cmf_copy_block(struct ccw_device *cdev)
return -EBUSY;
}
cmb_data = cdev->private->cmb;
- hw_block = cmbops->align(cmb_data->hw_block);
+ hw_block = cmb_data->hw_block;
if (!memcmp(cmb_data->last_block, hw_block, cmb_data->size))
/* No need to copy. */
return 0;
@@ -425,7 +430,7 @@ static void cmf_generic_reset(struct ccw_device *cdev)
* Need to reset hw block as well to make the hardware start
* from 0 again.
*/
- memset(cmbops->align(cmb_data->hw_block), 0, cmb_data->size);
+ memset(cmb_data->hw_block, 0, cmb_data->size);
cmb_data->last_update = 0;
}
cdev->private->cmb_start_time = get_tod_clock();
@@ -606,12 +611,6 @@ static void free_cmb(struct ccw_device *cdev)
spin_lock_irq(cdev->ccwlock);
priv = cdev->private;
-
- if (list_empty(&priv->cmb_list)) {
- /* already freed */
- goto out;
- }
-
cmb_data = priv->cmb;
priv->cmb = NULL;
if (cmb_data)
@@ -626,7 +625,6 @@ static void free_cmb(struct ccw_device *cdev)
free_pages((unsigned long)cmb_area.mem, get_order(size));
cmb_area.mem = NULL;
}
-out:
spin_unlock_irq(cdev->ccwlock);
spin_unlock(&cmb_area.lock);
}
@@ -755,11 +753,6 @@ static void reset_cmb(struct ccw_device *cdev)
cmf_generic_reset(cdev);
}
-static void * align_cmb(void *area)
-{
- return area;
-}
-
static struct attribute_group cmf_attr_group;
static struct cmb_operations cmbops_basic = {
@@ -769,7 +762,6 @@ static struct cmb_operations cmbops_basic = {
.read = read_cmb,
.readall = readall_cmb,
.reset = reset_cmb,
- .align = align_cmb,
.attr_group = &cmf_attr_group,
};
@@ -804,64 +796,57 @@ struct cmbe {
u32 device_busy_time;
u32 initial_command_response_time;
u32 reserved[7];
-};
+} __packed __aligned(64);
-/*
- * kmalloc only guarantees 8 byte alignment, but we need cmbe
- * pointers to be naturally aligned. Make sure to allocate
- * enough space for two cmbes.
- */
-static inline struct cmbe *cmbe_align(struct cmbe *c)
-{
- unsigned long addr;
- addr = ((unsigned long)c + sizeof (struct cmbe) - sizeof(long)) &
- ~(sizeof (struct cmbe) - sizeof(long));
- return (struct cmbe*)addr;
-}
+static struct kmem_cache *cmbe_cache;
static int alloc_cmbe(struct ccw_device *cdev)
{
- struct cmbe *cmbe;
struct cmb_data *cmb_data;
- int ret;
+ struct cmbe *cmbe;
+ int ret = -ENOMEM;
- cmbe = kzalloc (sizeof (*cmbe) * 2, GFP_KERNEL);
+ cmbe = kmem_cache_zalloc(cmbe_cache, GFP_KERNEL);
if (!cmbe)
- return -ENOMEM;
- cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL);
- if (!cmb_data) {
- ret = -ENOMEM;
+ return ret;
+
+ cmb_data = kzalloc(sizeof(*cmb_data), GFP_KERNEL);
+ if (!cmb_data)
goto out_free;
- }
+
cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL);
- if (!cmb_data->last_block) {
- ret = -ENOMEM;
+ if (!cmb_data->last_block)
goto out_free;
- }
- cmb_data->size = sizeof(struct cmbe);
- spin_lock_irq(cdev->ccwlock);
- if (cdev->private->cmb) {
- spin_unlock_irq(cdev->ccwlock);
- ret = -EBUSY;
- goto out_free;
- }
+
+ cmb_data->size = sizeof(*cmbe);
cmb_data->hw_block = cmbe;
+
+ spin_lock(&cmb_area.lock);
+ spin_lock_irq(cdev->ccwlock);
+ if (cdev->private->cmb)
+ goto out_unlock;
+
cdev->private->cmb = cmb_data;
- spin_unlock_irq(cdev->ccwlock);
/* activate global measurement if this is the first channel */
- spin_lock(&cmb_area.lock);
if (list_empty(&cmb_area.list))
cmf_activate(NULL, 1);
list_add_tail(&cdev->private->cmb_list, &cmb_area.list);
- spin_unlock(&cmb_area.lock);
+ spin_unlock_irq(cdev->ccwlock);
+ spin_unlock(&cmb_area.lock);
return 0;
+
+out_unlock:
+ spin_unlock_irq(cdev->ccwlock);
+ spin_unlock(&cmb_area.lock);
+ ret = -EBUSY;
out_free:
if (cmb_data)
kfree(cmb_data->last_block);
kfree(cmb_data);
- kfree(cmbe);
+ kmem_cache_free(cmbe_cache, cmbe);
+
return ret;
}
@@ -869,19 +854,21 @@ static void free_cmbe(struct ccw_device *cdev)
{
struct cmb_data *cmb_data;
+ spin_lock(&cmb_area.lock);
spin_lock_irq(cdev->ccwlock);
cmb_data = cdev->private->cmb;
cdev->private->cmb = NULL;
- if (cmb_data)
+ if (cmb_data) {
kfree(cmb_data->last_block);
+ kmem_cache_free(cmbe_cache, cmb_data->hw_block);
+ }
kfree(cmb_data);
- spin_unlock_irq(cdev->ccwlock);
/* deactivate global measurement if this is the last channel */
- spin_lock(&cmb_area.lock);
list_del_init(&cdev->private->cmb_list);
if (list_empty(&cmb_area.list))
cmf_activate(NULL, 0);
+ spin_unlock_irq(cdev->ccwlock);
spin_unlock(&cmb_area.lock);
}
@@ -897,7 +884,7 @@ static int set_cmbe(struct ccw_device *cdev, u32 mme)
return -EINVAL;
}
cmb_data = cdev->private->cmb;
- mba = mme ? (unsigned long) cmbe_align(cmb_data->hw_block) : 0;
+ mba = mme ? (unsigned long) cmb_data->hw_block : 0;
spin_unlock_irqrestore(cdev->ccwlock, flags);
return set_schib_wait(cdev, mme, 1, mba);
@@ -1022,11 +1009,6 @@ static void reset_cmbe(struct ccw_device *cdev)
cmf_generic_reset(cdev);
}
-static void * align_cmbe(void *area)
-{
- return cmbe_align(area);
-}
-
static struct attribute_group cmf_attr_group_ext;
static struct cmb_operations cmbops_extended = {
@@ -1036,7 +1018,6 @@ static struct cmb_operations cmbops_extended = {
.read = read_cmbe,
.readall = readall_cmbe,
.reset = reset_cmbe,
- .align = align_cmbe,
.attr_group = &cmf_attr_group_ext,
};
@@ -1171,23 +1152,28 @@ static ssize_t cmb_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- return sprintf(buf, "%d\n", to_ccwdev(dev)->private->cmb ? 1 : 0);
+ struct ccw_device *cdev = to_ccwdev(dev);
+ int enabled;
+
+ spin_lock_irq(cdev->ccwlock);
+ enabled = !!cdev->private->cmb;
+ spin_unlock_irq(cdev->ccwlock);
+
+ return sprintf(buf, "%d\n", enabled);
}
static ssize_t cmb_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t c)
{
- struct ccw_device *cdev;
- int ret;
+ struct ccw_device *cdev = to_ccwdev(dev);
unsigned long val;
+ int ret;
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
- cdev = to_ccwdev(dev);
-
switch (val) {
case 0:
ret = disable_cmf(cdev);
@@ -1195,12 +1181,13 @@ static ssize_t cmb_enable_store(struct device *dev,
case 1:
ret = enable_cmf(cdev);
break;
+ default:
+ ret = -EINVAL;
}
- return c;
+ return ret ? ret : c;
}
-
-DEVICE_ATTR(cmb_enable, 0644, cmb_enable_show, cmb_enable_store);
+DEVICE_ATTR_RW(cmb_enable);
int ccw_set_cmf(struct ccw_device *cdev, int enable)
{
@@ -1220,41 +1207,71 @@ int enable_cmf(struct ccw_device *cdev)
{
int ret;
+ device_lock(&cdev->dev);
+ get_device(&cdev->dev);
ret = cmbops->alloc(cdev);
- cmbops->reset(cdev);
if (ret)
- return ret;
+ goto out;
+ cmbops->reset(cdev);
+ ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group);
+ if (ret) {
+ cmbops->free(cdev);
+ goto out;
+ }
ret = cmbops->set(cdev, 2);
if (ret) {
+ sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
cmbops->free(cdev);
- return ret;
}
- ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group);
- if (!ret)
- return 0;
- cmbops->set(cdev, 0); //FIXME: this can fail
- cmbops->free(cdev);
+out:
+ if (ret)
+ put_device(&cdev->dev);
+
+ device_unlock(&cdev->dev);
return ret;
}
/**
- * disable_cmf() - switch off the channel measurement for a specific device
+ * __disable_cmf() - switch off the channel measurement for a specific device
* @cdev: The ccw device to be disabled
*
* Returns %0 for success or a negative error value.
*
* Context:
- * non-atomic
+ * non-atomic, device_lock() held.
*/
-int disable_cmf(struct ccw_device *cdev)
+int __disable_cmf(struct ccw_device *cdev)
{
int ret;
ret = cmbops->set(cdev, 0);
if (ret)
return ret;
- cmbops->free(cdev);
+
sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
+ cmbops->free(cdev);
+ put_device(&cdev->dev);
+
+ return ret;
+}
+
+/**
+ * disable_cmf() - switch off the channel measurement for a specific device
+ * @cdev: The ccw device to be disabled
+ *
+ * Returns %0 for success or a negative error value.
+ *
+ * Context:
+ * non-atomic
+ */
+int disable_cmf(struct ccw_device *cdev)
+{
+ int ret;
+
+ device_lock(&cdev->dev);
+ ret = __disable_cmf(cdev);
+ device_unlock(&cdev->dev);
+
return ret;
}
@@ -1295,10 +1312,32 @@ int cmf_reenable(struct ccw_device *cdev)
return cmbops->set(cdev, 2);
}
+/**
+ * cmf_reactivate() - reactivate measurement block updates
+ *
+ * Use this during resume from hibernate.
+ */
+void cmf_reactivate(void)
+{
+ spin_lock(&cmb_area.lock);
+ if (!list_empty(&cmb_area.list))
+ cmf_activate(cmb_area.mem, 1);
+ spin_unlock(&cmb_area.lock);
+}
+
+static int __init init_cmbe(void)
+{
+ cmbe_cache = kmem_cache_create("cmbe_cache", sizeof(struct cmbe),
+ __alignof__(struct cmbe), 0, NULL);
+
+ return cmbe_cache ? 0 : -ENOMEM;
+}
+
static int __init init_cmf(void)
{
char *format_string;
- char *detect_string = "parameter";
+ char *detect_string;
+ int ret;
/*
* If the user did not give a parameter, see if we are running on a
@@ -1324,15 +1363,18 @@ static int __init init_cmf(void)
case CMF_EXTENDED:
format_string = "extended";
cmbops = &cmbops_extended;
+
+ ret = init_cmbe();
+ if (ret)
+ return ret;
break;
default:
- return 1;
+ return -EINVAL;
}
pr_info("Channel measurement facility initialized using format "
"%s (mode %s)\n", format_string, detect_string);
return 0;
}
-
module_init(init_cmf);
diff --git a/kernel/drivers/s390/cio/css.c b/kernel/drivers/s390/cio/css.c
index 0268e5fd5..489e703dc 100644
--- a/kernel/drivers/s390/cio/css.c
+++ b/kernel/drivers/s390/cio/css.c
@@ -44,7 +44,6 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
int ret;
init_subchannel_id(&schid);
- ret = -ENODEV;
do {
do {
ret = fn(schid, data);
@@ -703,17 +702,12 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
css->global_pgid.pgid_high.ext_cssid.version = 0x80;
css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid;
} else {
-#ifdef CONFIG_SMP
css->global_pgid.pgid_high.cpu_addr = stap();
-#else
- css->global_pgid.pgid_high.cpu_addr = 0;
-#endif
}
get_cpu_id(&cpu_id);
css->global_pgid.cpu_id = cpu_id.ident;
css->global_pgid.cpu_model = cpu_id.machine;
css->global_pgid.tod_high = tod_high;
-
}
static void
@@ -1089,6 +1083,7 @@ void channel_subsystem_reinit(void)
if (chp)
chp_update_desc(chp);
}
+ cmf_reactivate();
}
#ifdef CONFIG_PROC_FS
diff --git a/kernel/drivers/s390/cio/device.c b/kernel/drivers/s390/cio/device.c
index dfef5e63c..6aae68412 100644
--- a/kernel/drivers/s390/cio/device.c
+++ b/kernel/drivers/s390/cio/device.c
@@ -1787,6 +1787,8 @@ static int ccw_device_remove(struct device *dev)
cdev->drv = NULL;
cdev->private->int_class = IRQIO_CIO;
spin_unlock_irq(cdev->ccwlock);
+ __disable_cmf(cdev);
+
return 0;
}
@@ -1797,7 +1799,7 @@ static void ccw_device_shutdown(struct device *dev)
cdev = to_ccwdev(dev);
if (cdev->drv && cdev->drv->shutdown)
cdev->drv->shutdown(cdev);
- disable_cmf(cdev);
+ __disable_cmf(cdev);
}
static int ccw_device_pm_prepare(struct device *dev)
diff --git a/kernel/drivers/s390/cio/device.h b/kernel/drivers/s390/cio/device.h
index 8d1d29873..065b1be98 100644
--- a/kernel/drivers/s390/cio/device.h
+++ b/kernel/drivers/s390/cio/device.h
@@ -125,11 +125,6 @@ void ccw_device_verify_done(struct ccw_device *, int);
void ccw_device_disband_start(struct ccw_device *);
void ccw_device_disband_done(struct ccw_device *, int);
-void ccw_device_stlck_start(struct ccw_device *, void *, void *, void *);
-void ccw_device_stlck_done(struct ccw_device *, void *, int);
-
-int ccw_device_call_handler(struct ccw_device *);
-
int ccw_device_stlck(struct ccw_device *);
/* Helper function for machine check handling. */
@@ -145,6 +140,7 @@ void ccw_device_set_timeout(struct ccw_device *, int);
void retry_set_schib(struct ccw_device *cdev);
void cmf_retry_copy_block(struct ccw_device *);
int cmf_reenable(struct ccw_device *);
+void cmf_reactivate(void);
int ccw_set_cmf(struct ccw_device *cdev, int enable);
extern struct device_attribute dev_attr_cmb_enable;
#endif
diff --git a/kernel/drivers/s390/cio/device_fsm.c b/kernel/drivers/s390/cio/device_fsm.c
index 83da53c8e..92e03b42e 100644
--- a/kernel/drivers/s390/cio/device_fsm.c
+++ b/kernel/drivers/s390/cio/device_fsm.c
@@ -731,6 +731,44 @@ static void ccw_device_boxed_verify(struct ccw_device *cdev,
}
/*
+ * Pass interrupt to device driver.
+ */
+static int ccw_device_call_handler(struct ccw_device *cdev)
+{
+ unsigned int stctl;
+ int ending_status;
+
+ /*
+ * we allow for the device action handler if .
+ * - we received ending status
+ * - the action handler requested to see all interrupts
+ * - we received an intermediate status
+ * - fast notification was requested (primary status)
+ * - unsolicited interrupts
+ */
+ stctl = scsw_stctl(&cdev->private->irb.scsw);
+ ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
+ (stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
+ (stctl == SCSW_STCTL_STATUS_PEND);
+ if (!ending_status &&
+ !cdev->private->options.repall &&
+ !(stctl & SCSW_STCTL_INTER_STATUS) &&
+ !(cdev->private->options.fast &&
+ (stctl & SCSW_STCTL_PRIM_STATUS)))
+ return 0;
+
+ if (ending_status)
+ ccw_device_set_timeout(cdev, 0);
+
+ if (cdev->handler)
+ cdev->handler(cdev, cdev->private->intparm,
+ &cdev->private->irb);
+
+ memset(&cdev->private->irb, 0, sizeof(struct irb));
+ return 1;
+}
+
+/*
* Got an interrupt for a normal io (state online).
*/
static void
diff --git a/kernel/drivers/s390/cio/device_ops.c b/kernel/drivers/s390/cio/device_ops.c
index f3c417943..a69f702a2 100644
--- a/kernel/drivers/s390/cio/device_ops.c
+++ b/kernel/drivers/s390/cio/device_ops.c
@@ -412,52 +412,6 @@ int ccw_device_resume(struct ccw_device *cdev)
return cio_resume(sch);
}
-/*
- * Pass interrupt to device driver.
- */
-int
-ccw_device_call_handler(struct ccw_device *cdev)
-{
- unsigned int stctl;
- int ending_status;
-
- /*
- * we allow for the device action handler if .
- * - we received ending status
- * - the action handler requested to see all interrupts
- * - we received an intermediate status
- * - fast notification was requested (primary status)
- * - unsolicited interrupts
- */
- stctl = scsw_stctl(&cdev->private->irb.scsw);
- ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
- (stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
- (stctl == SCSW_STCTL_STATUS_PEND);
- if (!ending_status &&
- !cdev->private->options.repall &&
- !(stctl & SCSW_STCTL_INTER_STATUS) &&
- !(cdev->private->options.fast &&
- (stctl & SCSW_STCTL_PRIM_STATUS)))
- return 0;
-
- /* Clear pending timers for device driver initiated I/O. */
- if (ending_status)
- ccw_device_set_timeout(cdev, 0);
- /*
- * Now we are ready to call the device driver interrupt handler.
- */
- if (cdev->handler)
- cdev->handler(cdev, cdev->private->intparm,
- &cdev->private->irb);
-
- /*
- * Clear the old and now useless interrupt response block.
- */
- memset(&cdev->private->irb, 0, sizeof(struct irb));
-
- return 1;
-}
-
/**
* ccw_device_get_ciw() - Search for CIW command in extended sense data.
* @cdev: ccw device to inspect
@@ -502,67 +456,6 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
return sch->lpm;
}
-struct stlck_data {
- struct completion done;
- int rc;
-};
-
-void ccw_device_stlck_done(struct ccw_device *cdev, void *data, int rc)
-{
- struct stlck_data *sdata = data;
-
- sdata->rc = rc;
- complete(&sdata->done);
-}
-
-/*
- * Perform unconditional reserve + release.
- */
-int ccw_device_stlck(struct ccw_device *cdev)
-{
- struct subchannel *sch = to_subchannel(cdev->dev.parent);
- struct stlck_data data;
- u8 *buffer;
- int rc;
-
- /* Check if steal lock operation is valid for this device. */
- if (cdev->drv) {
- if (!cdev->private->options.force)
- return -EINVAL;
- }
- buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
- init_completion(&data.done);
- data.rc = -EIO;
- spin_lock_irq(sch->lock);
- rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
- if (rc)
- goto out_unlock;
- /* Perform operation. */
- cdev->private->state = DEV_STATE_STEAL_LOCK,
- ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]);
- spin_unlock_irq(sch->lock);
- /* Wait for operation to finish. */
- if (wait_for_completion_interruptible(&data.done)) {
- /* Got a signal. */
- spin_lock_irq(sch->lock);
- ccw_request_cancel(cdev);
- spin_unlock_irq(sch->lock);
- wait_for_completion(&data.done);
- }
- rc = data.rc;
- /* Check results. */
- spin_lock_irq(sch->lock);
- cio_disable_subchannel(sch);
- cdev->private->state = DEV_STATE_BOXED;
-out_unlock:
- spin_unlock_irq(sch->lock);
- kfree(buffer);
-
- return rc;
-}
-
/**
* chp_get_chp_desc - return newly allocated channel-path descriptor
* @cdev: device to obtain the descriptor for
diff --git a/kernel/drivers/s390/cio/device_pgid.c b/kernel/drivers/s390/cio/device_pgid.c
index 37ada05e8..da246b67e 100644
--- a/kernel/drivers/s390/cio/device_pgid.c
+++ b/kernel/drivers/s390/cio/device_pgid.c
@@ -9,9 +9,10 @@
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/bitops.h>
#include <linux/types.h>
#include <linux/errno.h>
-#include <linux/bitops.h>
+#include <linux/slab.h>
#include <asm/ccwdev.h>
#include <asm/cio.h>
@@ -133,7 +134,7 @@ static void spid_build_cp(struct ccw_device *cdev, u8 fn)
{
struct ccw_request *req = &cdev->private->req;
struct ccw1 *cp = cdev->private->iccws;
- int i = 8 - ffs(req->lpm);
+ int i = pathmask_to_pos(req->lpm);
struct pgid *pgid = &cdev->private->pgid[i];
pgid->inf.fc = fn;
@@ -434,7 +435,7 @@ static void snid_build_cp(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
struct ccw1 *cp = cdev->private->iccws;
- int i = 8 - ffs(req->lpm);
+ int i = pathmask_to_pos(req->lpm);
/* Channel program setup. */
cp->cmd_code = CCW_CMD_SENSE_PGID;
@@ -616,6 +617,11 @@ void ccw_device_disband_start(struct ccw_device *cdev)
ccw_request_start(cdev);
}
+struct stlck_data {
+ struct completion done;
+ int rc;
+};
+
static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2)
{
struct ccw_request *req = &cdev->private->req;
@@ -634,7 +640,10 @@ static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2)
static void stlck_callback(struct ccw_device *cdev, void *data, int rc)
{
- ccw_device_stlck_done(cdev, data, rc);
+ struct stlck_data *sdata = data;
+
+ sdata->rc = rc;
+ complete(&sdata->done);
}
/**
@@ -645,11 +654,9 @@ static void stlck_callback(struct ccw_device *cdev, void *data, int rc)
* @buf2: data pointer used in channel program
*
* Execute a channel program on @cdev to release an existing PGID reservation.
- * When finished, call ccw_device_stlck_done with a return code specifying the
- * result.
*/
-void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1,
- void *buf2)
+static void ccw_device_stlck_start(struct ccw_device *cdev, void *data,
+ void *buf1, void *buf2)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct ccw_request *req = &cdev->private->req;
@@ -667,3 +674,50 @@ void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1,
ccw_request_start(cdev);
}
+/*
+ * Perform unconditional reserve + release.
+ */
+int ccw_device_stlck(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct stlck_data data;
+ u8 *buffer;
+ int rc;
+
+ /* Check if steal lock operation is valid for this device. */
+ if (cdev->drv) {
+ if (!cdev->private->options.force)
+ return -EINVAL;
+ }
+ buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ init_completion(&data.done);
+ data.rc = -EIO;
+ spin_lock_irq(sch->lock);
+ rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
+ if (rc)
+ goto out_unlock;
+ /* Perform operation. */
+ cdev->private->state = DEV_STATE_STEAL_LOCK;
+ ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]);
+ spin_unlock_irq(sch->lock);
+ /* Wait for operation to finish. */
+ if (wait_for_completion_interruptible(&data.done)) {
+ /* Got a signal. */
+ spin_lock_irq(sch->lock);
+ ccw_request_cancel(cdev);
+ spin_unlock_irq(sch->lock);
+ wait_for_completion(&data.done);
+ }
+ rc = data.rc;
+ /* Check results. */
+ spin_lock_irq(sch->lock);
+ cio_disable_subchannel(sch);
+ cdev->private->state = DEV_STATE_BOXED;
+out_unlock:
+ spin_unlock_irq(sch->lock);
+ kfree(buffer);
+
+ return rc;
+}
diff --git a/kernel/drivers/s390/cio/eadm_sch.c b/kernel/drivers/s390/cio/eadm_sch.c
index bee8c11cd..b3f44bc7f 100644
--- a/kernel/drivers/s390/cio/eadm_sch.c
+++ b/kernel/drivers/s390/cio/eadm_sch.c
@@ -336,7 +336,6 @@ static int eadm_subchannel_sch_event(struct subchannel *sch, int process)
{
struct eadm_private *private;
unsigned long flags;
- int ret = 0;
spin_lock_irqsave(sch->lock, flags);
if (!device_is_registered(&sch->dev))
@@ -356,7 +355,7 @@ static int eadm_subchannel_sch_event(struct subchannel *sch, int process)
out_unlock:
spin_unlock_irqrestore(sch->lock, flags);
- return ret;
+ return 0;
}
static struct css_device_id eadm_subchannel_ids[] = {
diff --git a/kernel/drivers/s390/cio/qdio_main.c b/kernel/drivers/s390/cio/qdio_main.c
index 848e3b64e..4bb5262f7 100644
--- a/kernel/drivers/s390/cio/qdio_main.c
+++ b/kernel/drivers/s390/cio/qdio_main.c
@@ -319,6 +319,8 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit,
int retries = 0, cc;
unsigned long laob = 0;
+ WARN_ON_ONCE(aob && ((queue_type(q) != QDIO_IQDIO_QFMT) ||
+ !q->u.out.use_cq));
if (q->u.out.use_cq && aob != 0) {
fc = QDIO_SIGA_WRITEQ;
laob = aob;
@@ -329,8 +331,6 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit,
fc |= QDIO_SIGA_QEBSM_FLAG;
}
again:
- WARN_ON_ONCE((aob && queue_type(q) != QDIO_IQDIO_QFMT) ||
- (aob && fc != QDIO_SIGA_WRITEQ));
cc = do_siga_output(schid, q->mask, busy_bit, fc, laob);
/* hipersocket busy condition */
diff --git a/kernel/drivers/s390/crypto/Makefile b/kernel/drivers/s390/crypto/Makefile
index 771faf709..b8ab18676 100644
--- a/kernel/drivers/s390/crypto/Makefile
+++ b/kernel/drivers/s390/crypto/Makefile
@@ -3,6 +3,9 @@
#
ap-objs := ap_bus.o
-obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o zcrypt_pcicc.o zcrypt_pcixcc.o
-obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o zcrypt_cex4.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*
+obj-$(CONFIG_ZCRYPT) += zcrypt_pcixcc.o zcrypt_cex2a.o zcrypt_cex4.o
diff --git a/kernel/drivers/s390/crypto/ap_bus.c b/kernel/drivers/s390/crypto/ap_bus.c
index 3ba611419..24ec282e1 100644
--- a/kernel/drivers/s390/crypto/ap_bus.c
+++ b/kernel/drivers/s390/crypto/ap_bus.c
@@ -37,6 +37,7 @@
#include <linux/notifier.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
+#include <linux/suspend.h>
#include <asm/reset.h>
#include <asm/airq.h>
#include <linux/atomic.h>
@@ -48,23 +49,6 @@
#include "ap_bus.h"
-/* Some prototypes. */
-static void ap_scan_bus(struct work_struct *);
-static void ap_poll_all(unsigned long);
-static enum hrtimer_restart ap_poll_timeout(struct hrtimer *);
-static int ap_poll_thread_start(void);
-static void ap_poll_thread_stop(void);
-static void ap_request_timeout(unsigned long);
-static inline void ap_schedule_poll_timer(void);
-static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags);
-static int ap_device_remove(struct device *dev);
-static int ap_device_probe(struct device *dev);
-static void ap_interrupt_handler(struct airq_struct *airq);
-static void ap_reset(struct ap_device *ap_dev);
-static void ap_config_timeout(unsigned long ptr);
-static int ap_select_domain(void);
-static void ap_query_configuration(void);
-
/*
* Module description.
*/
@@ -90,19 +74,21 @@ static struct device *ap_root_device = NULL;
static struct ap_config_info *ap_configuration;
static DEFINE_SPINLOCK(ap_device_list_lock);
static LIST_HEAD(ap_device_list);
+static bool initialised;
/*
- * Workqueue & timer for bus rescan.
+ * Workqueue timer for bus rescan.
*/
-static struct workqueue_struct *ap_work_queue;
static struct timer_list ap_config_timer;
static int ap_config_time = AP_CONFIG_TIME;
-static DECLARE_WORK(ap_config_work, ap_scan_bus);
+static void ap_scan_bus(struct work_struct *);
+static DECLARE_WORK(ap_scan_work, ap_scan_bus);
/*
* Tasklet & timer for AP request polling and interrupts
*/
-static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0);
+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;
@@ -115,6 +101,8 @@ static unsigned long long poll_timeout = 250000;
/* Suspend flag */
static int ap_suspend_flag;
+/* Maximum domain id */
+static int ap_max_domain_id;
/* Flag to check if domain was set through module parameter domain=. This is
* important when supsend and resume is done in a z/VM environment where the
* domain might change. */
@@ -122,6 +110,8 @@ static int user_set_domain = 0;
static struct bus_type ap_bus_type;
/* Adapter interrupt definitions */
+static void ap_interrupt_handler(struct airq_struct *airq);
+
static int ap_airq_flag;
static struct airq_struct ap_airq = {
@@ -182,44 +172,27 @@ static int ap_configuration_available(void)
/**
* ap_test_queue(): Test adjunct processor queue.
* @qid: The AP queue number
- * @queue_depth: Pointer to queue depth value
- * @device_type: Pointer to device type value
+ * @info: Pointer to queue descriptor
*
* Returns AP queue status structure.
*/
static inline struct ap_queue_status
-ap_test_queue(ap_qid_t qid, int *queue_depth, int *device_type)
+ap_test_queue(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;
+ if (test_facility(15))
+ reg0 |= 1UL << 23; /* set APFT T bit*/
asm volatile(".long 0xb2af0000" /* PQAP(TAPQ) */
: "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc");
- *device_type = (int) (reg2 >> 24);
- *queue_depth = (int) (reg2 & 0xff);
+ if (info)
+ *info = reg2;
return reg1;
}
/**
- * ap_query_facilities(): PQAP(TAPQ) query facilities.
- * @qid: The AP queue number
- *
- * Returns content of general register 2 after the PQAP(TAPQ)
- * instruction was called.
- */
-static inline unsigned long ap_query_facilities(ap_qid_t qid)
-{
- register unsigned long reg0 asm ("0") = qid | 0x00800000UL;
- register unsigned long reg1 asm ("1");
- register unsigned long reg2 asm ("2") = 0UL;
-
- asm volatile(".long 0xb2af0000" /* PQAP(TAPQ) */
- : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc");
- return reg2;
-}
-
-/**
* ap_reset_queue(): Reset adjunct processor queue.
* @qid: The AP queue number
*
@@ -259,31 +232,19 @@ ap_queue_interruption_control(ap_qid_t qid, void *ind)
return reg1_out;
}
-static inline struct ap_queue_status
-__ap_query_functions(ap_qid_t qid, unsigned int *functions)
-{
- register unsigned long reg0 asm ("0") = 0UL | qid | (1UL << 23);
- register struct ap_queue_status reg1 asm ("1") = AP_QUEUE_STATUS_INVALID;
- register unsigned long reg2 asm ("2");
-
- asm volatile(
- ".long 0xb2af0000\n" /* PQAP(TAPQ) */
- "0:\n"
- EX_TABLE(0b, 0b)
- : "+d" (reg0), "+d" (reg1), "=d" (reg2)
- :
- : "cc");
-
- *functions = (unsigned int)(reg2 >> 32);
- return reg1;
-}
-
-static inline int __ap_query_configuration(struct ap_config_info *config)
+/**
+ * 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 unsigned char *reg2 asm ("2") = (unsigned char *)config;
+ register void *reg2 asm ("2") = (void *) ap_configuration;
+ if (!ap_configuration)
+ return -EOPNOTSUPP;
asm volatile(
".long 0xb2af0000\n" /* PQAP(QCI) */
"0: la %1,0\n"
@@ -297,48 +258,60 @@ static inline int __ap_query_configuration(struct ap_config_info *config)
}
/**
- * ap_query_functions(): Query supported functions.
- * @qid: The AP queue number
- * @functions: Pointer to functions field.
- *
- * Returns
- * 0 on success.
- * -ENODEV if queue not valid.
- * -EBUSY if device busy.
- * -EINVAL if query function is not supported
+ * ap_init_configuration(): Allocate and query configuration array.
*/
-static int ap_query_functions(ap_qid_t qid, unsigned int *functions)
+static void ap_init_configuration(void)
{
- struct ap_queue_status status;
- int i;
- status = __ap_query_functions(qid, functions);
-
- for (i = 0; i < AP_MAX_RESET; i++) {
- if (ap_queue_status_invalid_test(&status))
- return -ENODEV;
+ if (!ap_configuration_available())
+ return;
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- return 0;
- case AP_RESPONSE_RESET_IN_PROGRESS:
- case AP_RESPONSE_BUSY:
- break;
- case AP_RESPONSE_Q_NOT_AVAIL:
- case AP_RESPONSE_DECONFIGURED:
- case AP_RESPONSE_CHECKSTOPPED:
- case AP_RESPONSE_INVALID_ADDRESS:
- return -ENODEV;
- case AP_RESPONSE_OTHERWISE_CHANGED:
- break;
- default:
- break;
- }
- if (i < AP_MAX_RESET - 1) {
- udelay(5);
- status = __ap_query_functions(qid, functions);
- }
+ ap_configuration = kzalloc(sizeof(*ap_configuration), GFP_KERNEL);
+ if (!ap_configuration)
+ return;
+ if (ap_query_configuration() != 0) {
+ kfree(ap_configuration);
+ ap_configuration = NULL;
+ return;
}
- return -EBUSY;
+}
+
+/*
+ * ap_test_config(): helper function to extract the nrth bit
+ * within the unsigned int array field.
+ */
+static inline int ap_test_config(unsigned int *field, unsigned int nr)
+{
+ return ap_test_bit((field + (nr >> 5)), (nr & 0x1f));
+}
+
+/*
+ * ap_test_config_card_id(): Test, whether an AP card ID is configured.
+ * @id AP card ID
+ *
+ * Returns 0 if the card is not configured
+ * 1 if the card is configured or
+ * if the configuration information is not available
+ */
+static inline int ap_test_config_card_id(unsigned int id)
+{
+ if (!ap_configuration) /* QCI not supported */
+ return 1;
+ return ap_test_config(ap_configuration->apm, id);
+}
+
+/*
+ * ap_test_config_domain(): Test, whether an AP usage domain is configured.
+ * @domain AP usage domain ID
+ *
+ * Returns 0 if the usage domain is not configured
+ * 1 if the usage domain is configured or
+ * if the configuration information is not available
+ */
+static inline int ap_test_config_domain(unsigned int domain)
+{
+ if (!ap_configuration) /* QCI not supported */
+ return domain < 16;
+ return ap_test_config(ap_configuration->aqm, domain);
}
/**
@@ -350,47 +323,27 @@ static int ap_query_functions(ap_qid_t qid, unsigned int *functions)
* 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(ap_qid_t qid, void *ind)
+static int ap_queue_enable_interruption(struct ap_device *ap_dev, void *ind)
{
struct ap_queue_status status;
- int t_depth, t_device_type, rc, i;
-
- rc = -EBUSY;
- status = ap_queue_interruption_control(qid, ind);
- for (i = 0; i < AP_MAX_RESET; i++) {
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- if (status.int_enabled)
- return 0;
- break;
- case AP_RESPONSE_RESET_IN_PROGRESS:
- case AP_RESPONSE_BUSY:
- if (i < AP_MAX_RESET - 1) {
- udelay(5);
- status = ap_queue_interruption_control(qid,
- ind);
- continue;
- }
- break;
- case AP_RESPONSE_Q_NOT_AVAIL:
- case AP_RESPONSE_DECONFIGURED:
- case AP_RESPONSE_CHECKSTOPPED:
- case AP_RESPONSE_INVALID_ADDRESS:
- return -ENODEV;
- case AP_RESPONSE_OTHERWISE_CHANGED:
- if (status.int_enabled)
- return 0;
- break;
- default:
- break;
- }
- if (i < AP_MAX_RESET - 1) {
- udelay(5);
- status = ap_test_queue(qid, &t_depth, &t_device_type);
- }
+ 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;
}
- return rc;
}
/**
@@ -515,148 +468,558 @@ EXPORT_SYMBOL(ap_recv);
* @qid: The AP queue number
* @queue_depth: Pointer to queue depth value
* @device_type: Pointer to device type value
- *
- * The test is repeated for AP_MAX_RESET times.
+ * @facilities: Pointer to facility indicator
*/
-static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type)
+static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type,
+ unsigned int *facilities)
{
struct ap_queue_status status;
- int t_depth, t_device_type, rc, i;
-
- rc = -EBUSY;
- for (i = 0; i < AP_MAX_RESET; i++) {
- status = ap_test_queue(qid, &t_depth, &t_device_type);
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- *queue_depth = t_depth + 1;
- *device_type = t_device_type;
- rc = 0;
- break;
- case AP_RESPONSE_Q_NOT_AVAIL:
- rc = -ENODEV;
- break;
- case AP_RESPONSE_RESET_IN_PROGRESS:
- break;
- case AP_RESPONSE_DECONFIGURED:
- rc = -ENODEV;
- break;
- case AP_RESPONSE_CHECKSTOPPED:
- rc = -ENODEV;
- break;
- case AP_RESPONSE_INVALID_ADDRESS:
- rc = -ENODEV;
- break;
- case AP_RESPONSE_OTHERWISE_CHANGED:
+ unsigned long info;
+ int nd;
+
+ if (!ap_test_config_card_id(AP_QID_DEVICE(qid)))
+ return -ENODEV;
+
+ status = ap_test_queue(qid, &info);
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ *queue_depth = (int)(info & 0xff);
+ *device_type = (int)((info >> 24) & 0xff);
+ *facilities = (unsigned int)(info >> 32);
+ /* Update maximum domain id */
+ nd = (info >> 16) & 0xff;
+ if ((info & (1UL << 57)) && nd > 0)
+ ap_max_domain_id = nd;
+ return 0;
+ case AP_RESPONSE_Q_NOT_AVAIL:
+ case AP_RESPONSE_DECONFIGURED:
+ case AP_RESPONSE_CHECKSTOPPED:
+ case AP_RESPONSE_INVALID_ADDRESS:
+ return -ENODEV;
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ case AP_RESPONSE_OTHERWISE_CHANGED:
+ case AP_RESPONSE_BUSY:
+ return -EBUSY;
+ default:
+ BUG();
+ }
+}
+
+/* State machine definitions and helpers */
+
+static void ap_sm_wait(enum ap_wait wait)
+{
+ ktime_t hr_time;
+
+ switch (wait) {
+ case AP_WAIT_AGAIN:
+ case AP_WAIT_INTERRUPT:
+ if (ap_using_interrupts())
break;
- case AP_RESPONSE_BUSY:
+ if (ap_poll_kthread) {
+ wake_up(&ap_poll_wait);
break;
- default:
- BUG();
}
- if (rc != -EBUSY)
- break;
- if (i < AP_MAX_RESET - 1)
- udelay(5);
+ /* Fall through */
+ 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);
+ hrtimer_forward_now(&ap_poll_timer, hr_time);
+ hrtimer_restart(&ap_poll_timer);
+ }
+ spin_unlock_bh(&ap_poll_timer_lock);
+ break;
+ case AP_WAIT_NONE:
+ default:
+ break;
}
- return rc;
+}
+
+static enum ap_wait ap_sm_nop(struct ap_device *ap_dev)
+{
+ return AP_WAIT_NONE;
}
/**
- * ap_init_queue(): Reset an AP queue.
- * @qid: The AP queue number
+ * 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
*
- * Reset an AP queue and wait for it to become available again.
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
*/
-static int ap_init_queue(ap_qid_t qid)
+static struct ap_queue_status ap_sm_recv(struct ap_device *ap_dev)
{
struct ap_queue_status status;
- int rc, dummy, i;
-
- rc = -ENODEV;
- status = ap_reset_queue(qid);
- for (i = 0; i < AP_MAX_RESET; i++) {
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- if (status.queue_empty)
- rc = 0;
- break;
- case AP_RESPONSE_Q_NOT_AVAIL:
- case AP_RESPONSE_DECONFIGURED:
- case AP_RESPONSE_CHECKSTOPPED:
- i = AP_MAX_RESET; /* return with -ENODEV */
- break;
- case AP_RESPONSE_RESET_IN_PROGRESS:
- rc = -EBUSY;
- case AP_RESPONSE_BUSY:
- default:
+ 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;
}
- if (rc != -ENODEV && rc != -EBUSY)
+ case AP_RESPONSE_NO_PENDING_REPLY:
+ if (!status.queue_empty || ap_dev->queue_count <= 0)
break;
- if (i < AP_MAX_RESET - 1) {
- /* Time we are waiting until we give up (0.7sec * 90).
- * Since the actual request (in progress) will not
- * interrupted immediately for the reset command,
- * we have to be patient. In worst case we have to
- * wait 60sec + reset time (some msec).
- */
- schedule_timeout(AP_RESET_TIMEOUT);
- status = ap_test_queue(qid, &dummy, &dummy);
+ /* 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;
+
+ 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;
}
- if (rc == 0 && ap_using_interrupts()) {
- rc = ap_queue_enable_interruption(qid, ap_airq.lsi_ptr);
- /* If interruption mode is supported by the machine,
- * but an AP can not be enabled for interruption then
- * the AP will be discarded. */
- if (rc)
- pr_err("Registering adapter interrupts for "
- "AP %d failed\n", AP_QID_DEVICE(qid));
+}
+
+/**
+ * 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;
}
- return rc;
}
/**
- * ap_increase_queue_count(): Arm request timeout.
- * @ap_dev: Pointer to an AP device.
+ * 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
*
- * Arm request timeout if an AP device was idle and a new request is submitted.
+ * Submit the Reset command to an AP queue.
*/
-static void ap_increase_queue_count(struct ap_device *ap_dev)
+static enum ap_wait ap_sm_reset(struct ap_device *ap_dev)
{
- int timeout = ap_dev->drv->request_timeout;
+ struct ap_queue_status status;
- ap_dev->queue_count++;
- if (ap_dev->queue_count == 1) {
- mod_timer(&ap_dev->timeout, jiffies + timeout);
- ap_dev->reset = AP_RESET_ARMED;
+ 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_decrease_queue_count(): Decrease queue count.
- * @ap_dev: Pointer to an AP device.
+ * ap_sm_reset_wait(): Test queue for completion of the reset operation
+ * @ap_dev: pointer to the AP device
*
- * If AP device is still alive, re-schedule request timeout if there are still
- * pending requests.
+ * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0.
*/
-static void ap_decrease_queue_count(struct ap_device *ap_dev)
+static enum ap_wait ap_sm_reset_wait(struct ap_device *ap_dev)
{
- int timeout = ap_dev->drv->request_timeout;
+ struct ap_queue_status status;
+ unsigned long info;
- ap_dev->queue_count--;
if (ap_dev->queue_count > 0)
- mod_timer(&ap_dev->timeout, jiffies + timeout);
+ /* Try to read a completed message and get the status */
+ status = ap_sm_recv(ap_dev);
else
- /*
- * The timeout timer should to be disabled now - since
- * del_timer_sync() is very expensive, we just tell via the
- * reset flag to ignore the pending timeout timer.
- */
- ap_dev->reset = AP_RESET_IGNORE;
+ /* 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)
+ /* 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
+ */
+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_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)
+{
+ struct ap_device *ap_dev = (struct ap_device *) 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);
+}
+
+/**
+ * ap_poll_timeout(): AP receive polling for finished AP requests.
+ * @unused: Unused pointer.
+ *
+ * Schedules the AP tasklet using a high resolution timer.
+ */
+static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
+{
+ if (!ap_suspend_flag)
+ tasklet_schedule(&ap_tasklet);
+ return HRTIMER_NORESTART;
+}
+
+/**
+ * ap_interrupt_handler() - Schedule ap_tasklet on interrupt
+ * @airq: pointer to adapter interrupt descriptor
+ */
+static void ap_interrupt_handler(struct airq_struct *airq)
+{
+ inc_irq_stat(IRQIO_APB);
+ if (!ap_suspend_flag)
+ tasklet_schedule(&ap_tasklet);
+}
+
+/**
+ * ap_tasklet_fn(): Tasklet to poll all AP devices.
+ * @dummy: Unused variable
+ *
+ * Poll all AP devices on the bus.
+ */
+static void ap_tasklet_fn(unsigned long dummy)
+{
+ struct ap_device *ap_dev;
+ enum ap_wait wait = AP_WAIT_NONE;
+
+ /* Reset the indicator if interrupts are used. Thus new interrupts can
+ * be received. Doing it in the beginning of the tasklet is therefor
+ * important that no requests on any AP get lost.
+ */
+ 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_unlock(&ap_device_list_lock);
+ ap_sm_wait(wait);
+}
+
+/**
+ * ap_poll_thread(): Thread that polls for finished requests.
+ * @data: Unused pointer
+ *
+ * AP bus poll thread. The purpose of this thread is to poll for
+ * finished requests in a loop if there is a "free" cpu - that is
+ * a cpu that doesn't have anything better to do. The polling stops
+ * as soon as there is another task or if all messages have been
+ * delivered.
+ */
+static int ap_poll_thread(void *data)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ set_user_nice(current, MAX_NICE);
+ set_freezable();
+ 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) {
+ schedule();
+ try_to_freeze();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&ap_poll_wait, &wait);
+ if (need_resched()) {
+ schedule();
+ try_to_freeze();
+ continue;
+ }
+ ap_tasklet_fn(0);
+ } while (!kthread_should_stop());
+ return 0;
+}
+
+static int ap_poll_thread_start(void)
+{
+ int rc;
+
+ if (ap_using_interrupts() || ap_poll_kthread)
+ return 0;
+ mutex_lock(&ap_poll_thread_mutex);
+ ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
+ rc = PTR_RET(ap_poll_kthread);
+ if (rc)
+ ap_poll_kthread = NULL;
+ mutex_unlock(&ap_poll_thread_mutex);
+ return rc;
+}
+
+static void ap_poll_thread_stop(void)
+{
+ if (!ap_poll_kthread)
+ return;
+ mutex_lock(&ap_poll_thread_mutex);
+ kthread_stop(ap_poll_kthread);
+ ap_poll_kthread = NULL;
+ 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,
@@ -729,10 +1092,54 @@ static ssize_t ap_pendingq_count_show(struct device *dev,
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", to_ap_dev(dev)->device_type);
+ return sprintf(buf, "ap:t%02X\n", to_ap_dev(dev)->device_type);
}
static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL);
@@ -753,6 +1160,8 @@ static struct attribute *ap_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,
&dev_attr_modalias.attr,
&dev_attr_ap_functions.attr,
NULL
@@ -814,99 +1223,95 @@ static int ap_uevent (struct device *dev, struct kobj_uevent_env *env)
return retval;
}
-static int ap_bus_suspend(struct device *dev, pm_message_t state)
+static int ap_dev_suspend(struct device *dev, pm_message_t state)
{
struct ap_device *ap_dev = to_ap_dev(dev);
- unsigned long flags;
-
- if (!ap_suspend_flag) {
- ap_suspend_flag = 1;
-
- /* Disable scanning for devices, thus we do not want to scan
- * for them after removing.
- */
- del_timer_sync(&ap_config_timer);
- if (ap_work_queue != NULL) {
- destroy_workqueue(ap_work_queue);
- ap_work_queue = NULL;
- }
- tasklet_disable(&ap_tasklet);
- }
/* Poll on the device until all requests are finished. */
- do {
- flags = 0;
- spin_lock_bh(&ap_dev->lock);
- __ap_poll_device(ap_dev, &flags);
- spin_unlock_bh(&ap_dev->lock);
- } while ((flags & 1) || (flags & 2));
-
spin_lock_bh(&ap_dev->lock);
- ap_dev->unregistered = 1;
+ 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);
+ return 0;
+}
+static int ap_dev_resume(struct device *dev)
+{
return 0;
}
-static int ap_bus_resume(struct device *dev)
+static void ap_bus_suspend(void)
+{
+ ap_suspend_flag = 1;
+ /*
+ * Disable scanning for devices, thus we do not want to scan
+ * for them after removing.
+ */
+ flush_work(&ap_scan_work);
+ tasklet_disable(&ap_tasklet);
+}
+
+static int __ap_devices_unregister(struct device *dev, void *dummy)
+{
+ device_unregister(dev);
+ return 0;
+}
+
+static void ap_bus_resume(void)
{
- struct ap_device *ap_dev = to_ap_dev(dev);
int rc;
- if (ap_suspend_flag) {
- ap_suspend_flag = 0;
- if (ap_interrupts_available()) {
- if (!ap_using_interrupts()) {
- rc = register_adapter_interrupt(&ap_airq);
- ap_airq_flag = (rc == 0);
- }
- } else {
- if (ap_using_interrupts()) {
- unregister_adapter_interrupt(&ap_airq);
- ap_airq_flag = 0;
- }
- }
- ap_query_configuration();
- if (!user_set_domain) {
- ap_domain_index = -1;
- ap_select_domain();
- }
- init_timer(&ap_config_timer);
- ap_config_timer.function = ap_config_timeout;
- ap_config_timer.data = 0;
- ap_config_timer.expires = jiffies + ap_config_time * HZ;
- add_timer(&ap_config_timer);
- ap_work_queue = create_singlethread_workqueue("kapwork");
- if (!ap_work_queue)
- return -ENOMEM;
- tasklet_enable(&ap_tasklet);
- if (!ap_using_interrupts())
- ap_schedule_poll_timer();
- else
- tasklet_schedule(&ap_tasklet);
- if (ap_thread_flag)
- rc = ap_poll_thread_start();
- else
- rc = 0;
- } else
- rc = 0;
- if (AP_QID_QUEUE(ap_dev->qid) != ap_domain_index) {
- spin_lock_bh(&ap_dev->lock);
- ap_dev->qid = AP_MKQID(AP_QID_DEVICE(ap_dev->qid),
- ap_domain_index);
- spin_unlock_bh(&ap_dev->lock);
+ /* Unconditionally remove all AP devices */
+ bus_for_each_dev(&ap_bus_type, NULL, NULL, __ap_devices_unregister);
+ /* Reset thin interrupt setting */
+ if (ap_interrupts_available() && !ap_using_interrupts()) {
+ rc = register_adapter_interrupt(&ap_airq);
+ ap_airq_flag = (rc == 0);
+ }
+ if (!ap_interrupts_available() && ap_using_interrupts()) {
+ unregister_adapter_interrupt(&ap_airq);
+ ap_airq_flag = 0;
}
- queue_work(ap_work_queue, &ap_config_work);
+ /* Reset domain */
+ if (!user_set_domain)
+ ap_domain_index = -1;
+ /* Get things going again */
+ ap_suspend_flag = 0;
+ if (ap_airq_flag)
+ xchg(ap_airq.lsi_ptr, 0);
+ tasklet_enable(&ap_tasklet);
+ queue_work(system_long_wq, &ap_scan_work);
+}
- return rc;
+static int ap_power_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ switch (event) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ ap_bus_suspend();
+ break;
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ ap_bus_resume();
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
}
+static struct notifier_block ap_power_notifier = {
+ .notifier_call = ap_power_event,
+};
static struct bus_type ap_bus_type = {
.name = "ap",
.match = &ap_bus_match,
.uevent = &ap_uevent,
- .suspend = ap_bus_suspend,
- .resume = ap_bus_resume
+ .suspend = ap_dev_suspend,
+ .resume = ap_dev_resume,
};
static int ap_device_probe(struct device *dev)
@@ -916,17 +1321,9 @@ static int ap_device_probe(struct device *dev)
int rc;
ap_dev->drv = ap_drv;
-
- spin_lock_bh(&ap_device_list_lock);
- list_add(&ap_dev->list, &ap_device_list);
- spin_unlock_bh(&ap_device_list_lock);
-
rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV;
- if (rc) {
- spin_lock_bh(&ap_device_list_lock);
- list_del_init(&ap_dev->list);
- spin_unlock_bh(&ap_device_list_lock);
- }
+ if (rc)
+ ap_dev->drv = NULL;
return rc;
}
@@ -943,12 +1340,14 @@ static void __ap_flush_queue(struct ap_device *ap_dev)
list_for_each_entry_safe(ap_msg, next, &ap_dev->pendingq, list) {
list_del_init(&ap_msg->list);
ap_dev->pendingq_count--;
- ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+ 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->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+ ap_msg->rc = -EAGAIN;
+ ap_msg->receive(ap_dev, ap_msg, NULL);
}
}
@@ -978,11 +1377,19 @@ static int ap_device_remove(struct device *dev)
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)
{
struct device_driver *drv = &ap_drv->driver;
+ if (!initialised)
+ return -ENODEV;
+
drv->bus = &ap_bus_type;
drv->probe = ap_device_probe;
drv->remove = ap_device_remove;
@@ -1000,60 +1407,16 @@ EXPORT_SYMBOL(ap_driver_unregister);
void ap_bus_force_rescan(void)
{
- /* reconfigure the AP bus rescan timer. */
- mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
+ if (ap_suspend_flag)
+ return;
/* processing a asynchronous bus rescan */
- queue_work(ap_work_queue, &ap_config_work);
- flush_work(&ap_config_work);
+ del_timer(&ap_config_timer);
+ queue_work(system_long_wq, &ap_scan_work);
+ flush_work(&ap_scan_work);
}
EXPORT_SYMBOL(ap_bus_force_rescan);
/*
- * ap_test_config(): helper function to extract the nrth bit
- * within the unsigned int array field.
- */
-static inline int ap_test_config(unsigned int *field, unsigned int nr)
-{
- if (nr > 0xFFu)
- return 0;
- return ap_test_bit((field + (nr >> 5)), (nr & 0x1f));
-}
-
-/*
- * ap_test_config_card_id(): Test, whether an AP card ID is configured.
- * @id AP card ID
- *
- * Returns 0 if the card is not configured
- * 1 if the card is configured or
- * if the configuration information is not available
- */
-static inline int ap_test_config_card_id(unsigned int id)
-{
- if (!ap_configuration)
- return 1;
- return ap_test_config(ap_configuration->apm, id);
-}
-
-/*
- * ap_test_config_domain(): Test, whether an AP usage domain is configured.
- * @domain AP usage domain ID
- *
- * Returns 0 if the usage domain is not configured
- * 1 if the usage domain is configured or
- * if the configuration information is not available
- */
-static inline int ap_test_config_domain(unsigned int domain)
-{
- if (!ap_configuration) /* QCI not supported */
- if (domain < 16)
- return 1; /* then domains 0...15 are configured */
- else
- return 0;
- else
- return ap_test_config(ap_configuration->aqm, domain);
-}
-
-/*
* AP bus attributes.
*/
static ssize_t ap_domain_show(struct bus_type *bus, char *buf)
@@ -1065,21 +1428,20 @@ static BUS_ATTR(ap_domain, 0444, ap_domain_show, NULL);
static ssize_t ap_control_domain_mask_show(struct bus_type *bus, char *buf)
{
- if (ap_configuration != NULL) { /* QCI not supported */
- if (test_facility(76)) { /* format 1 - 256 bit domain field */
- return snprintf(buf, PAGE_SIZE,
- "0x%08x%08x%08x%08x%08x%08x%08x%08x\n",
+ 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],
ap_configuration->adm[2], ap_configuration->adm[3],
ap_configuration->adm[4], ap_configuration->adm[5],
ap_configuration->adm[6], ap_configuration->adm[7]);
- } else { /* format 0 - 16 bit domain field */
- return snprintf(buf, PAGE_SIZE, "%08x%08x\n",
- ap_configuration->adm[0], ap_configuration->adm[1]);
- }
- } else {
- return snprintf(buf, PAGE_SIZE, "not supported\n");
- }
}
static BUS_ATTR(ap_control_domain_mask, 0444,
@@ -1106,11 +1468,7 @@ static ssize_t ap_config_time_store(struct bus_type *bus,
if (sscanf(buf, "%d\n", &time) != 1 || time < 5 || time > 120)
return -EINVAL;
ap_config_time = time;
- if (!timer_pending(&ap_config_timer) ||
- !mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ)) {
- ap_config_timer.expires = jiffies + ap_config_time * HZ;
- add_timer(&ap_config_timer);
- }
+ mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
return count;
}
@@ -1131,9 +1489,8 @@ static ssize_t ap_poll_thread_store(struct bus_type *bus,
if (flag) {
rc = ap_poll_thread_start();
if (rc)
- return rc;
- }
- else
+ count = rc;
+ } else
ap_poll_thread_stop();
return count;
}
@@ -1171,35 +1528,12 @@ static BUS_ATTR(poll_timeout, 0644, poll_timeout_show, poll_timeout_store);
static ssize_t ap_max_domain_id_show(struct bus_type *bus, char *buf)
{
- ap_qid_t qid;
- int i, nd, max_domain_id = -1;
- unsigned long fbits;
-
- if (ap_configuration) {
- if (ap_domain_index >= 0 && ap_domain_index < AP_DOMAINS) {
- for (i = 0; i < AP_DEVICES; i++) {
- if (!ap_test_config_card_id(i))
- continue;
- qid = AP_MKQID(i, ap_domain_index);
- fbits = ap_query_facilities(qid);
- if (fbits & (1UL << 57)) {
- /* the N bit is 0, Nd field is filled */
- nd = (int)((fbits & 0x00FF0000UL)>>16);
- if (nd > 0)
- max_domain_id = nd;
- else
- max_domain_id = 15;
- } else {
- /* N bit is 1, max 16 domains */
- max_domain_id = 15;
- }
- break;
- }
- }
- } else {
- /* no APXA support, older machines with max 16 domains */
+ int max_domain_id;
+
+ if (ap_configuration)
+ max_domain_id = ap_max_domain_id ? : -1;
+ else
max_domain_id = 15;
- }
return snprintf(buf, PAGE_SIZE, "%d\n", max_domain_id);
}
@@ -1217,44 +1551,22 @@ static struct bus_attribute *const ap_bus_attrs[] = {
};
/**
- * ap_query_configuration(): Query AP configuration information.
- *
- * Query information of installed cards and configured domains from AP.
- */
-static void ap_query_configuration(void)
-{
- if (ap_configuration_available()) {
- if (!ap_configuration)
- ap_configuration =
- kzalloc(sizeof(struct ap_config_info),
- GFP_KERNEL);
- if (ap_configuration)
- __ap_query_configuration(ap_configuration);
- } else
- ap_configuration = NULL;
-}
-
-/**
* ap_select_domain(): Select an AP domain.
*
* Pick one of the 16 AP domains.
*/
static int ap_select_domain(void)
{
- int queue_depth, device_type, count, max_count, best_domain;
- ap_qid_t qid;
- int rc, i, j;
-
- /* IF APXA isn't installed, only 16 domains could be defined */
- if (!ap_configuration->ap_extended && (ap_domain_index > 15))
- return -EINVAL;
+ int count, max_count, best_domain;
+ struct ap_queue_status status;
+ int i, j;
/*
* We want to use a single domain. Either the one specified with
* the "domain=" parameter or the domain with the maximum number
* of devices.
*/
- if (ap_domain_index >= 0 && ap_domain_index < AP_DOMAINS)
+ if (ap_domain_index >= 0)
/* Domain has already been selected. */
return 0;
best_domain = -1;
@@ -1266,9 +1578,8 @@ static int ap_select_domain(void)
for (j = 0; j < AP_DEVICES; j++) {
if (!ap_test_config_card_id(j))
continue;
- qid = AP_MKQID(j, i);
- rc = ap_query_queue(qid, &queue_depth, &device_type);
- if (rc)
+ status = ap_test_queue(AP_MKQID(j, i), NULL);
+ if (status.response_code != AP_RESPONSE_NORMAL)
continue;
count++;
}
@@ -1285,109 +1596,6 @@ static int ap_select_domain(void)
}
/**
- * ap_probe_device_type(): Find the device type of an AP.
- * @ap_dev: pointer to the AP device.
- *
- * Find the device type if query queue returned a device type of 0.
- */
-static int ap_probe_device_type(struct ap_device *ap_dev)
-{
- static unsigned char msg[] = {
- 0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x01,0x00,0x43,0x43,0x41,0x2d,0x41,0x50,
- 0x50,0x4c,0x20,0x20,0x20,0x01,0x01,0x01,
- 0x00,0x00,0x00,0x00,0x50,0x4b,0x00,0x00,
- 0x00,0x00,0x01,0x1c,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x05,0xb8,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x70,0x00,0x41,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x54,0x32,0x01,0x00,0xa0,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0xb8,0x05,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,
- 0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20,
- 0x50,0x4b,0x0a,0x00,0x50,0x4b,0x43,0x53,
- 0x2d,0x31,0x2e,0x32,0x37,0x00,0x11,0x22,
- 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,
- 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,
- 0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66,
- 0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,
- 0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,
- 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,
- 0x11,0x22,0x33,0x5d,0x00,0x5b,0x00,0x77,
- 0x88,0x1e,0x00,0x00,0x57,0x00,0x00,0x00,
- 0x00,0x04,0x00,0x00,0x4f,0x00,0x00,0x00,
- 0x03,0x02,0x00,0x00,0x40,0x01,0x00,0x01,
- 0xce,0x02,0x68,0x2d,0x5f,0xa9,0xde,0x0c,
- 0xf6,0xd2,0x7b,0x58,0x4b,0xf9,0x28,0x68,
- 0x3d,0xb4,0xf4,0xef,0x78,0xd5,0xbe,0x66,
- 0x63,0x42,0xef,0xf8,0xfd,0xa4,0xf8,0xb0,
- 0x8e,0x29,0xc2,0xc9,0x2e,0xd8,0x45,0xb8,
- 0x53,0x8c,0x6f,0x4e,0x72,0x8f,0x6c,0x04,
- 0x9c,0x88,0xfc,0x1e,0xc5,0x83,0x55,0x57,
- 0xf7,0xdd,0xfd,0x4f,0x11,0x36,0x95,0x5d,
- };
- struct ap_queue_status status;
- unsigned long long psmid;
- char *reply;
- int rc, i;
-
- reply = (void *) get_zeroed_page(GFP_KERNEL);
- if (!reply) {
- rc = -ENOMEM;
- goto out;
- }
-
- status = __ap_send(ap_dev->qid, 0x0102030405060708ULL,
- msg, sizeof(msg), 0);
- if (status.response_code != AP_RESPONSE_NORMAL) {
- rc = -ENODEV;
- goto out_free;
- }
-
- /* Wait for the test message to complete. */
- for (i = 0; i < 6; i++) {
- mdelay(300);
- status = __ap_recv(ap_dev->qid, &psmid, reply, 4096);
- if (status.response_code == AP_RESPONSE_NORMAL &&
- psmid == 0x0102030405060708ULL)
- break;
- }
- if (i < 6) {
- /* Got an answer. */
- if (reply[0] == 0x00 && reply[1] == 0x86)
- ap_dev->device_type = AP_DEVICE_TYPE_PCICC;
- else
- ap_dev->device_type = AP_DEVICE_TYPE_PCICA;
- rc = 0;
- } else
- rc = -ENODEV;
-
-out_free:
- free_page((unsigned long) reply);
-out:
- return rc;
-}
-
-static void ap_interrupt_handler(struct airq_struct *airq)
-{
- inc_irq_stat(IRQIO_APB);
- tasklet_schedule(&ap_tasklet);
-}
-
-/**
* __ap_scan_bus(): Scan the AP bus.
* @dev: Pointer to device
* @data: Pointer to data
@@ -1399,560 +1607,111 @@ static int __ap_scan_bus(struct device *dev, void *data)
return to_ap_dev(dev)->qid == (ap_qid_t)(unsigned long) data;
}
-static void ap_device_release(struct device *dev)
-{
- struct ap_device *ap_dev = to_ap_dev(dev);
-
- kfree(ap_dev);
-}
-
static void ap_scan_bus(struct work_struct *unused)
{
struct ap_device *ap_dev;
struct device *dev;
ap_qid_t qid;
- int queue_depth, device_type;
- unsigned int device_functions;
- int rc, i;
+ int queue_depth = 0, device_type = 0;
+ unsigned int device_functions = 0;
+ int rc, i, borked;
ap_query_configuration();
- if (ap_select_domain() != 0) {
- return;
- }
+ if (ap_select_domain() != 0)
+ goto out;
+
for (i = 0; i < AP_DEVICES; i++) {
qid = AP_MKQID(i, ap_domain_index);
dev = bus_find_device(&ap_bus_type, NULL,
(void *)(unsigned long)qid,
__ap_scan_bus);
- if (ap_test_config_card_id(i))
- rc = ap_query_queue(qid, &queue_depth, &device_type);
- else
- rc = -ENODEV;
+ rc = ap_query_queue(qid, &queue_depth, &device_type,
+ &device_functions);
if (dev) {
- if (rc == -EBUSY) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(AP_RESET_TIMEOUT);
- rc = ap_query_queue(qid, &queue_depth,
- &device_type);
- }
ap_dev = to_ap_dev(dev);
spin_lock_bh(&ap_dev->lock);
- if (rc || ap_dev->unregistered) {
- spin_unlock_bh(&ap_dev->lock);
- if (ap_dev->unregistered)
- i--;
- device_unregister(dev);
- put_device(dev);
- continue;
- }
+ 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 */
+ device_unregister(dev);
put_device(dev);
- continue;
+ if (!borked)
+ continue;
}
if (rc)
continue;
- rc = ap_init_queue(qid);
- if (rc)
- continue;
ap_dev = kzalloc(sizeof(*ap_dev), GFP_KERNEL);
if (!ap_dev)
break;
ap_dev->qid = qid;
+ ap_dev->state = AP_STATE_RESET_START;
+ ap_dev->interrupt = AP_INTR_DISABLED;
ap_dev->queue_depth = queue_depth;
- ap_dev->unregistered = 1;
+ 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);
- switch (device_type) {
- case 0:
- /* device type probing for old cards */
- if (ap_probe_device_type(ap_dev)) {
- kfree(ap_dev);
- continue;
- }
- break;
- default:
- ap_dev->device_type = device_type;
- }
- ap_dev->raw_hwtype = device_type;
-
- rc = ap_query_functions(qid, &device_functions);
- if (!rc)
- ap_dev->functions = device_functions;
- else
- ap_dev->functions = 0u;
ap_dev->device.bus = &ap_bus_type;
ap_dev->device.parent = ap_root_device;
- if (dev_set_name(&ap_dev->device, "card%02x",
- AP_QID_DEVICE(ap_dev->qid))) {
+ 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);
continue;
}
/* Add device attributes. */
rc = sysfs_create_group(&ap_dev->device.kobj,
&ap_dev_attr_group);
- if (!rc) {
- spin_lock_bh(&ap_dev->lock);
- ap_dev->unregistered = 0;
- spin_unlock_bh(&ap_dev->lock);
- }
- else
+ if (rc) {
device_unregister(&ap_dev->device);
- }
-}
-
-static void
-ap_config_timeout(unsigned long ptr)
-{
- queue_work(ap_work_queue, &ap_config_work);
- ap_config_timer.expires = jiffies + ap_config_time * HZ;
- add_timer(&ap_config_timer);
-}
-
-/**
- * __ap_schedule_poll_timer(): Schedule poll timer.
- *
- * Set up the timer to run the poll tasklet
- */
-static inline void __ap_schedule_poll_timer(void)
-{
- ktime_t hr_time;
-
- spin_lock_bh(&ap_poll_timer_lock);
- if (!hrtimer_is_queued(&ap_poll_timer) && !ap_suspend_flag) {
- hr_time = ktime_set(0, poll_timeout);
- hrtimer_forward_now(&ap_poll_timer, hr_time);
- hrtimer_restart(&ap_poll_timer);
- }
- spin_unlock_bh(&ap_poll_timer_lock);
-}
-
-/**
- * ap_schedule_poll_timer(): Schedule poll timer.
- *
- * Set up the timer to run the poll tasklet
- */
-static inline void ap_schedule_poll_timer(void)
-{
- if (ap_using_interrupts())
- return;
- __ap_schedule_poll_timer();
-}
-
-/**
- * ap_poll_read(): Receive pending reply messages from an AP device.
- * @ap_dev: pointer to the AP device
- * @flags: pointer to control flags, bit 2^0 is set if another poll is
- * required, bit 2^1 is set if the poll timer needs to get armed
- *
- * Returns 0 if the device is still present, -ENODEV if not.
- */
-static int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags)
-{
- struct ap_queue_status status;
- struct ap_message *ap_msg;
-
- if (ap_dev->queue_count <= 0)
- return 0;
- status = __ap_recv(ap_dev->qid, &ap_dev->reply->psmid,
- ap_dev->reply->message, ap_dev->reply->length);
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- atomic_dec(&ap_poll_requests);
- ap_decrease_queue_count(ap_dev);
- 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;
- }
- if (ap_dev->queue_count > 0)
- *flags |= 1;
- break;
- case AP_RESPONSE_NO_PENDING_REPLY:
- if (status.queue_empty) {
- /* The card shouldn't forget requests but who knows. */
- 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;
- } else
- *flags |= 2;
- break;
- default:
- return -ENODEV;
- }
- return 0;
-}
-
-/**
- * ap_poll_write(): Send messages from the request queue to an AP device.
- * @ap_dev: pointer to the AP device
- * @flags: pointer to control flags, bit 2^0 is set if another poll is
- * required, bit 2^1 is set if the poll timer needs to get armed
- *
- * Returns 0 if the device is still present, -ENODEV if not.
- */
-static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
-{
- struct ap_queue_status status;
- struct ap_message *ap_msg;
-
- if (ap_dev->requestq_count <= 0 ||
- ap_dev->queue_count >= ap_dev->queue_depth)
- return 0;
- /* Start the next request on the queue. */
- ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list);
- status = __ap_send(ap_dev->qid, ap_msg->psmid,
- ap_msg->message, ap_msg->length, ap_msg->special);
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- atomic_inc(&ap_poll_requests);
- ap_increase_queue_count(ap_dev);
- list_move_tail(&ap_msg->list, &ap_dev->pendingq);
- ap_dev->requestq_count--;
- ap_dev->pendingq_count++;
- if (ap_dev->queue_count < ap_dev->queue_depth &&
- ap_dev->requestq_count > 0)
- *flags |= 1;
- *flags |= 2;
- break;
- case AP_RESPONSE_RESET_IN_PROGRESS:
- __ap_schedule_poll_timer();
- case AP_RESPONSE_Q_FULL:
- *flags |= 2;
- break;
- case AP_RESPONSE_MESSAGE_TOO_BIG:
- case AP_RESPONSE_REQ_FAC_NOT_INST:
- return -EINVAL;
- default:
- return -ENODEV;
- }
- return 0;
-}
-
-/**
- * ap_poll_queue(): Poll AP device for pending replies and send new messages.
- * @ap_dev: pointer to the bus device
- * @flags: pointer to control flags, bit 2^0 is set if another poll is
- * required, bit 2^1 is set if the poll timer needs to get armed
- *
- * Poll AP device for pending replies and send new messages. If either
- * ap_poll_read or ap_poll_write returns -ENODEV unregister the device.
- * Returns 0.
- */
-static inline int ap_poll_queue(struct ap_device *ap_dev, unsigned long *flags)
-{
- int rc;
-
- rc = ap_poll_read(ap_dev, flags);
- if (rc)
- return rc;
- return ap_poll_write(ap_dev, flags);
-}
-
-/**
- * __ap_queue_message(): Queue a message to a device.
- * @ap_dev: pointer to the AP device
- * @ap_msg: the message to be queued
- *
- * Queue a message to a device. Returns 0 if successful.
- */
-static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
-{
- struct ap_queue_status status;
-
- if (list_empty(&ap_dev->requestq) &&
- ap_dev->queue_count < ap_dev->queue_depth) {
- status = __ap_send(ap_dev->qid, ap_msg->psmid,
- ap_msg->message, ap_msg->length,
- ap_msg->special);
- switch (status.response_code) {
- case AP_RESPONSE_NORMAL:
- list_add_tail(&ap_msg->list, &ap_dev->pendingq);
- atomic_inc(&ap_poll_requests);
- ap_dev->pendingq_count++;
- ap_increase_queue_count(ap_dev);
- ap_dev->total_request_count++;
- break;
- case AP_RESPONSE_Q_FULL:
- case AP_RESPONSE_RESET_IN_PROGRESS:
- list_add_tail(&ap_msg->list, &ap_dev->requestq);
- ap_dev->requestq_count++;
- ap_dev->total_request_count++;
- return -EBUSY;
- case AP_RESPONSE_REQ_FAC_NOT_INST:
- case AP_RESPONSE_MESSAGE_TOO_BIG:
- ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL));
- return -EINVAL;
- default: /* Device is gone. */
- ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
- return -ENODEV;
- }
- } else {
- list_add_tail(&ap_msg->list, &ap_dev->requestq);
- ap_dev->requestq_count++;
- ap_dev->total_request_count++;
- return -EBUSY;
- }
- ap_schedule_poll_timer();
- return 0;
-}
-
-void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
-{
- unsigned long flags;
- int rc;
-
- /* For asynchronous message handling a valid receive-callback
- * is required. */
- BUG_ON(!ap_msg->receive);
-
- spin_lock_bh(&ap_dev->lock);
- if (!ap_dev->unregistered) {
- /* Make room on the queue by polling for finished requests. */
- rc = ap_poll_queue(ap_dev, &flags);
- if (!rc)
- rc = __ap_queue_message(ap_dev, ap_msg);
- if (!rc)
- wake_up(&ap_poll_wait);
- if (rc == -ENODEV)
- ap_dev->unregistered = 1;
- } else {
- ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
- rc = -ENODEV;
- }
- spin_unlock_bh(&ap_dev->lock);
- if (rc == -ENODEV)
- device_unregister(&ap_dev->device);
-}
-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_poll_timeout(): AP receive polling for finished AP requests.
- * @unused: Unused pointer.
- *
- * Schedules the AP tasklet using a high resolution timer.
- */
-static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
-{
- tasklet_schedule(&ap_tasklet);
- return HRTIMER_NORESTART;
-}
-
-/**
- * ap_reset(): Reset a not responding AP device.
- * @ap_dev: Pointer to the AP device
- *
- * Reset a not responding AP device and move all requests from the
- * pending queue to the request queue.
- */
-static void ap_reset(struct ap_device *ap_dev)
-{
- int rc;
-
- ap_dev->reset = AP_RESET_IGNORE;
- 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;
- rc = ap_init_queue(ap_dev->qid);
- if (rc == -ENODEV)
- ap_dev->unregistered = 1;
- else
- __ap_schedule_poll_timer();
-}
-
-static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags)
-{
- if (!ap_dev->unregistered) {
- if (ap_poll_queue(ap_dev, flags))
- ap_dev->unregistered = 1;
- if (ap_dev->reset == AP_RESET_DO)
- ap_reset(ap_dev);
- }
- return 0;
-}
-
-/**
- * ap_poll_all(): Poll all AP devices.
- * @dummy: Unused variable
- *
- * Poll all AP devices on the bus in a round robin fashion. Continue
- * polling until bit 2^0 of the control flags is not set. If bit 2^1
- * of the control flags has been set arm the poll timer.
- */
-static void ap_poll_all(unsigned long dummy)
-{
- unsigned long flags;
- struct ap_device *ap_dev;
-
- /* Reset the indicator if interrupts are used. Thus new interrupts can
- * be received. Doing it in the beginning of the tasklet is therefor
- * important that no requests on any AP get lost.
- */
- if (ap_using_interrupts())
- xchg(ap_airq.lsi_ptr, 0);
- do {
- flags = 0;
- spin_lock(&ap_device_list_lock);
- list_for_each_entry(ap_dev, &ap_device_list, list) {
- spin_lock(&ap_dev->lock);
- __ap_poll_device(ap_dev, &flags);
- spin_unlock(&ap_dev->lock);
- }
- spin_unlock(&ap_device_list_lock);
- } while (flags & 1);
- if (flags & 2)
- ap_schedule_poll_timer();
-}
-
-/**
- * ap_poll_thread(): Thread that polls for finished requests.
- * @data: Unused pointer
- *
- * AP bus poll thread. The purpose of this thread is to poll for
- * finished requests in a loop if there is a "free" cpu - that is
- * a cpu that doesn't have anything better to do. The polling stops
- * as soon as there is another task or if all messages have been
- * delivered.
- */
-static int ap_poll_thread(void *data)
-{
- DECLARE_WAITQUEUE(wait, current);
- unsigned long flags;
- int requests;
- struct ap_device *ap_dev;
-
- set_user_nice(current, MAX_NICE);
- while (1) {
- if (ap_suspend_flag)
- return 0;
- if (need_resched()) {
- schedule();
continue;
}
- add_wait_queue(&ap_poll_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- if (kthread_should_stop())
- break;
- requests = atomic_read(&ap_poll_requests);
- if (requests <= 0)
- schedule();
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&ap_poll_wait, &wait);
-
- flags = 0;
- spin_lock_bh(&ap_device_list_lock);
- list_for_each_entry(ap_dev, &ap_device_list, list) {
- spin_lock(&ap_dev->lock);
- __ap_poll_device(ap_dev, &flags);
- spin_unlock(&ap_dev->lock);
- }
- spin_unlock_bh(&ap_device_list_lock);
}
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&ap_poll_wait, &wait);
- return 0;
-}
-
-static int ap_poll_thread_start(void)
-{
- int rc;
-
- if (ap_using_interrupts() || ap_suspend_flag)
- return 0;
- mutex_lock(&ap_poll_thread_mutex);
- if (!ap_poll_kthread) {
- ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
- rc = PTR_RET(ap_poll_kthread);
- if (rc)
- ap_poll_kthread = NULL;
- }
- else
- rc = 0;
- mutex_unlock(&ap_poll_thread_mutex);
- return rc;
-}
-
-static void ap_poll_thread_stop(void)
-{
- mutex_lock(&ap_poll_thread_mutex);
- if (ap_poll_kthread) {
- kthread_stop(ap_poll_kthread);
- ap_poll_kthread = NULL;
- }
- mutex_unlock(&ap_poll_thread_mutex);
+out:
+ mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
}
-/**
- * ap_request_timeout(): Handling of request timeouts
- * @data: Holds the AP device.
- *
- * Handles request timeouts.
- */
-static void ap_request_timeout(unsigned long data)
+static void ap_config_timeout(unsigned long ptr)
{
- struct ap_device *ap_dev = (struct ap_device *) data;
-
- if (ap_dev->reset == AP_RESET_ARMED) {
- ap_dev->reset = AP_RESET_DO;
-
- if (ap_using_interrupts())
- tasklet_schedule(&ap_tasklet);
- }
+ if (ap_suspend_flag)
+ return;
+ queue_work(system_long_wq, &ap_scan_work);
}
static void ap_reset_domain(void)
{
int i;
- if ((ap_domain_index != -1) && (ap_test_config_domain(ap_domain_index)))
- for (i = 0; i < AP_DEVICES; i++)
- ap_reset_queue(AP_MKQID(i, ap_domain_index));
+ 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));
}
static void ap_reset_all(void)
@@ -1981,11 +1740,24 @@ static struct reset_call ap_reset_call = {
*/
int __init ap_module_init(void)
{
+ int max_domain_id;
int rc, i;
- if (ap_domain_index < -1 || ap_domain_index >= AP_DOMAINS) {
- pr_warning("%d is not a valid cryptographic domain\n",
- ap_domain_index);
+ if (ap_instructions_available() != 0) {
+ pr_warn("The hardware system does not support AP instructions\n");
+ return -ENODEV;
+ }
+
+ /* Get AP configuration data if available */
+ ap_init_configuration();
+
+ if (ap_configuration)
+ max_domain_id = ap_max_domain_id ? : (AP_DOMAINS - 1);
+ else
+ max_domain_id = 15;
+ if (ap_domain_index < -1 || ap_domain_index > max_domain_id) {
+ pr_warn("%d is not a valid cryptographic domain\n",
+ ap_domain_index);
return -EINVAL;
}
/* In resume callback we need to know if the user had set the domain.
@@ -1994,11 +1766,6 @@ int __init ap_module_init(void)
if (ap_domain_index >= 0)
user_set_domain = 1;
- if (ap_instructions_available() != 0) {
- pr_warning("The hardware system does not support "
- "AP instructions\n");
- return -ENODEV;
- }
if (ap_interrupts_available()) {
rc = register_adapter_interrupt(&ap_airq);
ap_airq_flag = (rc == 0);
@@ -2022,24 +1789,11 @@ int __init ap_module_init(void)
if (rc)
goto out_bus;
- ap_work_queue = create_singlethread_workqueue("kapwork");
- if (!ap_work_queue) {
- rc = -ENOMEM;
- goto out_root;
- }
-
- ap_query_configuration();
- if (ap_select_domain() == 0)
- ap_scan_bus(NULL);
-
/* Setup the AP bus rescan timer. */
- init_timer(&ap_config_timer);
- ap_config_timer.function = ap_config_timeout;
- ap_config_timer.data = 0;
- ap_config_timer.expires = jiffies + ap_config_time * HZ;
- add_timer(&ap_config_timer);
+ setup_timer(&ap_config_timer, ap_config_timeout, 0);
- /* Setup the high resultion poll timer.
+ /*
+ * Setup the high resultion poll timer.
* If we are running under z/VM adjust polling to z/VM polling rate.
*/
if (MACHINE_IS_VM)
@@ -2055,13 +1809,19 @@ int __init ap_module_init(void)
goto out_work;
}
+ rc = register_pm_notifier(&ap_power_notifier);
+ if (rc)
+ goto out_pm;
+
+ queue_work(system_long_wq, &ap_scan_work);
+ initialised = true;
+
return 0;
+out_pm:
+ ap_poll_thread_stop();
out_work:
- del_timer_sync(&ap_config_timer);
hrtimer_cancel(&ap_poll_timer);
- destroy_workqueue(ap_work_queue);
-out_root:
root_device_unregister(ap_root_device);
out_bus:
while (i--)
@@ -2071,14 +1831,10 @@ out:
unregister_reset_call(&ap_reset_call);
if (ap_using_interrupts())
unregister_adapter_interrupt(&ap_airq);
+ kfree(ap_configuration);
return rc;
}
-static int __ap_match_all(struct device *dev, void *data)
-{
- return 1;
-}
-
/**
* ap_modules_exit(): The module termination code
*
@@ -2087,24 +1843,20 @@ static int __ap_match_all(struct device *dev, void *data)
void ap_module_exit(void)
{
int i;
- struct device *dev;
+ initialised = false;
ap_reset_domain();
ap_poll_thread_stop();
del_timer_sync(&ap_config_timer);
hrtimer_cancel(&ap_poll_timer);
- destroy_workqueue(ap_work_queue);
tasklet_kill(&ap_tasklet);
- while ((dev = bus_find_device(&ap_bus_type, NULL, NULL,
- __ap_match_all)))
- {
- device_unregister(dev);
- put_device(dev);
- }
+ bus_for_each_dev(&ap_bus_type, NULL, NULL, __ap_devices_unregister);
for (i = 0; ap_bus_attrs[i]; i++)
bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
+ unregister_pm_notifier(&ap_power_notifier);
root_device_unregister(ap_root_device);
bus_unregister(&ap_bus_type);
+ kfree(ap_configuration);
unregister_reset_call(&ap_reset_call);
if (ap_using_interrupts())
unregister_adapter_interrupt(&ap_airq);
diff --git a/kernel/drivers/s390/crypto/ap_bus.h b/kernel/drivers/s390/crypto/ap_bus.h
index 2737d261a..6adcbdf22 100644
--- a/kernel/drivers/s390/crypto/ap_bus.h
+++ b/kernel/drivers/s390/crypto/ap_bus.h
@@ -32,7 +32,6 @@
#define AP_DEVICES 64 /* Number of AP devices. */
#define AP_DOMAINS 256 /* Number of AP domains. */
-#define AP_MAX_RESET 90 /* Maximum number of resets. */
#define AP_RESET_TIMEOUT (HZ*0.7) /* Time in ticks for reset timeouts. */
#define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */
#define AP_POLL_TIME 1 /* Time in ticks between receive polls. */
@@ -73,21 +72,9 @@ struct ap_queue_status {
unsigned int pad2 : 16;
} __packed;
-#define AP_QUEUE_STATUS_INVALID \
- { 1, 1, 1, 0xF, 1, 0xFF, 0xFFFF }
-static inline
-int ap_queue_status_invalid_test(struct ap_queue_status *status)
-{
- struct ap_queue_status invalid = AP_QUEUE_STATUS_INVALID;
- return !(memcmp(status, &invalid, sizeof(struct ap_queue_status)));
-}
-
-#define AP_MAX_BITS 31
static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
{
- if (nr > AP_MAX_BITS)
- return 0;
return (*ptr & (0x80000000u >> nr)) != 0;
}
@@ -130,11 +117,45 @@ static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
#define AP_FUNC_APXA 6
/*
- * AP reset flag states
+ * AP interrupt states
*/
-#define AP_RESET_IGNORE 0 /* request timeout will be ignored */
-#define AP_RESET_ARMED 1 /* request timeout timer is active */
-#define AP_RESET_DO 2 /* AP reset required */
+#define AP_INTR_DISABLED 0 /* AP interrupt disabled */
+#define AP_INTR_ENABLED 1 /* AP interrupt enabled */
+
+/*
+ * AP device states
+ */
+enum ap_state {
+ AP_STATE_RESET_START,
+ AP_STATE_RESET_WAIT,
+ AP_STATE_SETIRQ_WAIT,
+ AP_STATE_IDLE,
+ AP_STATE_WORKING,
+ AP_STATE_QUEUE_FULL,
+ AP_STATE_SUSPEND_WAIT,
+ AP_STATE_BORKED,
+ NR_AP_STATES
+};
+
+/*
+ * AP device events
+ */
+enum ap_event {
+ AP_EVENT_POLL,
+ AP_EVENT_TIMEOUT,
+ NR_AP_EVENTS
+};
+
+/*
+ * AP wait behaviour
+ */
+enum ap_wait {
+ AP_WAIT_AGAIN, /* retry immediately */
+ AP_WAIT_TIMEOUT, /* wait for timeout */
+ AP_WAIT_INTERRUPT, /* wait for thin interrupt (if available) */
+ AP_WAIT_NONE, /* no wait */
+ NR_AP_WAIT
+};
struct ap_device;
struct ap_message;
@@ -153,21 +174,24 @@ 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. */
+ enum ap_state state; /* State of the AP device. */
+
ap_qid_t qid; /* AP queue id. */
int queue_depth; /* AP queue depth.*/
int device_type; /* AP device type. */
int raw_hwtype; /* AP raw hardware type. */
unsigned int functions; /* AP device function bitfield. */
- int unregistered; /* marks AP device as unregistered */
struct timer_list timeout; /* Timer for request timeouts. */
- int reset; /* Reset required after req. timeout. */
+ 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. */
@@ -188,6 +212,7 @@ struct ap_message {
unsigned long long psmid; /* Message id. */
void *message; /* Pointer to message buffer. */
size_t length; /* Message length. */
+ int rc; /* Return code for this message */
void *private; /* ap driver private pointer. */
unsigned int special:1; /* Used for special commands. */
@@ -220,6 +245,7 @@ static inline void ap_init_message(struct ap_message *ap_msg)
{
ap_msg->psmid = 0;
ap_msg->length = 0;
+ ap_msg->rc = 0;
ap_msg->special = 0;
ap_msg->receive = NULL;
}
diff --git a/kernel/drivers/s390/crypto/zcrypt_api.c b/kernel/drivers/s390/crypto/zcrypt_api.c
index 08f1830cb..9f8fa42c0 100644
--- a/kernel/drivers/s390/crypto/zcrypt_api.c
+++ b/kernel/drivers/s390/crypto/zcrypt_api.c
@@ -54,6 +54,10 @@ MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " \
"Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
+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;
@@ -313,11 +317,9 @@ EXPORT_SYMBOL(zcrypt_device_unregister);
void zcrypt_msgtype_register(struct zcrypt_ops *zops)
{
- if (zops->owner) {
- spin_lock_bh(&zcrypt_ops_list_lock);
- list_add_tail(&zops->list, &zcrypt_ops_list);
- spin_unlock_bh(&zcrypt_ops_list_lock);
- }
+ 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);
@@ -338,7 +340,7 @@ struct zcrypt_ops *__ops_lookup(unsigned char *name, int variant)
spin_lock_bh(&zcrypt_ops_list_lock);
list_for_each_entry(zops, &zcrypt_ops_list, list) {
if ((zops->variant == variant) &&
- (!strncmp(zops->owner->name, name, MODULE_NAME_LEN))) {
+ (!strncmp(zops->name, name, sizeof(zops->name)))) {
found = 1;
break;
}
@@ -468,8 +470,7 @@ static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt)
unsigned long long z1, z2, z3;
int rc, copied;
- if (crt->outputdatalength < crt->inputdatalength ||
- (crt->inputdatalength & 1))
+ if (crt->outputdatalength < crt->inputdatalength)
return -EINVAL;
/*
* As long as outputdatalength is big enough, we can set the
@@ -1202,16 +1203,8 @@ static void sprinthx(unsigned char *title, struct seq_file *m,
static void sprinthx4(unsigned char *title, struct seq_file *m,
unsigned int *array, unsigned int len)
{
- int r;
-
seq_printf(m, "\n%s\n", title);
- for (r = 0; r < len; r++) {
- if ((r % 8) == 0)
- seq_printf(m, " ");
- seq_printf(m, "%08X ", array[r]);
- if ((r % 8) == 7)
- seq_putc(m, '\n');
- }
+ seq_hex_dump(m, " ", DUMP_PREFIX_NONE, 32, 4, array, len, false);
seq_putc(m, '\n');
}
@@ -1373,6 +1366,7 @@ static int zcrypt_rng_data_read(struct hwrng *rng, u32 *data)
static struct hwrng zcrypt_rng_dev = {
.name = "zcrypt",
.data_read = zcrypt_rng_data_read,
+ .quality = 990,
};
static int zcrypt_rng_device_add(void)
@@ -1387,6 +1381,8 @@ static int zcrypt_rng_device_add(void)
goto out;
}
zcrypt_rng_buffer_index = 0;
+ if (!zcrypt_hwrng_seed)
+ zcrypt_rng_dev.quality = 0;
rc = hwrng_register(&zcrypt_rng_dev);
if (rc)
goto out_free;
diff --git a/kernel/drivers/s390/crypto/zcrypt_api.h b/kernel/drivers/s390/crypto/zcrypt_api.h
index 750876891..38618f05a 100644
--- a/kernel/drivers/s390/crypto/zcrypt_api.h
+++ b/kernel/drivers/s390/crypto/zcrypt_api.h
@@ -96,6 +96,7 @@ struct zcrypt_ops {
struct list_head list; /* zcrypt ops list. */
struct module *owner;
int variant;
+ char name[128];
};
struct zcrypt_device {
diff --git a/kernel/drivers/s390/crypto/zcrypt_cca_key.h b/kernel/drivers/s390/crypto/zcrypt_cca_key.h
index 1f42f103c..ca0cdbe46 100644
--- a/kernel/drivers/s390/crypto/zcrypt_cca_key.h
+++ b/kernel/drivers/s390/crypto/zcrypt_cca_key.h
@@ -291,7 +291,7 @@ static inline int zcrypt_type6_crt_key(struct ica_rsa_modexpo_crt *crt,
memset(key, 0, sizeof(*key));
- short_len = crt->inputdatalength / 2;
+ short_len = (crt->inputdatalength + 1) / 2;
long_len = short_len + 8;
pad_len = -(3*long_len + 2*short_len) & 7;
key_len = 3*long_len + 2*short_len + pad_len + crt->inputdatalength;
diff --git a/kernel/drivers/s390/crypto/zcrypt_cex4.c b/kernel/drivers/s390/crypto/zcrypt_cex4.c
index 71e698b85..bb3908818 100644
--- a/kernel/drivers/s390/crypto/zcrypt_cex4.c
+++ b/kernel/drivers/s390/crypto/zcrypt_cex4.c
@@ -39,7 +39,7 @@
* But the maximum time limit managed by the stomper code is set to 60sec.
* Hence we have to wait at least that time period.
*/
-#define CEX4_CLEANUP_TIME (61*HZ)
+#define CEX4_CLEANUP_TIME (900*HZ)
static struct ap_device_id zcrypt_cex4_ids[] = {
{ AP_DEVICE(AP_DEVICE_TYPE_CEX4) },
diff --git a/kernel/drivers/s390/crypto/zcrypt_msgtype50.c b/kernel/drivers/s390/crypto/zcrypt_msgtype50.c
index 334e282f2..74edf2934 100644
--- a/kernel/drivers/s390/crypto/zcrypt_msgtype50.c
+++ b/kernel/drivers/s390/crypto/zcrypt_msgtype50.c
@@ -248,7 +248,7 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev,
unsigned char *p, *q, *dp, *dq, *u, *inp;
mod_len = crt->inputdatalength;
- short_len = mod_len / 2;
+ short_len = (mod_len + 1) / 2;
/*
* CEX2A and CEX3A w/o FW update can handle requests up to
@@ -395,10 +395,8 @@ static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
+ if (!reply)
+ 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)
@@ -449,10 +447,12 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
init_completion(&work);
ap_queue_message(zdev->ap_dev, &ap_msg);
rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, mex->outputdata,
- mex->outputdatalength);
- else
+ if (rc == 0) {
+ rc = ap_msg.rc;
+ if (rc == 0)
+ rc = convert_response(zdev, &ap_msg, mex->outputdata,
+ mex->outputdatalength);
+ } else
/* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
out_free:
@@ -493,10 +493,12 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
init_completion(&work);
ap_queue_message(zdev->ap_dev, &ap_msg);
rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, crt->outputdata,
- crt->outputdatalength);
- else
+ if (rc == 0) {
+ rc = ap_msg.rc;
+ if (rc == 0)
+ rc = convert_response(zdev, &ap_msg, crt->outputdata,
+ crt->outputdatalength);
+ } else
/* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
out_free:
@@ -511,6 +513,7 @@ static struct zcrypt_ops zcrypt_msgtype50_ops = {
.rsa_modexpo = zcrypt_cex2a_modexpo,
.rsa_modexpo_crt = zcrypt_cex2a_modexpo_crt,
.owner = THIS_MODULE,
+ .name = MSGTYPE50_NAME,
.variant = MSGTYPE50_VARIANT_DEFAULT,
};
diff --git a/kernel/drivers/s390/crypto/zcrypt_msgtype6.c b/kernel/drivers/s390/crypto/zcrypt_msgtype6.c
index 46b324ce6..9a2dd472c 100644
--- a/kernel/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/kernel/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -829,10 +829,8 @@ static void zcrypt_msgtype6_receive(struct ap_device *ap_dev,
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
+ if (!reply)
+ goto out; /* ap_msg->rc indicates the error */
t86r = reply->message;
if (t86r->hdr.type == TYPE86_RSP_CODE &&
t86r->cprbx.cprb_ver_id == 0x02) {
@@ -880,10 +878,8 @@ static void zcrypt_msgtype6_receive_ep11(struct ap_device *ap_dev,
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
+ if (!reply)
+ goto out; /* ap_msg->rc indicates the error */
t86r = reply->message;
if (t86r->hdr.type == TYPE86_RSP_CODE &&
t86r->cprbx.cprb_ver_id == 0x04) {
@@ -935,10 +931,13 @@ static long zcrypt_msgtype6_modexpo(struct zcrypt_device *zdev,
init_completion(&resp_type.work);
ap_queue_message(zdev->ap_dev, &ap_msg);
rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_ica(zdev, &ap_msg, mex->outputdata,
- mex->outputdatalength);
- else
+ if (rc == 0) {
+ rc = ap_msg.rc;
+ if (rc == 0)
+ rc = convert_response_ica(zdev, &ap_msg,
+ mex->outputdata,
+ mex->outputdatalength);
+ } else
/* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
out_free:
@@ -976,10 +975,13 @@ static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_device *zdev,
init_completion(&resp_type.work);
ap_queue_message(zdev->ap_dev, &ap_msg);
rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_ica(zdev, &ap_msg, crt->outputdata,
- crt->outputdatalength);
- else
+ if (rc == 0) {
+ rc = ap_msg.rc;
+ if (rc == 0)
+ rc = convert_response_ica(zdev, &ap_msg,
+ crt->outputdata,
+ crt->outputdatalength);
+ } else
/* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
out_free:
@@ -1017,9 +1019,11 @@ static long zcrypt_msgtype6_send_cprb(struct zcrypt_device *zdev,
init_completion(&resp_type.work);
ap_queue_message(zdev->ap_dev, &ap_msg);
rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_xcrb(zdev, &ap_msg, xcRB);
- else
+ if (rc == 0) {
+ rc = ap_msg.rc;
+ if (rc == 0)
+ rc = convert_response_xcrb(zdev, &ap_msg, xcRB);
+ } else
/* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
out_free:
@@ -1057,9 +1061,12 @@ static long zcrypt_msgtype6_send_ep11_cprb(struct zcrypt_device *zdev,
init_completion(&resp_type.work);
ap_queue_message(zdev->ap_dev, &ap_msg);
rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_ep11_xcrb(zdev, &ap_msg, xcrb);
- else /* Signal pending. */
+ if (rc == 0) {
+ rc = ap_msg.rc;
+ if (rc == 0)
+ rc = convert_response_ep11_xcrb(zdev, &ap_msg, xcrb);
+ } else
+ /* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
out_free:
@@ -1096,9 +1103,11 @@ static long zcrypt_msgtype6_rng(struct zcrypt_device *zdev,
init_completion(&resp_type.work);
ap_queue_message(zdev->ap_dev, &ap_msg);
rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_rng(zdev, &ap_msg, buffer);
- else
+ if (rc == 0) {
+ rc = ap_msg.rc;
+ if (rc == 0)
+ rc = convert_response_rng(zdev, &ap_msg, buffer);
+ } else
/* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
kfree(ap_msg.message);
@@ -1110,6 +1119,7 @@ static long zcrypt_msgtype6_rng(struct zcrypt_device *zdev,
*/
static struct zcrypt_ops zcrypt_msgtype6_norng_ops = {
.owner = THIS_MODULE,
+ .name = MSGTYPE06_NAME,
.variant = MSGTYPE06_VARIANT_NORNG,
.rsa_modexpo = zcrypt_msgtype6_modexpo,
.rsa_modexpo_crt = zcrypt_msgtype6_modexpo_crt,
@@ -1118,6 +1128,7 @@ static struct zcrypt_ops zcrypt_msgtype6_norng_ops = {
static struct zcrypt_ops zcrypt_msgtype6_ops = {
.owner = THIS_MODULE,
+ .name = MSGTYPE06_NAME,
.variant = MSGTYPE06_VARIANT_DEFAULT,
.rsa_modexpo = zcrypt_msgtype6_modexpo,
.rsa_modexpo_crt = zcrypt_msgtype6_modexpo_crt,
@@ -1127,6 +1138,7 @@ static struct zcrypt_ops zcrypt_msgtype6_ops = {
static struct zcrypt_ops zcrypt_msgtype6_ep11_ops = {
.owner = THIS_MODULE,
+ .name = MSGTYPE06_NAME,
.variant = MSGTYPE06_VARIANT_EP11,
.rsa_modexpo = NULL,
.rsa_modexpo_crt = NULL,
diff --git a/kernel/drivers/s390/crypto/zcrypt_pcica.c b/kernel/drivers/s390/crypto/zcrypt_pcica.c
deleted file mode 100644
index 7a743f4c6..000000000
--- a/kernel/drivers/s390/crypto/zcrypt_pcica.c
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * zcrypt 2.1.0
- *
- * Copyright IBM Corp. 2001, 2006
- * Author(s): Robert Burroughs
- * Eric Rossman (edrossma@us.ibm.com)
- *
- * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
- * Ralph Wuerthner <rwuerthn@de.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define KMSG_COMPONENT "zcrypt"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/atomic.h>
-#include <asm/uaccess.h>
-
-#include "ap_bus.h"
-#include "zcrypt_api.h"
-#include "zcrypt_error.h"
-#include "zcrypt_pcica.h"
-
-#define PCICA_MIN_MOD_SIZE 1 /* 8 bits */
-#define PCICA_MAX_MOD_SIZE 256 /* 2048 bits */
-
-#define PCICA_SPEED_RATING 2800
-
-#define PCICA_MAX_MESSAGE_SIZE 0x3a0 /* sizeof(struct type4_lcr) */
-#define PCICA_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */
-
-#define PCICA_CLEANUP_TIME (15*HZ)
-
-static struct ap_device_id zcrypt_pcica_ids[] = {
- { AP_DEVICE(AP_DEVICE_TYPE_PCICA) },
- { /* end of list */ },
-};
-
-MODULE_DEVICE_TABLE(ap, zcrypt_pcica_ids);
-MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("PCICA Cryptographic Coprocessor device driver, "
- "Copyright IBM Corp. 2001, 2006");
-MODULE_LICENSE("GPL");
-
-static int zcrypt_pcica_probe(struct ap_device *ap_dev);
-static void zcrypt_pcica_remove(struct ap_device *ap_dev);
-static void zcrypt_pcica_receive(struct ap_device *, struct ap_message *,
- struct ap_message *);
-
-static struct ap_driver zcrypt_pcica_driver = {
- .probe = zcrypt_pcica_probe,
- .remove = zcrypt_pcica_remove,
- .ids = zcrypt_pcica_ids,
- .request_timeout = PCICA_CLEANUP_TIME,
-};
-
-/**
- * Convert a ICAMEX message to a type4 MEX message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @mex: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICAMEX_msg_to_type4MEX_msg(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo *mex)
-{
- unsigned char *modulus, *exponent, *message;
- int mod_len;
-
- mod_len = mex->inputdatalength;
-
- if (mod_len <= 128) {
- struct type4_sme *sme = ap_msg->message;
- memset(sme, 0, sizeof(*sme));
- ap_msg->length = sizeof(*sme);
- sme->header.msg_fmt = TYPE4_SME_FMT;
- sme->header.msg_len = sizeof(*sme);
- sme->header.msg_type_code = TYPE4_TYPE_CODE;
- sme->header.request_code = TYPE4_REQU_CODE;
- modulus = sme->modulus + sizeof(sme->modulus) - mod_len;
- exponent = sme->exponent + sizeof(sme->exponent) - mod_len;
- message = sme->message + sizeof(sme->message) - mod_len;
- } else {
- struct type4_lme *lme = ap_msg->message;
- memset(lme, 0, sizeof(*lme));
- ap_msg->length = sizeof(*lme);
- lme->header.msg_fmt = TYPE4_LME_FMT;
- lme->header.msg_len = sizeof(*lme);
- lme->header.msg_type_code = TYPE4_TYPE_CODE;
- lme->header.request_code = TYPE4_REQU_CODE;
- modulus = lme->modulus + sizeof(lme->modulus) - mod_len;
- exponent = lme->exponent + sizeof(lme->exponent) - mod_len;
- message = lme->message + sizeof(lme->message) - mod_len;
- }
-
- if (copy_from_user(modulus, mex->n_modulus, mod_len) ||
- copy_from_user(exponent, mex->b_key, mod_len) ||
- copy_from_user(message, mex->inputdata, mod_len))
- return -EFAULT;
- return 0;
-}
-
-/**
- * Convert a ICACRT message to a type4 CRT message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @crt: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICACRT_msg_to_type4CRT_msg(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo_crt *crt)
-{
- unsigned char *p, *q, *dp, *dq, *u, *inp;
- int mod_len, short_len, long_len;
-
- mod_len = crt->inputdatalength;
- short_len = mod_len / 2;
- long_len = mod_len / 2 + 8;
-
- if (mod_len <= 128) {
- struct type4_scr *scr = ap_msg->message;
- memset(scr, 0, sizeof(*scr));
- ap_msg->length = sizeof(*scr);
- scr->header.msg_type_code = TYPE4_TYPE_CODE;
- scr->header.request_code = TYPE4_REQU_CODE;
- scr->header.msg_fmt = TYPE4_SCR_FMT;
- scr->header.msg_len = sizeof(*scr);
- p = scr->p + sizeof(scr->p) - long_len;
- q = scr->q + sizeof(scr->q) - short_len;
- dp = scr->dp + sizeof(scr->dp) - long_len;
- dq = scr->dq + sizeof(scr->dq) - short_len;
- u = scr->u + sizeof(scr->u) - long_len;
- inp = scr->message + sizeof(scr->message) - mod_len;
- } else {
- struct type4_lcr *lcr = ap_msg->message;
- memset(lcr, 0, sizeof(*lcr));
- ap_msg->length = sizeof(*lcr);
- lcr->header.msg_type_code = TYPE4_TYPE_CODE;
- lcr->header.request_code = TYPE4_REQU_CODE;
- lcr->header.msg_fmt = TYPE4_LCR_FMT;
- lcr->header.msg_len = sizeof(*lcr);
- p = lcr->p + sizeof(lcr->p) - long_len;
- q = lcr->q + sizeof(lcr->q) - short_len;
- dp = lcr->dp + sizeof(lcr->dp) - long_len;
- dq = lcr->dq + sizeof(lcr->dq) - short_len;
- u = lcr->u + sizeof(lcr->u) - long_len;
- inp = lcr->message + sizeof(lcr->message) - mod_len;
- }
-
- if (copy_from_user(p, crt->np_prime, long_len) ||
- copy_from_user(q, crt->nq_prime, short_len) ||
- copy_from_user(dp, crt->bp_key, long_len) ||
- copy_from_user(dq, crt->bq_key, short_len) ||
- copy_from_user(u, crt->u_mult_inv, long_len) ||
- copy_from_user(inp, crt->inputdata, mod_len))
- return -EFAULT;
- return 0;
-}
-
-/**
- * Copy results from a type 84 reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @data: pointer to user output data
- * @length: size of user output data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int convert_type84(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- struct type84_hdr *t84h = reply->message;
- char *data;
-
- if (t84h->len < sizeof(*t84h) + outputdatalength) {
- /* The result is too short, the PCICA card may not do that.. */
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- zdev->ap_dev->qid);
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
- zdev->ap_dev->qid, zdev->online, t84h->code);
- return -EAGAIN; /* repeat the request on a different device. */
- }
- BUG_ON(t84h->len > PCICA_MAX_RESPONSE_SIZE);
- data = reply->message + t84h->len - outputdatalength;
- if (copy_to_user(outputdata, data, outputdatalength))
- return -EFAULT;
- return 0;
-}
-
-static int convert_response(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- /* Response type byte is the second byte in the response. */
- switch (((unsigned char *) reply->message)[1]) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- return convert_error(zdev, reply);
- case TYPE84_RSP_CODE:
- return convert_type84(zdev, reply,
- outputdata, outputdatalength);
- default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- zdev->ap_dev->qid);
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%dfail",
- zdev->ap_dev->qid, zdev->online);
- return -EAGAIN; /* repeat the request on a different device. */
- }
-}
-
-/**
- * This function is called from the AP bus code after a crypto request
- * "msg" has finished with the reply message "reply".
- * It is called from tasklet context.
- * @ap_dev: pointer to the AP device
- * @msg: pointer to the AP message
- * @reply: pointer to the AP reply message
- */
-static void zcrypt_pcica_receive(struct ap_device *ap_dev,
- struct ap_message *msg,
- struct ap_message *reply)
-{
- static struct error_hdr error_reply = {
- .type = TYPE82_RSP_CODE,
- .reply_code = REP82_ERROR_MACHINE_FAILURE,
- };
- struct type84_hdr *t84h;
- int length;
-
- /* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
- t84h = reply->message;
- if (t84h->code == TYPE84_RSP_CODE) {
- length = min(PCICA_MAX_RESPONSE_SIZE, (int) t84h->len);
- memcpy(msg->message, reply->message, length);
- } else
- memcpy(msg->message, reply->message, sizeof error_reply);
-out:
- complete((struct completion *) msg->private);
-}
-
-static atomic_t zcrypt_step = ATOMIC_INIT(0);
-
-/**
- * The request distributor calls this function if it picked the PCICA
- * device to handle a modexpo request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCICA device to the request distributor
- * @mex: pointer to the modexpo request buffer
- */
-static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo *mex)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcica_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICAMEX_msg_to_type4MEX_msg(zdev, &ap_msg, mex);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, mex->outputdata,
- mex->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- kfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCICA
- * device to handle a modexpo_crt request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCICA device to the request distributor
- * @crt: pointer to the modexpoc_crt request buffer
- */
-static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo_crt *crt)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcica_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICACRT_msg_to_type4CRT_msg(zdev, &ap_msg, crt);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, crt->outputdata,
- crt->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- kfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The crypto operations for a PCICA card.
- */
-static struct zcrypt_ops zcrypt_pcica_ops = {
- .rsa_modexpo = zcrypt_pcica_modexpo,
- .rsa_modexpo_crt = zcrypt_pcica_modexpo_crt,
-};
-
-/**
- * Probe function for PCICA cards. It always accepts the AP device
- * since the bus_match already checked the hardware type.
- * @ap_dev: pointer to the AP device.
- */
-static int zcrypt_pcica_probe(struct ap_device *ap_dev)
-{
- struct zcrypt_device *zdev;
- int rc;
-
- zdev = zcrypt_device_alloc(PCICA_MAX_RESPONSE_SIZE);
- if (!zdev)
- return -ENOMEM;
- zdev->ap_dev = ap_dev;
- zdev->ops = &zcrypt_pcica_ops;
- zdev->online = 1;
- zdev->user_space_type = ZCRYPT_PCICA;
- zdev->type_string = "PCICA";
- zdev->min_mod_size = PCICA_MIN_MOD_SIZE;
- zdev->max_mod_size = PCICA_MAX_MOD_SIZE;
- zdev->speed_rating = PCICA_SPEED_RATING;
- zdev->max_exp_bit_length = PCICA_MAX_MOD_SIZE;
- ap_dev->reply = &zdev->reply;
- ap_dev->private = zdev;
- rc = zcrypt_device_register(zdev);
- if (rc)
- goto out_free;
- return 0;
-
-out_free:
- ap_dev->private = NULL;
- zcrypt_device_free(zdev);
- return rc;
-}
-
-/**
- * This is called to remove the extended PCICA driver information
- * if an AP device is removed.
- */
-static void zcrypt_pcica_remove(struct ap_device *ap_dev)
-{
- struct zcrypt_device *zdev = ap_dev->private;
-
- zcrypt_device_unregister(zdev);
-}
-
-int __init zcrypt_pcica_init(void)
-{
- return ap_driver_register(&zcrypt_pcica_driver, THIS_MODULE, "pcica");
-}
-
-void zcrypt_pcica_exit(void)
-{
- ap_driver_unregister(&zcrypt_pcica_driver);
-}
-
-module_init(zcrypt_pcica_init);
-module_exit(zcrypt_pcica_exit);
diff --git a/kernel/drivers/s390/crypto/zcrypt_pcica.h b/kernel/drivers/s390/crypto/zcrypt_pcica.h
deleted file mode 100644
index 9a59155ca..000000000
--- a/kernel/drivers/s390/crypto/zcrypt_pcica.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * zcrypt 2.1.0
- *
- * Copyright IBM Corp. 2001, 2006
- * Author(s): Robert Burroughs
- * Eric Rossman (edrossma@us.ibm.com)
- *
- * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _ZCRYPT_PCICA_H_
-#define _ZCRYPT_PCICA_H_
-
-/**
- * The type 4 message family is associated with a PCICA card.
- *
- * The four members of the family are described below.
- *
- * Note that all unsigned char arrays are right-justified and left-padded
- * with zeroes.
- *
- * Note that all reserved fields must be zeroes.
- */
-struct type4_hdr {
- unsigned char reserved1;
- unsigned char msg_type_code; /* 0x04 */
- unsigned short msg_len;
- unsigned char request_code; /* 0x40 */
- unsigned char msg_fmt;
- unsigned short reserved2;
-} __attribute__((packed));
-
-#define TYPE4_TYPE_CODE 0x04
-#define TYPE4_REQU_CODE 0x40
-
-#define TYPE4_SME_FMT 0x00
-#define TYPE4_LME_FMT 0x10
-#define TYPE4_SCR_FMT 0x40
-#define TYPE4_LCR_FMT 0x50
-
-/* Mod-Exp, with a small modulus */
-struct type4_sme {
- struct type4_hdr header;
- unsigned char message[128];
- unsigned char exponent[128];
- unsigned char modulus[128];
-} __attribute__((packed));
-
-/* Mod-Exp, with a large modulus */
-struct type4_lme {
- struct type4_hdr header;
- unsigned char message[256];
- unsigned char exponent[256];
- unsigned char modulus[256];
-} __attribute__((packed));
-
-/* CRT, with a small modulus */
-struct type4_scr {
- struct type4_hdr header;
- unsigned char message[128];
- unsigned char dp[72];
- unsigned char dq[64];
- unsigned char p[72];
- unsigned char q[64];
- unsigned char u[72];
-} __attribute__((packed));
-
-/* CRT, with a large modulus */
-struct type4_lcr {
- struct type4_hdr header;
- unsigned char message[256];
- unsigned char dp[136];
- unsigned char dq[128];
- unsigned char p[136];
- unsigned char q[128];
- unsigned char u[136];
-} __attribute__((packed));
-
-/**
- * The type 84 response family is associated with a PCICA card.
- *
- * Note that all unsigned char arrays are right-justified and left-padded
- * with zeroes.
- *
- * Note that all reserved fields must be zeroes.
- */
-
-struct type84_hdr {
- unsigned char reserved1;
- unsigned char code;
- unsigned short len;
- unsigned char reserved2[4];
-} __attribute__((packed));
-
-#define TYPE84_RSP_CODE 0x84
-
-int zcrypt_pcica_init(void);
-void zcrypt_pcica_exit(void);
-
-#endif /* _ZCRYPT_PCICA_H_ */
diff --git a/kernel/drivers/s390/crypto/zcrypt_pcicc.c b/kernel/drivers/s390/crypto/zcrypt_pcicc.c
deleted file mode 100644
index 4d14c04b7..000000000
--- a/kernel/drivers/s390/crypto/zcrypt_pcicc.c
+++ /dev/null
@@ -1,627 +0,0 @@
-/*
- * zcrypt 2.1.0
- *
- * Copyright IBM Corp. 2001, 2006
- * Author(s): Robert Burroughs
- * Eric Rossman (edrossma@us.ibm.com)
- *
- * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
- * Ralph Wuerthner <rwuerthn@de.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define KMSG_COMPONENT "zcrypt"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/gfp.h>
-#include <linux/err.h>
-#include <linux/atomic.h>
-#include <asm/uaccess.h>
-
-#include "ap_bus.h"
-#include "zcrypt_api.h"
-#include "zcrypt_error.h"
-#include "zcrypt_pcicc.h"
-#include "zcrypt_cca_key.h"
-
-#define PCICC_MIN_MOD_SIZE 64 /* 512 bits */
-#define PCICC_MAX_MOD_SIZE_OLD 128 /* 1024 bits */
-#define PCICC_MAX_MOD_SIZE 256 /* 2048 bits */
-
-/*
- * PCICC cards need a speed rating of 0. This keeps them at the end of
- * the zcrypt device list (see zcrypt_api.c). PCICC cards are only
- * used if no other cards are present because they are slow and can only
- * cope with PKCS12 padded requests. The logic is queer. PKCS11 padded
- * requests are rejected. The modexpo function encrypts PKCS12 padded data
- * and decrypts any non-PKCS12 padded data (except PKCS11) in the assumption
- * that it's encrypted PKCS12 data. The modexpo_crt function always decrypts
- * the data in the assumption that its PKCS12 encrypted data.
- */
-#define PCICC_SPEED_RATING 0
-
-#define PCICC_MAX_MESSAGE_SIZE 0x710 /* max size type6 v1 crt message */
-#define PCICC_MAX_RESPONSE_SIZE 0x710 /* max size type86 v1 reply */
-
-#define PCICC_CLEANUP_TIME (15*HZ)
-
-static struct ap_device_id zcrypt_pcicc_ids[] = {
- { AP_DEVICE(AP_DEVICE_TYPE_PCICC) },
- { /* end of list */ },
-};
-
-MODULE_DEVICE_TABLE(ap, zcrypt_pcicc_ids);
-MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("PCICC Cryptographic Coprocessor device driver, "
- "Copyright IBM Corp. 2001, 2006");
-MODULE_LICENSE("GPL");
-
-static int zcrypt_pcicc_probe(struct ap_device *ap_dev);
-static void zcrypt_pcicc_remove(struct ap_device *ap_dev);
-static void zcrypt_pcicc_receive(struct ap_device *, struct ap_message *,
- struct ap_message *);
-
-static struct ap_driver zcrypt_pcicc_driver = {
- .probe = zcrypt_pcicc_probe,
- .remove = zcrypt_pcicc_remove,
- .ids = zcrypt_pcicc_ids,
- .request_timeout = PCICC_CLEANUP_TIME,
-};
-
-/**
- * The following is used to initialize the CPRB passed to the PCICC card
- * in a type6 message. The 3 fields that must be filled in at execution
- * time are req_parml, rpl_parml and usage_domain. Note that all three
- * fields are *little*-endian. Actually, everything about this interface
- * is ascii/little-endian, since the device has 'Intel inside'.
- *
- * The CPRB is followed immediately by the parm block.
- * The parm block contains:
- * - function code ('PD' 0x5044 or 'PK' 0x504B)
- * - rule block (0x0A00 'PKCS-1.2' or 0x0A00 'ZERO-PAD')
- * - VUD block
- */
-static struct CPRB static_cprb = {
- .cprb_len = __constant_cpu_to_le16(0x0070),
- .cprb_ver_id = 0x41,
- .func_id = {0x54,0x32},
- .checkpoint_flag= 0x01,
- .svr_namel = __constant_cpu_to_le16(0x0008),
- .svr_name = {'I','C','S','F',' ',' ',' ',' '}
-};
-
-/**
- * Check the message for PKCS11 padding.
- */
-static inline int is_PKCS11_padded(unsigned char *buffer, int length)
-{
- int i;
- if ((buffer[0] != 0x00) || (buffer[1] != 0x01))
- return 0;
- for (i = 2; i < length; i++)
- if (buffer[i] != 0xFF)
- break;
- if (i < 10 || i == length)
- return 0;
- if (buffer[i] != 0x00)
- return 0;
- return 1;
-}
-
-/**
- * Check the message for PKCS12 padding.
- */
-static inline int is_PKCS12_padded(unsigned char *buffer, int length)
-{
- int i;
- if ((buffer[0] != 0x00) || (buffer[1] != 0x02))
- return 0;
- for (i = 2; i < length; i++)
- if (buffer[i] == 0x00)
- break;
- if ((i < 10) || (i == length))
- return 0;
- if (buffer[i] != 0x00)
- return 0;
- return 1;
-}
-
-/**
- * Convert a ICAMEX message to a type6 MEX message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @mex: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICAMEX_msg_to_type6MEX_msg(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo *mex)
-{
- static struct type6_hdr static_type6_hdr = {
- .type = 0x06,
- .offset1 = 0x00000058,
- .agent_id = {0x01,0x00,0x43,0x43,0x41,0x2D,0x41,0x50,
- 0x50,0x4C,0x20,0x20,0x20,0x01,0x01,0x01},
- .function_code = {'P','K'},
- };
- static struct function_and_rules_block static_pke_function_and_rules ={
- .function_code = {'P','K'},
- .ulen = __constant_cpu_to_le16(10),
- .only_rule = {'P','K','C','S','-','1','.','2'}
- };
- struct {
- struct type6_hdr hdr;
- struct CPRB cprb;
- struct function_and_rules_block fr;
- unsigned short length;
- char text[0];
- } __attribute__((packed)) *msg = ap_msg->message;
- int vud_len, pad_len, size;
-
- /* VUD.ciphertext */
- if (copy_from_user(msg->text, mex->inputdata, mex->inputdatalength))
- return -EFAULT;
-
- if (is_PKCS11_padded(msg->text, mex->inputdatalength))
- return -EINVAL;
-
- /* static message header and f&r */
- msg->hdr = static_type6_hdr;
- msg->fr = static_pke_function_and_rules;
-
- if (is_PKCS12_padded(msg->text, mex->inputdatalength)) {
- /* strip the padding and adjust the data length */
- pad_len = strnlen(msg->text + 2, mex->inputdatalength - 2) + 3;
- if (pad_len <= 9 || pad_len >= mex->inputdatalength)
- return -ENODEV;
- vud_len = mex->inputdatalength - pad_len;
- memmove(msg->text, msg->text + pad_len, vud_len);
- msg->length = cpu_to_le16(vud_len + 2);
-
- /* Set up key after the variable length text. */
- size = zcrypt_type6_mex_key_en(mex, msg->text + vud_len, 0);
- if (size < 0)
- return size;
- size += sizeof(*msg) + vud_len; /* total size of msg */
- } else {
- vud_len = mex->inputdatalength;
- msg->length = cpu_to_le16(2 + vud_len);
-
- msg->hdr.function_code[1] = 'D';
- msg->fr.function_code[1] = 'D';
-
- /* Set up key after the variable length text. */
- size = zcrypt_type6_mex_key_de(mex, msg->text + vud_len, 0);
- if (size < 0)
- return size;
- size += sizeof(*msg) + vud_len; /* total size of msg */
- }
-
- /* message header, cprb and f&r */
- msg->hdr.ToCardLen1 = (size - sizeof(msg->hdr) + 3) & -4;
- msg->hdr.FromCardLen1 = PCICC_MAX_RESPONSE_SIZE - sizeof(msg->hdr);
-
- msg->cprb = static_cprb;
- msg->cprb.usage_domain[0]= AP_QID_QUEUE(zdev->ap_dev->qid);
- msg->cprb.req_parml = cpu_to_le16(size - sizeof(msg->hdr) -
- sizeof(msg->cprb));
- msg->cprb.rpl_parml = cpu_to_le16(msg->hdr.FromCardLen1);
-
- ap_msg->length = (size + 3) & -4;
- return 0;
-}
-
-/**
- * Convert a ICACRT message to a type6 CRT message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @crt: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICACRT_msg_to_type6CRT_msg(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo_crt *crt)
-{
- static struct type6_hdr static_type6_hdr = {
- .type = 0x06,
- .offset1 = 0x00000058,
- .agent_id = {0x01,0x00,0x43,0x43,0x41,0x2D,0x41,0x50,
- 0x50,0x4C,0x20,0x20,0x20,0x01,0x01,0x01},
- .function_code = {'P','D'},
- };
- static struct function_and_rules_block static_pkd_function_and_rules ={
- .function_code = {'P','D'},
- .ulen = __constant_cpu_to_le16(10),
- .only_rule = {'P','K','C','S','-','1','.','2'}
- };
- struct {
- struct type6_hdr hdr;
- struct CPRB cprb;
- struct function_and_rules_block fr;
- unsigned short length;
- char text[0];
- } __attribute__((packed)) *msg = ap_msg->message;
- int size;
-
- /* VUD.ciphertext */
- msg->length = cpu_to_le16(2 + crt->inputdatalength);
- if (copy_from_user(msg->text, crt->inputdata, crt->inputdatalength))
- return -EFAULT;
-
- if (is_PKCS11_padded(msg->text, crt->inputdatalength))
- return -EINVAL;
-
- /* Set up key after the variable length text. */
- size = zcrypt_type6_crt_key(crt, msg->text + crt->inputdatalength, 0);
- if (size < 0)
- return size;
- size += sizeof(*msg) + crt->inputdatalength; /* total size of msg */
-
- /* message header, cprb and f&r */
- msg->hdr = static_type6_hdr;
- msg->hdr.ToCardLen1 = (size - sizeof(msg->hdr) + 3) & -4;
- msg->hdr.FromCardLen1 = PCICC_MAX_RESPONSE_SIZE - sizeof(msg->hdr);
-
- msg->cprb = static_cprb;
- msg->cprb.usage_domain[0] = AP_QID_QUEUE(zdev->ap_dev->qid);
- msg->cprb.req_parml = msg->cprb.rpl_parml =
- cpu_to_le16(size - sizeof(msg->hdr) - sizeof(msg->cprb));
-
- msg->fr = static_pkd_function_and_rules;
-
- ap_msg->length = (size + 3) & -4;
- return 0;
-}
-
-/**
- * Copy results from a type 86 reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @data: pointer to user output data
- * @length: size of user output data
- *
- * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
- */
-struct type86_reply {
- struct type86_hdr hdr;
- struct type86_fmt2_ext fmt2;
- struct CPRB cprb;
- unsigned char pad[4]; /* 4 byte function code/rules block ? */
- unsigned short length;
- char text[0];
-} __attribute__((packed));
-
-static int convert_type86(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- static unsigned char static_pad[] = {
- 0x00,0x02,
- 0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD,
- 0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57,
- 0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B,
- 0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39,
- 0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5,
- 0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D,
- 0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB,
- 0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F,
- 0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9,
- 0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45,
- 0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9,
- 0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F,
- 0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD,
- 0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D,
- 0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD,
- 0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9,
- 0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B,
- 0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B,
- 0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B,
- 0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD,
- 0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7,
- 0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1,
- 0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3,
- 0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23,
- 0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55,
- 0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43,
- 0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F,
- 0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F,
- 0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5,
- 0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD,
- 0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41,
- 0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09
- };
- struct type86_reply *msg = reply->message;
- unsigned short service_rc, service_rs;
- unsigned int reply_len, pad_len;
- char *data;
-
- service_rc = le16_to_cpu(msg->cprb.ccp_rtcode);
- if (unlikely(service_rc != 0)) {
- service_rs = le16_to_cpu(msg->cprb.ccp_rscode);
- if (service_rc == 8 && service_rs == 66)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 65)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 770) {
- zdev->max_mod_size = PCICC_MAX_MOD_SIZE_OLD;
- return -EAGAIN;
- }
- if (service_rc == 8 && service_rs == 783) {
- zdev->max_mod_size = PCICC_MAX_MOD_SIZE_OLD;
- return -EAGAIN;
- }
- if (service_rc == 8 && service_rs == 72)
- return -EINVAL;
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- zdev->ap_dev->qid);
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
- zdev->ap_dev->qid, zdev->online,
- msg->hdr.reply_code);
- return -EAGAIN; /* repeat the request on a different device. */
- }
- data = msg->text;
- reply_len = le16_to_cpu(msg->length) - 2;
- if (reply_len > outputdatalength)
- return -EINVAL;
- /*
- * For all encipher requests, the length of the ciphertext (reply_len)
- * will always equal the modulus length. For MEX decipher requests
- * the output needs to get padded. Minimum pad size is 10.
- *
- * Currently, the cases where padding will be added is for:
- * - PCIXCC_MCL2 using a CRT form token (since PKD didn't support
- * ZERO-PAD and CRT is only supported for PKD requests)
- * - PCICC, always
- */
- pad_len = outputdatalength - reply_len;
- if (pad_len > 0) {
- if (pad_len < 10)
- return -EINVAL;
- /* 'restore' padding left in the PCICC/PCIXCC card. */
- if (copy_to_user(outputdata, static_pad, pad_len - 1))
- return -EFAULT;
- if (put_user(0, outputdata + pad_len - 1))
- return -EFAULT;
- }
- /* Copy the crypto response to user space. */
- if (copy_to_user(outputdata + pad_len, data, reply_len))
- return -EFAULT;
- return 0;
-}
-
-static int convert_response(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- struct type86_reply *msg = reply->message;
-
- /* Response type byte is the second byte in the response. */
- switch (msg->hdr.type) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- return convert_error(zdev, reply);
- case TYPE86_RSP_CODE:
- if (msg->hdr.reply_code)
- return convert_error(zdev, reply);
- if (msg->cprb.cprb_ver_id == 0x01)
- return convert_type86(zdev, reply,
- outputdata, outputdatalength);
- /* no break, incorrect cprb version is an unknown response */
- default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- zdev->ap_dev->qid);
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%dfail",
- zdev->ap_dev->qid, zdev->online);
- return -EAGAIN; /* repeat the request on a different device. */
- }
-}
-
-/**
- * This function is called from the AP bus code after a crypto request
- * "msg" has finished with the reply message "reply".
- * It is called from tasklet context.
- * @ap_dev: pointer to the AP device
- * @msg: pointer to the AP message
- * @reply: pointer to the AP reply message
- */
-static void zcrypt_pcicc_receive(struct ap_device *ap_dev,
- struct ap_message *msg,
- struct ap_message *reply)
-{
- static struct error_hdr error_reply = {
- .type = TYPE82_RSP_CODE,
- .reply_code = REP82_ERROR_MACHINE_FAILURE,
- };
- struct type86_reply *t86r;
- int length;
-
- /* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
- t86r = reply->message;
- if (t86r->hdr.type == TYPE86_RSP_CODE &&
- t86r->cprb.cprb_ver_id == 0x01) {
- length = sizeof(struct type86_reply) + t86r->length - 2;
- length = min(PCICC_MAX_RESPONSE_SIZE, length);
- memcpy(msg->message, reply->message, length);
- } else
- memcpy(msg->message, reply->message, sizeof error_reply);
-out:
- complete((struct completion *) msg->private);
-}
-
-static atomic_t zcrypt_step = ATOMIC_INIT(0);
-
-/**
- * The request distributor calls this function if it picked the PCICC
- * device to handle a modexpo request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCICC device to the request distributor
- * @mex: pointer to the modexpo request buffer
- */
-static long zcrypt_pcicc_modexpo(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo *mex)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcicc_receive;
- ap_msg.length = PAGE_SIZE;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICAMEX_msg_to_type6MEX_msg(zdev, &ap_msg, mex);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, mex->outputdata,
- mex->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- free_page((unsigned long) ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCICC
- * device to handle a modexpo_crt request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCICC device to the request distributor
- * @crt: pointer to the modexpoc_crt request buffer
- */
-static long zcrypt_pcicc_modexpo_crt(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo_crt *crt)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcicc_receive;
- ap_msg.length = PAGE_SIZE;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICACRT_msg_to_type6CRT_msg(zdev, &ap_msg, crt);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, crt->outputdata,
- crt->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- free_page((unsigned long) ap_msg.message);
- return rc;
-}
-
-/**
- * The crypto operations for a PCICC card.
- */
-static struct zcrypt_ops zcrypt_pcicc_ops = {
- .rsa_modexpo = zcrypt_pcicc_modexpo,
- .rsa_modexpo_crt = zcrypt_pcicc_modexpo_crt,
-};
-
-/**
- * Probe function for PCICC cards. It always accepts the AP device
- * since the bus_match already checked the hardware type.
- * @ap_dev: pointer to the AP device.
- */
-static int zcrypt_pcicc_probe(struct ap_device *ap_dev)
-{
- struct zcrypt_device *zdev;
- int rc;
-
- zdev = zcrypt_device_alloc(PCICC_MAX_RESPONSE_SIZE);
- if (!zdev)
- return -ENOMEM;
- zdev->ap_dev = ap_dev;
- zdev->ops = &zcrypt_pcicc_ops;
- zdev->online = 1;
- zdev->user_space_type = ZCRYPT_PCICC;
- zdev->type_string = "PCICC";
- zdev->min_mod_size = PCICC_MIN_MOD_SIZE;
- zdev->max_mod_size = PCICC_MAX_MOD_SIZE;
- zdev->speed_rating = PCICC_SPEED_RATING;
- zdev->max_exp_bit_length = PCICC_MAX_MOD_SIZE;
- ap_dev->reply = &zdev->reply;
- ap_dev->private = zdev;
- rc = zcrypt_device_register(zdev);
- if (rc)
- goto out_free;
- return 0;
-
- out_free:
- ap_dev->private = NULL;
- zcrypt_device_free(zdev);
- return rc;
-}
-
-/**
- * This is called to remove the extended PCICC driver information
- * if an AP device is removed.
- */
-static void zcrypt_pcicc_remove(struct ap_device *ap_dev)
-{
- struct zcrypt_device *zdev = ap_dev->private;
-
- zcrypt_device_unregister(zdev);
-}
-
-int __init zcrypt_pcicc_init(void)
-{
- return ap_driver_register(&zcrypt_pcicc_driver, THIS_MODULE, "pcicc");
-}
-
-void zcrypt_pcicc_exit(void)
-{
- ap_driver_unregister(&zcrypt_pcicc_driver);
-}
-
-module_init(zcrypt_pcicc_init);
-module_exit(zcrypt_pcicc_exit);
diff --git a/kernel/drivers/s390/crypto/zcrypt_pcicc.h b/kernel/drivers/s390/crypto/zcrypt_pcicc.h
deleted file mode 100644
index 7fe27e150..000000000
--- a/kernel/drivers/s390/crypto/zcrypt_pcicc.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * zcrypt 2.1.0
- *
- * Copyright IBM Corp. 2001, 2006
- * Author(s): Robert Burroughs
- * Eric Rossman (edrossma@us.ibm.com)
- *
- * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _ZCRYPT_PCICC_H_
-#define _ZCRYPT_PCICC_H_
-
-/**
- * The type 6 message family is associated with PCICC or PCIXCC cards.
- *
- * It contains a message header followed by a CPRB, both of which
- * are described below.
- *
- * Note that all reserved fields must be zeroes.
- */
-struct type6_hdr {
- unsigned char reserved1; /* 0x00 */
- unsigned char type; /* 0x06 */
- unsigned char reserved2[2]; /* 0x0000 */
- unsigned char right[4]; /* 0x00000000 */
- unsigned char reserved3[2]; /* 0x0000 */
- unsigned char reserved4[2]; /* 0x0000 */
- unsigned char apfs[4]; /* 0x00000000 */
- unsigned int offset1; /* 0x00000058 (offset to CPRB) */
- unsigned int offset2; /* 0x00000000 */
- unsigned int offset3; /* 0x00000000 */
- unsigned int offset4; /* 0x00000000 */
- unsigned char agent_id[16]; /* PCICC: */
- /* 0x0100 */
- /* 0x4343412d4150504c202020 */
- /* 0x010101 */
- /* PCIXCC: */
- /* 0x4341000000000000 */
- /* 0x0000000000000000 */
- unsigned char rqid[2]; /* rqid. internal to 603 */
- unsigned char reserved5[2]; /* 0x0000 */
- unsigned char function_code[2]; /* for PKD, 0x5044 (ascii 'PD') */
- unsigned char reserved6[2]; /* 0x0000 */
- unsigned int ToCardLen1; /* (request CPRB len + 3) & -4 */
- unsigned int ToCardLen2; /* db len 0x00000000 for PKD */
- unsigned int ToCardLen3; /* 0x00000000 */
- unsigned int ToCardLen4; /* 0x00000000 */
- unsigned int FromCardLen1; /* response buffer length */
- unsigned int FromCardLen2; /* db len 0x00000000 for PKD */
- unsigned int FromCardLen3; /* 0x00000000 */
- unsigned int FromCardLen4; /* 0x00000000 */
-} __attribute__((packed));
-
-/**
- * CPRB
- * Note that all shorts, ints and longs are little-endian.
- * All pointer fields are 32-bits long, and mean nothing
- *
- * A request CPRB is followed by a request_parameter_block.
- *
- * The request (or reply) parameter block is organized thus:
- * function code
- * VUD block
- * key block
- */
-struct CPRB {
- unsigned short cprb_len; /* CPRB length */
- unsigned char cprb_ver_id; /* CPRB version id. */
- unsigned char pad_000; /* Alignment pad byte. */
- unsigned char srpi_rtcode[4]; /* SRPI return code LELONG */
- unsigned char srpi_verb; /* SRPI verb type */
- unsigned char flags; /* flags */
- unsigned char func_id[2]; /* function id */
- unsigned char checkpoint_flag; /* */
- unsigned char resv2; /* reserved */
- unsigned short req_parml; /* request parameter buffer */
- /* length 16-bit little endian */
- unsigned char req_parmp[4]; /* request parameter buffer *
- * pointer (means nothing: the *
- * parameter buffer follows *
- * the CPRB). */
- unsigned char req_datal[4]; /* request data buffer */
- /* length ULELONG */
- unsigned char req_datap[4]; /* request data buffer */
- /* pointer */
- unsigned short rpl_parml; /* reply parameter buffer */
- /* length 16-bit little endian */
- unsigned char pad_001[2]; /* Alignment pad bytes. ULESHORT */
- unsigned char rpl_parmp[4]; /* reply parameter buffer *
- * pointer (means nothing: the *
- * parameter buffer follows *
- * the CPRB). */
- unsigned char rpl_datal[4]; /* reply data buffer len ULELONG */
- unsigned char rpl_datap[4]; /* reply data buffer */
- /* pointer */
- unsigned short ccp_rscode; /* server reason code ULESHORT */
- unsigned short ccp_rtcode; /* server return code ULESHORT */
- unsigned char repd_parml[2]; /* replied parameter len ULESHORT*/
- unsigned char mac_data_len[2]; /* Mac Data Length ULESHORT */
- unsigned char repd_datal[4]; /* replied data length ULELONG */
- unsigned char req_pc[2]; /* PC identifier */
- unsigned char res_origin[8]; /* resource origin */
- unsigned char mac_value[8]; /* Mac Value */
- unsigned char logon_id[8]; /* Logon Identifier */
- unsigned char usage_domain[2]; /* cdx */
- unsigned char resv3[18]; /* reserved for requestor */
- unsigned short svr_namel; /* server name length ULESHORT */
- unsigned char svr_name[8]; /* server name */
-} __attribute__((packed));
-
-/**
- * The type 86 message family is associated with PCICC and PCIXCC cards.
- *
- * It contains a message header followed by a CPRB. The CPRB is
- * the same as the request CPRB, which is described above.
- *
- * If format is 1, an error condition exists and no data beyond
- * the 8-byte message header is of interest.
- *
- * The non-error message is shown below.
- *
- * Note that all reserved fields must be zeroes.
- */
-struct type86_hdr {
- unsigned char reserved1; /* 0x00 */
- unsigned char type; /* 0x86 */
- unsigned char format; /* 0x01 (error) or 0x02 (ok) */
- unsigned char reserved2; /* 0x00 */
- unsigned char reply_code; /* reply code (see above) */
- unsigned char reserved3[3]; /* 0x000000 */
-} __attribute__((packed));
-
-#define TYPE86_RSP_CODE 0x86
-#define TYPE86_FMT2 0x02
-
-struct type86_fmt2_ext {
- unsigned char reserved[4]; /* 0x00000000 */
- unsigned char apfs[4]; /* final status */
- unsigned int count1; /* length of CPRB + parameters */
- unsigned int offset1; /* offset to CPRB */
- unsigned int count2; /* 0x00000000 */
- unsigned int offset2; /* db offset 0x00000000 for PKD */
- unsigned int count3; /* 0x00000000 */
- unsigned int offset3; /* 0x00000000 */
- unsigned int count4; /* 0x00000000 */
- unsigned int offset4; /* 0x00000000 */
-} __attribute__((packed));
-
-struct function_and_rules_block {
- unsigned char function_code[2];
- unsigned short ulen;
- unsigned char only_rule[8];
-} __attribute__((packed));
-
-int zcrypt_pcicc_init(void);
-void zcrypt_pcicc_exit(void);
-
-#endif /* _ZCRYPT_PCICC_H_ */
diff --git a/kernel/drivers/s390/crypto/zcrypt_pcixcc.c b/kernel/drivers/s390/crypto/zcrypt_pcixcc.c
index 899ffa19f..f41852768 100644
--- a/kernel/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/kernel/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -182,7 +182,7 @@ static int zcrypt_pcixcc_mcl(struct ap_device *ap_dev)
/* Wait for the test message to complete. */
for (i = 0; i < 6; i++) {
- mdelay(300);
+ msleep(300);
rc = ap_recv(ap_dev->qid, &psmid, reply, 4096);
if (rc == 0 && psmid == 0x0102030405060708ULL)
break;
diff --git a/kernel/drivers/s390/net/lcs.c b/kernel/drivers/s390/net/lcs.c
index 00b7d9c9f..2f5b518b0 100644
--- a/kernel/drivers/s390/net/lcs.c
+++ b/kernel/drivers/s390/net/lcs.c
@@ -2150,7 +2150,7 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
rc = lcs_detect(card);
if (rc) {
LCS_DBF_TEXT(2, setup, "dtctfail");
- dev_err(&card->dev->dev,
+ dev_err(&ccwgdev->dev,
"Detecting a network adapter for LCS devices"
" failed with rc=%d (0x%x)\n", rc, rc);
lcs_stopcard(card);
diff --git a/kernel/drivers/s390/net/netiucv.c b/kernel/drivers/s390/net/netiucv.c
index 33f7040d7..0ba3a2f81 100644
--- a/kernel/drivers/s390/net/netiucv.c
+++ b/kernel/drivers/s390/net/netiucv.c
@@ -149,12 +149,11 @@ static struct device_driver netiucv_driver = {
.pm = &netiucv_pm_ops,
};
-static int netiucv_callback_connreq(struct iucv_path *,
- u8 ipvmid[8], u8 ipuser[16]);
-static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
-static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
-static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
-static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
+static int netiucv_callback_connreq(struct iucv_path *, u8 *, u8 *);
+static void netiucv_callback_connack(struct iucv_path *, u8 *);
+static void netiucv_callback_connrej(struct iucv_path *, u8 *);
+static void netiucv_callback_connsusp(struct iucv_path *, u8 *);
+static void netiucv_callback_connres(struct iucv_path *, u8 *);
static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
@@ -556,8 +555,8 @@ static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
}
-static int netiucv_callback_connreq(struct iucv_path *path,
- u8 ipvmid[8], u8 ipuser[16])
+static int netiucv_callback_connreq(struct iucv_path *path, u8 *ipvmid,
+ u8 *ipuser)
{
struct iucv_connection *conn = path->private;
struct iucv_event ev;
@@ -587,21 +586,21 @@ static int netiucv_callback_connreq(struct iucv_path *path,
return rc;
}
-static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
+static void netiucv_callback_connrej(struct iucv_path *path, u8 *ipuser)
{
struct iucv_connection *conn = path->private;
fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
}
-static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
+static void netiucv_callback_connsusp(struct iucv_path *path, u8 *ipuser)
{
struct iucv_connection *conn = path->private;
fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
}
-static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
+static void netiucv_callback_connres(struct iucv_path *path, u8 *ipuser)
{
struct iucv_connection *conn = path->private;
diff --git a/kernel/drivers/s390/net/qeth_core.h b/kernel/drivers/s390/net/qeth_core.h
index 3abac0288..1766a20eb 100644
--- a/kernel/drivers/s390/net/qeth_core.h
+++ b/kernel/drivers/s390/net/qeth_core.h
@@ -18,6 +18,7 @@
#include <linux/bitops.h>
#include <linux/seq_file.h>
#include <linux/ethtool.h>
+#include <linux/hashtable.h>
#include <net/ipv6.h>
#include <net/if_inet6.h>
@@ -175,6 +176,8 @@ struct qeth_sbp_info {
__u32 supported_funcs;
enum qeth_sbp_roles role;
__u32 hostnotification:1;
+ __u32 reflect_promisc:1;
+ __u32 reflect_promisc_primary:1;
};
static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa,
@@ -661,9 +664,7 @@ struct qeth_card_info {
char mcl_level[QETH_MCL_LENGTH + 1];
int guestlan;
int mac_bits;
- int portname_required;
int portno;
- char portname[9];
enum qeth_card_types type;
enum qeth_link_types link_type;
int is_multicast_different;
@@ -739,11 +740,17 @@ struct qeth_vlan_vid {
unsigned short vid;
};
-struct qeth_mc_mac {
- struct list_head list;
- __u8 mc_addr[MAX_ADDR_LEN];
- unsigned char mc_addrlen;
- int is_vmac;
+enum qeth_mac_disposition {
+ QETH_DISP_MAC_DELETE = 0,
+ QETH_DISP_MAC_DO_NOTHING = 1,
+ QETH_DISP_MAC_ADD = 2,
+};
+
+struct qeth_mac {
+ u8 mac_addr[OSA_ADDR_LEN];
+ u8 is_uc:1;
+ u8 disp_flag:2;
+ struct hlist_node hnode;
};
struct qeth_rx {
@@ -790,7 +797,7 @@ struct qeth_card {
spinlock_t mclock;
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
struct list_head vid_list;
- struct list_head mc_list;
+ DECLARE_HASHTABLE(mac_htable, 4);
struct work_struct kernel_thread_starter;
spinlock_t thread_mask_lock;
unsigned long thread_start_mask;
@@ -967,6 +974,15 @@ int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action);
int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot);
void qeth_trace_features(struct qeth_card *);
void qeth_close_dev(struct qeth_card *);
+int qeth_send_simple_setassparms(struct qeth_card *, enum qeth_ipa_funcs,
+ __u16, long);
+int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, __u16,
+ long,
+ int (*reply_cb)(struct qeth_card *,
+ struct qeth_reply *, unsigned long),
+ void *);
+int qeth_start_ipa_tx_checksum(struct qeth_card *);
+int qeth_set_rx_csum(struct qeth_card *, int);
/* exports for OSN */
int qeth_osn_assist(struct net_device *, void *, int);
diff --git a/kernel/drivers/s390/net/qeth_core_main.c b/kernel/drivers/s390/net/qeth_core_main.c
index 3466d3cb7..31ac53fa5 100644
--- a/kernel/drivers/s390/net/qeth_core_main.c
+++ b/kernel/drivers/s390/net/qeth_core_main.c
@@ -645,7 +645,8 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
card->info.hwtrap = 2;
qeth_schedule_recovery(card);
return NULL;
- case IPA_CMD_SETBRIDGEPORT:
+ case IPA_CMD_SETBRIDGEPORT_IQD:
+ case IPA_CMD_SETBRIDGEPORT_OSA:
case IPA_CMD_ADDRESS_CHANGE_NOTIF:
if (card->discipline->control_event_handler
(card, cmd))
@@ -1981,14 +1982,6 @@ static void qeth_idx_read_cb(struct qeth_channel *channel,
goto out;
}
-/**
- * * temporary fix for microcode bug
- * * to revert it,replace OR by AND
- * */
- if ((!QETH_IDX_NO_PORTNAME_REQUIRED(iob->data)) ||
- (card->info.type == QETH_CARD_TYPE_OSD))
- card->info.portname_required = 1;
-
memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
if (temp != qeth_peer_func_level(card->info.func_level)) {
QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function "
@@ -2359,8 +2352,6 @@ static int qeth_ulp_enable(struct qeth_card *card)
&card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data),
&card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(iob->data),
- card->info.portname, 9);
rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob,
qeth_ulp_enable_cb, NULL);
return rc;
@@ -2679,48 +2670,6 @@ out_qdio:
return rc;
}
-static void qeth_print_status_with_portname(struct qeth_card *card)
-{
- char dbf_text[15];
- int i;
-
- sprintf(dbf_text, "%s", card->info.portname + 1);
- for (i = 0; i < 8; i++)
- dbf_text[i] =
- (char) _ebcasc[(__u8) dbf_text[i]];
- dbf_text[8] = 0;
- dev_info(&card->gdev->dev, "Device is a%s card%s%s%s\n"
- "with link type %s (portname: %s)\n",
- qeth_get_cardname(card),
- (card->info.mcl_level[0]) ? " (level: " : "",
- (card->info.mcl_level[0]) ? card->info.mcl_level : "",
- (card->info.mcl_level[0]) ? ")" : "",
- qeth_get_cardname_short(card),
- dbf_text);
-
-}
-
-static void qeth_print_status_no_portname(struct qeth_card *card)
-{
- if (card->info.portname[0])
- dev_info(&card->gdev->dev, "Device is a%s "
- "card%s%s%s\nwith link type %s "
- "(no portname needed by interface).\n",
- qeth_get_cardname(card),
- (card->info.mcl_level[0]) ? " (level: " : "",
- (card->info.mcl_level[0]) ? card->info.mcl_level : "",
- (card->info.mcl_level[0]) ? ")" : "",
- qeth_get_cardname_short(card));
- else
- dev_info(&card->gdev->dev, "Device is a%s "
- "card%s%s%s\nwith link type %s.\n",
- qeth_get_cardname(card),
- (card->info.mcl_level[0]) ? " (level: " : "",
- (card->info.mcl_level[0]) ? card->info.mcl_level : "",
- (card->info.mcl_level[0]) ? ")" : "",
- qeth_get_cardname_short(card));
-}
-
void qeth_print_status_message(struct qeth_card *card)
{
switch (card->info.type) {
@@ -2757,10 +2706,13 @@ void qeth_print_status_message(struct qeth_card *card)
default:
memset(&card->info.mcl_level[0], 0, QETH_MCL_LENGTH + 1);
}
- if (card->info.portname_required)
- qeth_print_status_with_portname(card);
- else
- qeth_print_status_no_portname(card);
+ dev_info(&card->gdev->dev,
+ "Device is a%s card%s%s%s\nwith link type %s.\n",
+ qeth_get_cardname(card),
+ (card->info.mcl_level[0]) ? " (level: " : "",
+ (card->info.mcl_level[0]) ? card->info.mcl_level : "",
+ (card->info.mcl_level[0]) ? ")" : "",
+ qeth_get_cardname_short(card));
}
EXPORT_SYMBOL_GPL(qeth_print_status_message);
@@ -5026,13 +4978,11 @@ static void qeth_core_free_card(struct qeth_card *card)
void qeth_trace_features(struct qeth_card *card)
{
QETH_CARD_TEXT(card, 2, "features");
- QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.supported_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.enabled_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.supported_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.enabled_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.supported_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.enabled_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->info.diagass_support);
+ QETH_CARD_HEX(card, 2, &card->options.ipa4, sizeof(card->options.ipa4));
+ QETH_CARD_HEX(card, 2, &card->options.ipa6, sizeof(card->options.ipa6));
+ QETH_CARD_HEX(card, 2, &card->options.adp, sizeof(card->options.adp));
+ QETH_CARD_HEX(card, 2, &card->info.diagass_support,
+ sizeof(card->info.diagass_support));
}
EXPORT_SYMBOL_GPL(qeth_trace_features);
@@ -5131,6 +5081,7 @@ retriable:
}
card->options.ipa4.supported_funcs = 0;
+ card->options.ipa6.supported_funcs = 0;
card->options.adp.supported_funcs = 0;
card->options.sbp.supported_funcs = 0;
card->info.diagass_support = 0;
@@ -5316,6 +5267,102 @@ no_mem:
}
EXPORT_SYMBOL_GPL(qeth_core_get_next_skb);
+static int qeth_setassparms_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 4, "defadpcb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code == 0) {
+ cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
+ if (cmd->hdr.prot_version == QETH_PROT_IPV4)
+ card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled;
+ if (cmd->hdr.prot_version == QETH_PROT_IPV6)
+ card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
+ }
+ if (cmd->data.setassparms.hdr.assist_no == IPA_INBOUND_CHECKSUM &&
+ cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) {
+ card->info.csum_mask = cmd->data.setassparms.data.flags_32bit;
+ QETH_CARD_TEXT_(card, 3, "csum:%d", card->info.csum_mask);
+ }
+ if (cmd->data.setassparms.hdr.assist_no == IPA_OUTBOUND_CHECKSUM &&
+ cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) {
+ card->info.tx_csum_mask =
+ cmd->data.setassparms.data.flags_32bit;
+ QETH_CARD_TEXT_(card, 3, "tcsu:%d", card->info.tx_csum_mask);
+ }
+
+ return 0;
+}
+
+static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
+ enum qeth_ipa_funcs ipa_func,
+ __u16 cmd_code, __u16 len,
+ enum qeth_prot_versions prot)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 4, "getasscm");
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot);
+
+ if (iob) {
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setassparms.hdr.assist_no = ipa_func;
+ cmd->data.setassparms.hdr.length = 8 + len;
+ cmd->data.setassparms.hdr.command_code = cmd_code;
+ cmd->data.setassparms.hdr.return_code = 0;
+ cmd->data.setassparms.hdr.seq_no = 0;
+ }
+
+ return iob;
+}
+
+int qeth_send_setassparms(struct qeth_card *card,
+ struct qeth_cmd_buffer *iob, __u16 len, long data,
+ int (*reply_cb)(struct qeth_card *,
+ struct qeth_reply *, unsigned long),
+ void *reply_param)
+{
+ int rc;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 4, "sendassp");
+
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ if (len <= sizeof(__u32))
+ cmd->data.setassparms.data.flags_32bit = (__u32) data;
+ else /* (len > sizeof(__u32)) */
+ memcpy(&cmd->data.setassparms.data, (void *) data, len);
+
+ rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_send_setassparms);
+
+int qeth_send_simple_setassparms(struct qeth_card *card,
+ enum qeth_ipa_funcs ipa_func,
+ __u16 cmd_code, long data)
+{
+ int rc;
+ int length = 0;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_CARD_TEXT(card, 4, "simassp4");
+ if (data)
+ length = sizeof(__u32);
+ iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code,
+ length, QETH_PROT_IPV4);
+ if (!iob)
+ return -ENOMEM;
+ rc = qeth_send_setassparms(card, iob, length, data,
+ qeth_setassparms_cb, NULL);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_send_simple_setassparms);
+
static void qeth_unregister_dbf_views(void)
{
int x;
@@ -6002,6 +6049,75 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,
}
EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings);
+static int qeth_send_checksum_command(struct qeth_card *card)
+{
+ int rc;
+
+ rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ dev_warn(&card->gdev->dev, "Starting HW checksumming for %s "
+ "failed, using SW checksumming\n",
+ QETH_CARD_IFNAME(card));
+ return rc;
+ }
+ rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
+ IPA_CMD_ASS_ENABLE,
+ card->info.csum_mask);
+ if (rc) {
+ dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s "
+ "failed, using SW checksumming\n",
+ QETH_CARD_IFNAME(card));
+ return rc;
+ }
+ return 0;
+}
+
+int qeth_set_rx_csum(struct qeth_card *card, int on)
+{
+ int rc;
+
+ if (on) {
+ rc = qeth_send_checksum_command(card);
+ if (rc)
+ return -EIO;
+ dev_info(&card->gdev->dev,
+ "HW Checksumming (inbound) enabled\n");
+ } else {
+ rc = qeth_send_simple_setassparms(card,
+ IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0);
+ if (rc)
+ return -EIO;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qeth_set_rx_csum);
+
+int qeth_start_ipa_tx_checksum(struct qeth_card *card)
+{
+ int rc = 0;
+
+ if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
+ return rc;
+ rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
+ IPA_CMD_ASS_START, 0);
+ if (rc)
+ goto err_out;
+ rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
+ IPA_CMD_ASS_ENABLE,
+ card->info.tx_csum_mask);
+ if (rc)
+ goto err_out;
+
+ dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n");
+ return rc;
+err_out:
+ dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s "
+ "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card));
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_start_ipa_tx_checksum);
+
static int __init qeth_core_init(void)
{
int rc;
diff --git a/kernel/drivers/s390/net/qeth_core_mpc.c b/kernel/drivers/s390/net/qeth_core_mpc.c
index 7b55768a9..beb4bdc26 100644
--- a/kernel/drivers/s390/net/qeth_core_mpc.c
+++ b/kernel/drivers/s390/net/qeth_core_mpc.c
@@ -237,6 +237,7 @@ static struct ipa_cmd_names qeth_ipa_cmd_names[] = {
{IPA_CMD_DELGMAC, "delgmac"},
{IPA_CMD_SETVLAN, "setvlan"},
{IPA_CMD_DELVLAN, "delvlan"},
+ {IPA_CMD_SETBRIDGEPORT_OSA, "set_bridge_port(osa)"},
{IPA_CMD_SETCCID, "setccid"},
{IPA_CMD_DELCCID, "delccid"},
{IPA_CMD_MODCCID, "modccid"},
@@ -249,7 +250,7 @@ static struct ipa_cmd_names qeth_ipa_cmd_names[] = {
{IPA_CMD_DELIP, "delip"},
{IPA_CMD_SETADAPTERPARMS, "setadapterparms"},
{IPA_CMD_SET_DIAG_ASS, "set_diag_ass"},
- {IPA_CMD_SETBRIDGEPORT, "set_bridge_port"},
+ {IPA_CMD_SETBRIDGEPORT_IQD, "set_bridge_port(hs)"},
{IPA_CMD_CREATE_ADDR, "create_addr"},
{IPA_CMD_DESTROY_ADDR, "destroy_addr"},
{IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"},
diff --git a/kernel/drivers/s390/net/qeth_core_mpc.h b/kernel/drivers/s390/net/qeth_core_mpc.h
index 1558be1af..6cccc9a49 100644
--- a/kernel/drivers/s390/net/qeth_core_mpc.h
+++ b/kernel/drivers/s390/net/qeth_core_mpc.h
@@ -92,6 +92,7 @@ enum qeth_ipa_cmds {
IPA_CMD_DELGMAC = 0x24,
IPA_CMD_SETVLAN = 0x25,
IPA_CMD_DELVLAN = 0x26,
+ IPA_CMD_SETBRIDGEPORT_OSA = 0x2b,
IPA_CMD_SETCCID = 0x41,
IPA_CMD_DELCCID = 0x42,
IPA_CMD_MODCCID = 0x43,
@@ -104,7 +105,7 @@ enum qeth_ipa_cmds {
IPA_CMD_DELIP = 0xb7,
IPA_CMD_SETADAPTERPARMS = 0xb8,
IPA_CMD_SET_DIAG_ASS = 0xb9,
- IPA_CMD_SETBRIDGEPORT = 0xbe,
+ IPA_CMD_SETBRIDGEPORT_IQD = 0xbe,
IPA_CMD_CREATE_ADDR = 0xc3,
IPA_CMD_DESTROY_ADDR = 0xc4,
IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1,
diff --git a/kernel/drivers/s390/net/qeth_core_sys.c b/kernel/drivers/s390/net/qeth_core_sys.c
index 423bec56c..e6e5b9671 100644
--- a/kernel/drivers/s390/net/qeth_core_sys.c
+++ b/kernel/drivers/s390/net/qeth_core_sys.c
@@ -153,52 +153,17 @@ static DEVICE_ATTR(portno, 0644, qeth_dev_portno_show, qeth_dev_portno_store);
static ssize_t qeth_dev_portname_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct qeth_card *card = dev_get_drvdata(dev);
- char portname[9] = {0, };
-
- if (!card)
- return -EINVAL;
-
- if (card->info.portname_required) {
- memcpy(portname, card->info.portname + 1, 8);
- EBCASC(portname, 8);
- return sprintf(buf, "%s\n", portname);
- } else
- return sprintf(buf, "no portname required\n");
+ return sprintf(buf, "no portname required\n");
}
static ssize_t qeth_dev_portname_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
- char *tmp;
- int i, rc = 0;
-
- if (!card)
- return -EINVAL;
-
- mutex_lock(&card->conf_mutex);
- if ((card->state != CARD_STATE_DOWN) &&
- (card->state != CARD_STATE_RECOVER)) {
- rc = -EPERM;
- goto out;
- }
- tmp = strsep((char **) &buf, "\n");
- if ((strlen(tmp) > 8) || (strlen(tmp) == 0)) {
- rc = -EINVAL;
- goto out;
- }
-
- card->info.portname[0] = strlen(tmp);
- /* for beauty reasons */
- for (i = 1; i < 9; i++)
- card->info.portname[i] = ' ';
- strcpy(card->info.portname + 1, tmp);
- ASCEBC(card->info.portname + 1, 8);
-out:
- mutex_unlock(&card->conf_mutex);
- return rc ? rc : count;
+ dev_warn_once(&card->gdev->dev,
+ "portname is deprecated and is ignored\n");
+ return count;
}
static DEVICE_ATTR(portname, 0644, qeth_dev_portname_show,
diff --git a/kernel/drivers/s390/net/qeth_l2_main.c b/kernel/drivers/s390/net/qeth_l2_main.c
index 0ea086912..8f1b091e1 100644
--- a/kernel/drivers/s390/net/qeth_l2_main.c
+++ b/kernel/drivers/s390/net/qeth_l2_main.c
@@ -19,7 +19,9 @@
#include <linux/mii.h>
#include <linux/ip.h>
#include <linux/list.h>
-
+#include <linux/hash.h>
+#include <linux/hashtable.h>
+#include <linux/string.h>
#include "qeth_core.h"
#include "qeth_l2.h"
@@ -28,7 +30,7 @@ static int qeth_l2_stop(struct net_device *);
static int qeth_l2_send_delmac(struct qeth_card *, __u8 *);
static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *,
enum qeth_ipa_cmds);
-static void qeth_l2_set_multicast_list(struct net_device *);
+static void qeth_l2_set_rx_mode(struct net_device *);
static int qeth_l2_recover(void *);
static void qeth_bridgeport_query_support(struct qeth_card *card);
static void qeth_bridge_state_change(struct qeth_card *card,
@@ -137,7 +139,7 @@ static int qeth_setdel_makerc(struct qeth_card *card, int retcode)
rc = 0;
break;
case IPA_RC_L2_UNSUPPORTED_CMD:
- rc = -ENOSYS;
+ rc = -EOPNOTSUPP;
break;
case IPA_RC_L2_ADDR_TABLE_FULL:
rc = -ENOSPC;
@@ -193,49 +195,44 @@ static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
return rc;
}
-static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac)
+static inline u32 qeth_l2_mac_hash(const u8 *addr)
{
- struct qeth_mc_mac *mc;
- int rc;
-
- mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC);
+ return get_unaligned((u32 *)(&addr[2]));
+}
- if (!mc)
- return;
+static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac)
+{
- memcpy(mc->mc_addr, mac, OSA_ADDR_LEN);
- mc->mc_addrlen = OSA_ADDR_LEN;
- mc->is_vmac = vmac;
+ int rc;
- if (vmac) {
+ if (mac->is_uc) {
rc = qeth_setdel_makerc(card,
- qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC));
+ qeth_l2_send_setdelmac(card, mac->mac_addr,
+ IPA_CMD_SETVMAC));
} else {
rc = qeth_setdel_makerc(card,
- qeth_l2_send_setgroupmac(card, mac));
+ qeth_l2_send_setgroupmac(card, mac->mac_addr));
}
-
- if (!rc)
- list_add_tail(&mc->list, &card->mc_list);
- else
- kfree(mc);
+ return rc;
}
-static void qeth_l2_del_all_mc(struct qeth_card *card, int del)
+static void qeth_l2_del_all_macs(struct qeth_card *card, int del)
{
- struct qeth_mc_mac *mc, *tmp;
+ struct qeth_mac *mac;
+ struct hlist_node *tmp;
+ int i;
spin_lock_bh(&card->mclock);
- list_for_each_entry_safe(mc, tmp, &card->mc_list, list) {
+ hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) {
if (del) {
- if (mc->is_vmac)
- qeth_l2_send_setdelmac(card, mc->mc_addr,
- IPA_CMD_DELVMAC);
+ if (mac->is_uc)
+ qeth_l2_send_setdelmac(card, mac->mac_addr,
+ IPA_CMD_DELVMAC);
else
- qeth_l2_send_delgroupmac(card, mc->mc_addr);
+ qeth_l2_send_delgroupmac(card, mac->mac_addr);
}
- list_del(&mc->list);
- kfree(mc);
+ hash_del(&mac->hnode);
+ kfree(mac);
}
spin_unlock_bh(&card->mclock);
}
@@ -252,6 +249,23 @@ static inline int qeth_l2_get_cast_type(struct qeth_card *card,
return RTN_UNSPEC;
}
+static inline void qeth_l2_hdr_csum(struct qeth_card *card,
+ struct qeth_hdr *hdr, struct sk_buff *skb)
+{
+ struct iphdr *iph = ip_hdr(skb);
+
+ /* tcph->check contains already the pseudo hdr checksum
+ * so just set the header flags
+ */
+ if (iph->protocol == IPPROTO_UDP)
+ hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_UDP;
+ hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_CSUM_TRANSP_REQ |
+ QETH_HDR_EXT_CSUM_HDR_REQ;
+ iph->check = 0;
+ if (card->options.performance_stats)
+ card->perf_stats.tx_csum++;
+}
+
static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
struct sk_buff *skb, int cast_type)
{
@@ -386,14 +400,44 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev,
rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
kfree(tmpid);
}
- qeth_l2_set_multicast_list(card->dev);
+ qeth_l2_set_rx_mode(card->dev);
return rc;
}
-static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
+static netdev_features_t qeth_l2_fix_features(struct net_device *dev,
+ netdev_features_t features)
{
- int rc = 0;
+ struct qeth_card *card = dev->ml_priv;
+
+ QETH_DBF_TEXT(SETUP, 2, "fixfeat");
+ if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
+ features &= ~NETIF_F_IP_CSUM;
+ if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
+ features &= ~NETIF_F_RXCSUM;
+ QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));
+ return features;
+}
+
+static int qeth_l2_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct qeth_card *card = dev->ml_priv;
+ netdev_features_t changed = dev->features ^ features;
+
+ QETH_DBF_TEXT(SETUP, 2, "setfeat");
+ QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));
+ if (card->state == CARD_STATE_DOWN ||
+ card->state == CARD_STATE_RECOVER)
+ return 0;
+
+ if (!(changed & NETIF_F_RXCSUM))
+ return 0;
+ return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0);
+}
+
+static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
+{
QETH_DBF_TEXT(SETUP , 2, "stopcard");
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
@@ -413,7 +457,7 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
card->state = CARD_STATE_SOFTSETUP;
}
if (card->state == CARD_STATE_SOFTSETUP) {
- qeth_l2_del_all_mc(card, 0);
+ qeth_l2_del_all_macs(card, 0);
qeth_clear_ipacmd_list(card);
card->state = CARD_STATE_HARDSETUP;
}
@@ -427,7 +471,6 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
qeth_clear_cmd_buffers(&card->read);
qeth_clear_cmd_buffers(&card->write);
}
- return rc;
}
static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
@@ -453,11 +496,19 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
case QETH_HEADER_TYPE_LAYER2:
skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev);
- skb->ip_summed = CHECKSUM_NONE;
+ if ((card->dev->features & NETIF_F_RXCSUM)
+ && ((hdr->hdr.l2.flags[1] &
+ (QETH_HDR_EXT_CSUM_HDR_REQ |
+ QETH_HDR_EXT_CSUM_TRANSP_REQ)) ==
+ (QETH_HDR_EXT_CSUM_HDR_REQ |
+ QETH_HDR_EXT_CSUM_TRANSP_REQ)))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb->ip_summed = CHECKSUM_NONE;
if (skb->protocol == htons(ETH_P_802_2))
*((__u32 *)skb->cb) = ++card->seqno.pkt_seqno;
len = skb->len;
- netif_receive_skb(skb);
+ napi_gro_receive(&card->napi, skb);
break;
case QETH_HEADER_TYPE_OSN:
if (card->info.type == QETH_CARD_TYPE_OSN) {
@@ -683,30 +734,126 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
return rc ? -EINVAL : 0;
}
-static void qeth_l2_set_multicast_list(struct net_device *dev)
+static void qeth_promisc_to_bridge(struct qeth_card *card)
+{
+ struct net_device *dev = card->dev;
+ enum qeth_ipa_promisc_modes promisc_mode;
+ int role;
+ int rc;
+
+ QETH_CARD_TEXT(card, 3, "pmisc2br");
+
+ if (!card->options.sbp.reflect_promisc)
+ return;
+ promisc_mode = (dev->flags & IFF_PROMISC) ? SET_PROMISC_MODE_ON
+ : SET_PROMISC_MODE_OFF;
+ if (promisc_mode == card->info.promisc_mode)
+ return;
+
+ if (promisc_mode == SET_PROMISC_MODE_ON) {
+ if (card->options.sbp.reflect_promisc_primary)
+ role = QETH_SBP_ROLE_PRIMARY;
+ else
+ role = QETH_SBP_ROLE_SECONDARY;
+ } else
+ role = QETH_SBP_ROLE_NONE;
+
+ rc = qeth_bridgeport_setrole(card, role);
+ QETH_DBF_TEXT_(SETUP, 2, "bpm%c%04x",
+ (promisc_mode == SET_PROMISC_MODE_ON) ? '+' : '-', rc);
+ if (!rc) {
+ card->options.sbp.role = role;
+ card->info.promisc_mode = promisc_mode;
+ }
+
+}
+/* New MAC address is added to the hash table and marked to be written on card
+ * only if there is not in the hash table storage already
+ *
+*/
+static void
+qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha, u8 is_uc)
+{
+ struct qeth_mac *mac;
+
+ hash_for_each_possible(card->mac_htable, mac, hnode,
+ qeth_l2_mac_hash(ha->addr)) {
+ if (is_uc == mac->is_uc &&
+ !memcmp(ha->addr, mac->mac_addr, OSA_ADDR_LEN)) {
+ mac->disp_flag = QETH_DISP_MAC_DO_NOTHING;
+ return;
+ }
+ }
+
+ mac = kzalloc(sizeof(struct qeth_mac), GFP_ATOMIC);
+
+ if (!mac)
+ return;
+
+ memcpy(mac->mac_addr, ha->addr, OSA_ADDR_LEN);
+ mac->is_uc = is_uc;
+ mac->disp_flag = QETH_DISP_MAC_ADD;
+
+ hash_add(card->mac_htable, &mac->hnode,
+ qeth_l2_mac_hash(mac->mac_addr));
+
+}
+
+static void qeth_l2_set_rx_mode(struct net_device *dev)
{
struct qeth_card *card = dev->ml_priv;
struct netdev_hw_addr *ha;
+ struct qeth_mac *mac;
+ struct hlist_node *tmp;
+ int i;
+ int rc;
if (card->info.type == QETH_CARD_TYPE_OSN)
- return ;
+ return;
QETH_CARD_TEXT(card, 3, "setmulti");
if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
(card->state != CARD_STATE_UP))
return;
- qeth_l2_del_all_mc(card, 1);
+
spin_lock_bh(&card->mclock);
+
netdev_for_each_mc_addr(ha, dev)
- qeth_l2_add_mc(card, ha->addr, 0);
+ qeth_l2_add_mac(card, ha, 0);
netdev_for_each_uc_addr(ha, dev)
- qeth_l2_add_mc(card, ha->addr, 1);
+ qeth_l2_add_mac(card, ha, 1);
+
+ hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) {
+ if (mac->disp_flag == QETH_DISP_MAC_DELETE) {
+ if (!mac->is_uc)
+ rc = qeth_l2_send_delgroupmac(card,
+ mac->mac_addr);
+ else {
+ rc = qeth_l2_send_setdelmac(card, mac->mac_addr,
+ IPA_CMD_DELVMAC);
+ }
+
+ hash_del(&mac->hnode);
+ kfree(mac);
+
+ } else if (mac->disp_flag == QETH_DISP_MAC_ADD) {
+ rc = qeth_l2_write_mac(card, mac);
+ if (rc) {
+ hash_del(&mac->hnode);
+ kfree(mac);
+ } else
+ mac->disp_flag = QETH_DISP_MAC_DELETE;
+ } else
+ mac->disp_flag = QETH_DISP_MAC_DELETE;
+ }
spin_unlock_bh(&card->mclock);
- if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
- return;
- qeth_setadp_promisc_mode(card);
+
+ if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
+ qeth_setadp_promisc_mode(card);
+ else
+ qeth_promisc_to_bridge(card);
}
static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
@@ -772,6 +919,8 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
sizeof(struct qeth_hdr));
skb_set_mac_header(new_skb, sizeof(struct qeth_hdr));
qeth_l2_fill_header(card, hdr, new_skb, cast_type);
+ if (new_skb->ip_summed == CHECKSUM_PARTIAL)
+ qeth_l2_hdr_csum(card, hdr, new_skb);
}
}
@@ -884,7 +1033,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
qeth_l2_create_device_attributes(&gdev->dev);
INIT_LIST_HEAD(&card->vid_list);
- INIT_LIST_HEAD(&card->mc_list);
+ hash_init(card->mac_htable);
card->options.layer2 = 1;
card->info.hwtrap = 0;
return 0;
@@ -930,13 +1079,15 @@ static const struct net_device_ops qeth_l2_netdev_ops = {
.ndo_get_stats = qeth_get_stats,
.ndo_start_xmit = qeth_l2_hard_start_xmit,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_rx_mode = qeth_l2_set_multicast_list,
+ .ndo_set_rx_mode = qeth_l2_set_rx_mode,
.ndo_do_ioctl = qeth_l2_do_ioctl,
.ndo_set_mac_address = qeth_l2_set_mac_address,
.ndo_change_mtu = qeth_change_mtu,
.ndo_vlan_rx_add_vid = qeth_l2_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = qeth_l2_vlan_rx_kill_vid,
.ndo_tx_timeout = qeth_tx_timeout,
+ .ndo_fix_features = qeth_l2_fix_features,
+ .ndo_set_features = qeth_l2_set_features
};
static int qeth_l2_setup_netdev(struct qeth_card *card)
@@ -966,6 +1117,11 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
(card->info.type != QETH_CARD_TYPE_OSN) ?
&qeth_l2_ethtool_ops : &qeth_l2_osn_ops;
card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) {
+ card->dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
+ /* Turn on RX offloading per default */
+ card->dev->features |= NETIF_F_RXCSUM;
+ }
card->info.broadcast_capable = 1;
qeth_l2_request_initial_mac(card);
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
@@ -973,6 +1129,17 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
return register_netdev(card->dev);
}
+static int qeth_l2_start_ipassists(struct qeth_card *card)
+{
+ /* configure isolation level */
+ if (qeth_set_access_ctrl_online(card, 0))
+ return -ENODEV;
+ if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
+ qeth_set_rx_csum(card, 1);
+ qeth_start_ipa_tx_checksum(card);
+ return 0;
+}
+
static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
{
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
@@ -994,7 +1161,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
qeth_bridgeport_query_support(card);
if (card->options.sbp.supported_funcs)
dev_info(&card->gdev->dev,
- "The device represents a HiperSockets Bridge Capable Port\n");
+ "The device represents a Bridge Capable Port\n");
qeth_trace_features(card);
if (!card->dev && qeth_l2_setup_netdev(card)) {
@@ -1038,12 +1205,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
contin:
if ((card->info.type == QETH_CARD_TYPE_OSD) ||
(card->info.type == QETH_CARD_TYPE_OSX)) {
- /* configure isolation level */
- rc = qeth_set_access_ctrl_online(card, 0);
- if (rc) {
- rc = -ENODEV;
+ if (qeth_l2_start_ipassists(card))
goto out_remove;
- }
}
if (card->info.type != QETH_CARD_TYPE_OSN &&
@@ -1075,7 +1238,7 @@ contin:
rtnl_unlock();
}
/* this also sets saved unicast addresses */
- qeth_l2_set_multicast_list(card->dev);
+ qeth_l2_set_rx_mode(card->dev);
}
/* let user_space know that device is online */
kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
@@ -1247,7 +1410,8 @@ static int qeth_l2_control_event(struct qeth_card *card,
struct qeth_ipa_cmd *cmd)
{
switch (cmd->hdr.command) {
- case IPA_CMD_SETBRIDGEPORT:
+ case IPA_CMD_SETBRIDGEPORT_OSA:
+ case IPA_CMD_SETBRIDGEPORT_IQD:
if (cmd->data.sbp.hdr.command_code ==
IPA_SBP_BRIDGE_PORT_STATE_CHANGE) {
qeth_bridge_state_change(card, cmd);
@@ -1420,8 +1584,8 @@ static void qeth_bridge_emit_host_event(struct qeth_card *card,
env[i] = str[i]; i++;
}
if (code & IPA_ADDR_CHANGE_CODE_MACADDR) {
- snprintf(str[i], sizeof(str[i]), "MAC=%pM6",
- &addr_lnid->mac);
+ snprintf(str[i], sizeof(str[i]), "MAC=%pM",
+ addr_lnid->mac);
env[i] = str[i]; i++;
}
snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x",
@@ -1533,7 +1697,7 @@ static void qeth_bridge_host_event_worker(struct work_struct *work)
if (data->hostevs.lost_event_mask) {
dev_info(&data->card->gdev->dev,
-"Address notification from the HiperSockets Bridge Port stopped %s (%s)\n",
+"Address notification from the Bridge Port stopped %s (%s)\n",
data->card->dev->name,
(data->hostevs.lost_event_mask == 0x01)
? "Overflow"
@@ -1617,70 +1781,80 @@ static int qeth_bridgeport_makerc(struct qeth_card *card,
struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd)
{
int rc;
+ int is_iqd = (card->info.type == QETH_CARD_TYPE_IQD);
- switch (cbctl->ipa_rc) {
- case IPA_RC_SUCCESS:
+ if ((is_iqd && (cbctl->ipa_rc == IPA_RC_SUCCESS)) ||
+ (!is_iqd && (cbctl->ipa_rc == cbctl->cmd_rc)))
switch (cbctl->cmd_rc) {
case 0x0000:
rc = 0;
break;
+ case 0x2B04:
case 0x0004:
- rc = -ENOSYS;
+ rc = -EOPNOTSUPP;
break;
+ case 0x2B0C:
case 0x000C: /* Not configured as bridge Port */
rc = -ENODEV; /* maybe not the best code here? */
dev_err(&card->gdev->dev,
- "The HiperSockets device is not configured as a Bridge Port\n");
+ "The device is not configured as a Bridge Port\n");
break;
+ case 0x2B14:
case 0x0014: /* Another device is Primary */
switch (setcmd) {
case IPA_SBP_SET_PRIMARY_BRIDGE_PORT:
rc = -EEXIST;
dev_err(&card->gdev->dev,
- "The HiperSockets LAN already has a primary Bridge Port\n");
+ "The LAN already has a primary Bridge Port\n");
break;
case IPA_SBP_SET_SECONDARY_BRIDGE_PORT:
rc = -EBUSY;
dev_err(&card->gdev->dev,
- "The HiperSockets device is already a primary Bridge Port\n");
+ "The device is already a primary Bridge Port\n");
break;
default:
rc = -EIO;
}
break;
+ case 0x2B18:
case 0x0018: /* This device is currently Secondary */
rc = -EBUSY;
dev_err(&card->gdev->dev,
- "The HiperSockets device is already a secondary Bridge Port\n");
+ "The device is already a secondary Bridge Port\n");
break;
+ case 0x2B1C:
case 0x001C: /* Limit for Secondary devices reached */
rc = -EEXIST;
dev_err(&card->gdev->dev,
- "The HiperSockets LAN cannot have more secondary Bridge Ports\n");
+ "The LAN cannot have more secondary Bridge Ports\n");
break;
+ case 0x2B24:
case 0x0024: /* This device is currently Primary */
rc = -EBUSY;
dev_err(&card->gdev->dev,
- "The HiperSockets device is already a primary Bridge Port\n");
+ "The device is already a primary Bridge Port\n");
break;
+ case 0x2B20:
case 0x0020: /* Not authorized by zManager */
rc = -EACCES;
dev_err(&card->gdev->dev,
- "The HiperSockets device is not authorized to be a Bridge Port\n");
+ "The device is not authorized to be a Bridge Port\n");
break;
default:
rc = -EIO;
}
- break;
- case IPA_RC_NOTSUPP:
- rc = -ENOSYS;
- break;
- case IPA_RC_UNSUPPORTED_COMMAND:
- rc = -ENOSYS;
- break;
- default:
- rc = -EIO;
- }
+ else
+ switch (cbctl->ipa_rc) {
+ case IPA_RC_NOTSUPP:
+ rc = -EOPNOTSUPP;
+ break;
+ case IPA_RC_UNSUPPORTED_COMMAND:
+ rc = -EOPNOTSUPP;
+ break;
+ default:
+ rc = -EIO;
+ }
+
if (rc) {
QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc);
QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc);
@@ -1688,6 +1862,13 @@ static int qeth_bridgeport_makerc(struct qeth_card *card,
return rc;
}
+static inline int ipa_cmd_sbp(struct qeth_card *card)
+{
+ return (card->info.type == QETH_CARD_TYPE_IQD) ?
+ IPA_CMD_SETBRIDGEPORT_IQD :
+ IPA_CMD_SETBRIDGEPORT_OSA;
+}
+
static int qeth_bridgeport_query_support_cb(struct qeth_card *card,
struct qeth_reply *reply, unsigned long data)
{
@@ -1719,7 +1900,7 @@ static void qeth_bridgeport_query_support(struct qeth_card *card)
struct _qeth_sbp_cbctl cbctl;
QETH_CARD_TEXT(card, 2, "brqsuppo");
- iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
+ iob = qeth_get_ipacmd_buffer(card, ipa_cmd_sbp(card), 0);
if (!iob)
return;
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
@@ -1796,7 +1977,7 @@ int qeth_bridgeport_query_ports(struct qeth_card *card,
QETH_CARD_TEXT(card, 2, "brqports");
if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS))
return -EOPNOTSUPP;
- iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
+ iob = qeth_get_ipacmd_buffer(card, ipa_cmd_sbp(card), 0);
if (!iob)
return -ENOMEM;
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
@@ -1808,10 +1989,9 @@ int qeth_bridgeport_query_ports(struct qeth_card *card,
cmd->data.sbp.hdr.seq_no = 1;
rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb,
(void *)&cbctl);
- if (rc)
+ if (rc < 0)
return rc;
- rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS);
- return rc;
+ return qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS);
}
EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports);
@@ -1864,7 +2044,7 @@ int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
}
if (!(card->options.sbp.supported_funcs & setcmd))
return -EOPNOTSUPP;
- iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0);
+ iob = qeth_get_ipacmd_buffer(card, ipa_cmd_sbp(card), 0);
if (!iob)
return -ENOMEM;
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
@@ -1874,10 +2054,9 @@ int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
cmd->data.sbp.hdr.seq_no = 1;
rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb,
(void *)&cbctl);
- if (rc)
+ if (rc < 0)
return rc;
- rc = qeth_bridgeport_makerc(card, &cbctl, setcmd);
- return rc;
+ return qeth_bridgeport_makerc(card, &cbctl, setcmd);
}
/**
@@ -1898,7 +2077,7 @@ static int qeth_anset_makerc(struct qeth_card *card, int pnso_rc, u16 response)
case 0x0004:
case 0x0100:
case 0x0106:
- rc = -ENOSYS;
+ rc = -EOPNOTSUPP;
dev_err(&card->gdev->dev,
"Setting address notification failed\n");
break;
diff --git a/kernel/drivers/s390/net/qeth_l2_sys.c b/kernel/drivers/s390/net/qeth_l2_sys.c
index 59e3aa538..692db49e3 100644
--- a/kernel/drivers/s390/net/qeth_l2_sys.c
+++ b/kernel/drivers/s390/net/qeth_l2_sys.c
@@ -23,8 +23,6 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev,
if (!card)
return -EINVAL;
- mutex_lock(&card->conf_mutex);
-
if (qeth_card_hw_is_reachable(card) &&
card->options.sbp.supported_funcs)
rc = qeth_bridgeport_query_ports(card,
@@ -59,8 +57,6 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev,
rc = sprintf(buf, "%s\n", word);
}
- mutex_unlock(&card->conf_mutex);
-
return rc;
}
@@ -90,7 +86,9 @@ static ssize_t qeth_bridge_port_role_store(struct device *dev,
mutex_lock(&card->conf_mutex);
- if (qeth_card_hw_is_reachable(card)) {
+ if (card->options.sbp.reflect_promisc) /* Forbid direct manipulation */
+ rc = -EPERM;
+ else if (qeth_card_hw_is_reachable(card)) {
rc = qeth_bridgeport_setrole(card, role);
if (!rc)
card->options.sbp.role = role;
@@ -111,7 +109,7 @@ static ssize_t qeth_bridge_port_state_show(struct device *dev,
return qeth_bridge_port_role_state_show(dev, attr, buf, 1);
}
-static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show,
+static DEVICE_ATTR(bridge_state, 0444, qeth_bridge_port_state_show,
NULL);
static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev,
@@ -123,12 +121,8 @@ static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev,
if (!card)
return -EINVAL;
- mutex_lock(&card->conf_mutex);
-
enabled = card->options.sbp.hostnotification;
- mutex_unlock(&card->conf_mutex);
-
return sprintf(buf, "%d\n", enabled);
}
@@ -167,10 +161,72 @@ static DEVICE_ATTR(bridge_hostnotify, 0644,
qeth_bridgeport_hostnotification_show,
qeth_bridgeport_hostnotification_store);
+static ssize_t qeth_bridgeport_reflect_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ char *state;
+
+ if (!card)
+ return -EINVAL;
+
+ if (card->options.sbp.reflect_promisc) {
+ if (card->options.sbp.reflect_promisc_primary)
+ state = "primary";
+ else
+ state = "secondary";
+ } else
+ state = "none";
+
+ return sprintf(buf, "%s\n", state);
+}
+
+static ssize_t qeth_bridgeport_reflect_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ int enable, primary;
+ int rc = 0;
+
+ if (!card)
+ return -EINVAL;
+
+ if (sysfs_streq(buf, "none")) {
+ enable = 0;
+ primary = 0;
+ } else if (sysfs_streq(buf, "primary")) {
+ enable = 1;
+ primary = 1;
+ } else if (sysfs_streq(buf, "secondary")) {
+ enable = 1;
+ primary = 0;
+ } else
+ return -EINVAL;
+
+ mutex_lock(&card->conf_mutex);
+
+ if (card->options.sbp.role != QETH_SBP_ROLE_NONE)
+ rc = -EPERM;
+ else {
+ card->options.sbp.reflect_promisc = enable;
+ card->options.sbp.reflect_promisc_primary = primary;
+ rc = 0;
+ }
+
+ mutex_unlock(&card->conf_mutex);
+
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(bridge_reflect_promisc, 0644,
+ qeth_bridgeport_reflect_show,
+ qeth_bridgeport_reflect_store);
+
static struct attribute *qeth_l2_bridgeport_attrs[] = {
&dev_attr_bridge_role.attr,
&dev_attr_bridge_state.attr,
&dev_attr_bridge_hostnotify.attr,
+ &dev_attr_bridge_reflect_promisc.attr,
NULL,
};
diff --git a/kernel/drivers/s390/net/qeth_l3_main.c b/kernel/drivers/s390/net/qeth_l3_main.c
index 04e42c649..543960e96 100644
--- a/kernel/drivers/s390/net/qeth_l3_main.c
+++ b/kernel/drivers/s390/net/qeth_l3_main.c
@@ -1065,27 +1065,6 @@ static struct qeth_cmd_buffer *qeth_l3_get_setassparms_cmd(
return iob;
}
-static int qeth_l3_send_setassparms(struct qeth_card *card,
- struct qeth_cmd_buffer *iob, __u16 len, long data,
- int (*reply_cb)(struct qeth_card *, struct qeth_reply *,
- unsigned long),
- void *reply_param)
-{
- int rc;
- struct qeth_ipa_cmd *cmd;
-
- QETH_CARD_TEXT(card, 4, "sendassp");
-
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- if (len <= sizeof(__u32))
- cmd->data.setassparms.data.flags_32bit = (__u32) data;
- else /* (len > sizeof(__u32)) */
- memcpy(&cmd->data.setassparms.data, (void *) data, len);
-
- rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param);
- return rc;
-}
-
#ifdef CONFIG_QETH_IPV6
static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card,
enum qeth_ipa_funcs ipa_func, __u16 cmd_code)
@@ -1098,31 +1077,12 @@ static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card,
0, QETH_PROT_IPV6);
if (!iob)
return -ENOMEM;
- rc = qeth_l3_send_setassparms(card, iob, 0, 0,
+ rc = qeth_send_setassparms(card, iob, 0, 0,
qeth_l3_default_setassparms_cb, NULL);
return rc;
}
#endif
-static int qeth_l3_send_simple_setassparms(struct qeth_card *card,
- enum qeth_ipa_funcs ipa_func, __u16 cmd_code, long data)
-{
- int rc;
- int length = 0;
- struct qeth_cmd_buffer *iob;
-
- QETH_CARD_TEXT(card, 4, "simassp4");
- if (data)
- length = sizeof(__u32);
- iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code,
- length, QETH_PROT_IPV4);
- if (!iob)
- return -ENOMEM;
- rc = qeth_l3_send_setassparms(card, iob, length, data,
- qeth_l3_default_setassparms_cb, NULL);
- return rc;
-}
-
static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
{
int rc;
@@ -1135,8 +1095,8 @@ static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
QETH_CARD_IFNAME(card));
return 0;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
- IPA_CMD_ASS_START, 0);
+ rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_START, 0);
if (rc) {
dev_warn(&card->gdev->dev,
"Starting ARP processing support for %s failed\n",
@@ -1158,7 +1118,7 @@ static int qeth_l3_start_ipa_ip_fragmentation(struct qeth_card *card)
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_IP_FRAGMENTATION,
+ rc = qeth_send_simple_setassparms(card, IPA_IP_FRAGMENTATION,
IPA_CMD_ASS_START, 0);
if (rc) {
dev_warn(&card->gdev->dev,
@@ -1183,7 +1143,7 @@ static int qeth_l3_start_ipa_source_mac(struct qeth_card *card)
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_SOURCE_MAC,
+ rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC,
IPA_CMD_ASS_START, 0);
if (rc)
dev_warn(&card->gdev->dev,
@@ -1204,7 +1164,7 @@ static int qeth_l3_start_ipa_vlan(struct qeth_card *card)
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_VLAN_PRIO,
+ rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO,
IPA_CMD_ASS_START, 0);
if (rc) {
dev_warn(&card->gdev->dev,
@@ -1229,7 +1189,7 @@ static int qeth_l3_start_ipa_multicast(struct qeth_card *card)
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_MULTICASTING,
+ rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING,
IPA_CMD_ASS_START, 0);
if (rc) {
dev_warn(&card->gdev->dev,
@@ -1259,7 +1219,7 @@ static int qeth_l3_softsetup_ipv6(struct qeth_card *card)
QETH_CARD_IFNAME(card));
return rc;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_IPV6,
+ rc = qeth_send_simple_setassparms(card, IPA_IPV6,
IPA_CMD_ASS_START, 3);
if (rc) {
dev_err(&card->gdev->dev,
@@ -1319,7 +1279,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
rc = -EOPNOTSUPP;
goto out;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+ rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_START, 0);
if (rc) {
dev_warn(&card->gdev->dev, "Enabling broadcast filtering for "
@@ -1327,7 +1287,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
goto out;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+ rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_CONFIGURE, 1);
if (rc) {
dev_warn(&card->gdev->dev,
@@ -1337,7 +1297,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
}
card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
dev_info(&card->gdev->dev, "Broadcast enabled\n");
- rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+ rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_ENABLE, 1);
if (rc) {
dev_warn(&card->gdev->dev, "Setting up broadcast echo "
@@ -1353,84 +1313,18 @@ out:
return rc;
}
-static int qeth_l3_send_checksum_command(struct qeth_card *card)
-{
- int rc;
-
- rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
- IPA_CMD_ASS_START, 0);
- if (rc) {
- dev_warn(&card->gdev->dev, "Starting HW checksumming for %s "
- "failed, using SW checksumming\n",
- QETH_CARD_IFNAME(card));
- return rc;
- }
- rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
- IPA_CMD_ASS_ENABLE,
- card->info.csum_mask);
- if (rc) {
- dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s "
- "failed, using SW checksumming\n",
- QETH_CARD_IFNAME(card));
- return rc;
- }
- return 0;
-}
-
-static int qeth_l3_set_rx_csum(struct qeth_card *card, int on)
-{
- int rc = 0;
-
- if (on) {
- rc = qeth_l3_send_checksum_command(card);
- if (rc)
- return -EIO;
- dev_info(&card->gdev->dev,
- "HW Checksumming (inbound) enabled\n");
- } else {
- rc = qeth_l3_send_simple_setassparms(card,
- IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0);
- if (rc)
- return -EIO;
- }
-
- return 0;
-}
-
-static int qeth_l3_start_ipa_checksum(struct qeth_card *card)
+static void qeth_l3_start_ipa_checksum(struct qeth_card *card)
{
QETH_CARD_TEXT(card, 3, "strtcsum");
-
- if (card->dev->features & NETIF_F_RXCSUM) {
- rtnl_lock();
- /* force set_features call */
- card->dev->features &= ~NETIF_F_RXCSUM;
- netdev_update_features(card->dev);
- rtnl_unlock();
- }
- return 0;
+ if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM)
+ && (card->dev->features & NETIF_F_RXCSUM))
+ qeth_set_rx_csum(card, 1);
}
-static int qeth_l3_start_ipa_tx_checksum(struct qeth_card *card)
+static void qeth_l3_start_ipa_tx_checksum(struct qeth_card *card)
{
- int rc = 0;
-
- if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
- return rc;
- rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
- IPA_CMD_ASS_START, 0);
- if (rc)
- goto err_out;
- rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
- IPA_CMD_ASS_ENABLE, card->info.tx_csum_mask);
- if (rc)
- goto err_out;
- dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n");
- return rc;
-err_out:
- dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s "
- "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card));
- return rc;
+ QETH_CARD_TEXT(card, 3, "strttxcs");
+ qeth_start_ipa_tx_checksum(card);
}
static int qeth_l3_start_ipa_tso(struct qeth_card *card)
@@ -1445,8 +1339,8 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card)
QETH_CARD_IFNAME(card));
rc = -EOPNOTSUPP;
} else {
- rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
- IPA_CMD_ASS_START, 0);
+ rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
+ IPA_CMD_ASS_START, 0);
if (rc)
dev_warn(&card->gdev->dev, "Starting outbound TCP "
"segmentation offload for %s failed\n",
@@ -1950,7 +1844,6 @@ static inline int qeth_l3_rebuild_skb(struct qeth_card *card,
skb->ip_summed = CHECKSUM_NONE;
} else
skb->ip_summed = CHECKSUM_NONE;
-
return is_vlan;
}
@@ -2158,10 +2051,8 @@ static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
return card ;
}
-static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
+static void qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
{
- int rc = 0;
-
QETH_DBF_TEXT(SETUP, 2, "stopcard");
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
@@ -2196,7 +2087,6 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
qeth_clear_cmd_buffers(&card->read);
qeth_clear_cmd_buffers(&card->write);
}
- return rc;
}
/*
@@ -2290,7 +2180,7 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+ rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
no_entries);
if (rc) {
@@ -2555,7 +2445,7 @@ static int qeth_l3_arp_add_entry(struct qeth_card *card,
QETH_PROT_IPV4);
if (!iob)
return -ENOMEM;
- rc = qeth_l3_send_setassparms(card, iob,
+ rc = qeth_send_setassparms(card, iob,
sizeof(struct qeth_arp_cache_entry),
(unsigned long) entry,
qeth_l3_default_setassparms_cb, NULL);
@@ -2596,7 +2486,7 @@ static int qeth_l3_arp_remove_entry(struct qeth_card *card,
QETH_PROT_IPV4);
if (!iob)
return -ENOMEM;
- rc = qeth_l3_send_setassparms(card, iob,
+ rc = qeth_send_setassparms(card, iob,
12, (unsigned long)buf,
qeth_l3_default_setassparms_cb, NULL);
if (rc) {
@@ -2627,7 +2517,7 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card)
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+ rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
IPA_CMD_ASS_ARP_FLUSH_CACHE, 0);
if (rc) {
tmp = rc;
@@ -3190,7 +3080,6 @@ static netdev_features_t qeth_l3_fix_features(struct net_device *dev,
features &= ~NETIF_F_TSO;
if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
features &= ~NETIF_F_RXCSUM;
-
return features;
}
@@ -3198,8 +3087,7 @@ static int qeth_l3_set_features(struct net_device *dev,
netdev_features_t features)
{
struct qeth_card *card = dev->ml_priv;
- u32 changed = dev->features ^ features;
- int err;
+ netdev_features_t changed = dev->features ^ features;
if (!(changed & NETIF_F_RXCSUM))
return 0;
@@ -3208,11 +3096,7 @@ static int qeth_l3_set_features(struct net_device *dev,
card->state == CARD_STATE_RECOVER)
return 0;
- err = qeth_l3_set_rx_csum(card, features & NETIF_F_RXCSUM);
- if (err)
- dev->features = features ^ NETIF_F_RXCSUM;
-
- return err;
+ return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0);
}
static const struct ethtool_ops qeth_l3_ethtool_ops = {
diff --git a/kernel/drivers/s390/net/smsgiucv.c b/kernel/drivers/s390/net/smsgiucv.c
index d8f990b6b..a851d34c6 100644
--- a/kernel/drivers/s390/net/smsgiucv.c
+++ b/kernel/drivers/s390/net/smsgiucv.c
@@ -49,7 +49,7 @@ static DEFINE_SPINLOCK(smsg_list_lock);
static LIST_HEAD(smsg_list);
static int iucv_path_connected;
-static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]);
+static int smsg_path_pending(struct iucv_path *, u8 *, u8 *);
static void smsg_message_pending(struct iucv_path *, struct iucv_message *);
static struct iucv_handler smsg_handler = {
@@ -57,8 +57,7 @@ static struct iucv_handler smsg_handler = {
.message_pending = smsg_message_pending,
};
-static int smsg_path_pending(struct iucv_path *path, u8 ipvmid[8],
- u8 ipuser[16])
+static int smsg_path_pending(struct iucv_path *path, u8 *ipvmid, u8 *ipuser)
{
if (strncmp(ipvmid, "*MSG ", 8) != 0)
return -EINVAL;
diff --git a/kernel/drivers/s390/scsi/zfcp_aux.c b/kernel/drivers/s390/scsi/zfcp_aux.c
index 01a73395a..c00ac4650 100644
--- a/kernel/drivers/s390/scsi/zfcp_aux.c
+++ b/kernel/drivers/s390/scsi/zfcp_aux.c
@@ -529,7 +529,7 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
list_add_tail(&port->list, &adapter->port_list);
write_unlock_irq(&adapter->port_list_lock);
- atomic_set_mask(status | ZFCP_STATUS_COMMON_RUNNING, &port->status);
+ atomic_or(status | ZFCP_STATUS_COMMON_RUNNING, &port->status);
return port;
diff --git a/kernel/drivers/s390/scsi/zfcp_erp.c b/kernel/drivers/s390/scsi/zfcp_erp.c
index acde3f5d6..3fb410977 100644
--- a/kernel/drivers/s390/scsi/zfcp_erp.c
+++ b/kernel/drivers/s390/scsi/zfcp_erp.c
@@ -190,7 +190,7 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status,
if (!(act_status & ZFCP_STATUS_ERP_NO_REF))
if (scsi_device_get(sdev))
return NULL;
- atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+ atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE,
&zfcp_sdev->status);
erp_action = &zfcp_sdev->erp_action;
memset(erp_action, 0, sizeof(struct zfcp_erp_action));
@@ -206,7 +206,7 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status,
if (!get_device(&port->dev))
return NULL;
zfcp_erp_action_dismiss_port(port);
- atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
+ atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
erp_action = &port->erp_action;
memset(erp_action, 0, sizeof(struct zfcp_erp_action));
erp_action->port = port;
@@ -217,7 +217,7 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status,
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
kref_get(&adapter->ref);
zfcp_erp_action_dismiss_adapter(adapter);
- atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
+ atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
erp_action = &adapter->erp_action;
memset(erp_action, 0, sizeof(struct zfcp_erp_action));
if (!(atomic_read(&adapter->status) &
@@ -254,7 +254,7 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
act = zfcp_erp_setup_act(need, act_status, adapter, port, sdev);
if (!act)
goto out;
- atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
+ atomic_or(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
++adapter->erp_total_count;
list_add_tail(&act->list, &adapter->erp_ready_head);
wake_up(&adapter->erp_ready_wq);
@@ -486,14 +486,14 @@ static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
{
if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status))
zfcp_dbf_rec_run("eraubl1", &adapter->erp_action);
- atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
+ atomic_or(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
}
static void zfcp_erp_port_unblock(struct zfcp_port *port)
{
if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status))
zfcp_dbf_rec_run("erpubl1", &port->erp_action);
- atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
+ atomic_or(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
}
static void zfcp_erp_lun_unblock(struct scsi_device *sdev)
@@ -502,7 +502,7 @@ static void zfcp_erp_lun_unblock(struct scsi_device *sdev)
if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status))
zfcp_dbf_rec_run("erlubl1", &sdev_to_zfcp(sdev)->erp_action);
- atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status);
+ atomic_or(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status);
}
static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
@@ -642,7 +642,7 @@ static void zfcp_erp_wakeup(struct zfcp_adapter *adapter)
read_lock_irqsave(&adapter->erp_lock, flags);
if (list_empty(&adapter->erp_ready_head) &&
list_empty(&adapter->erp_running_head)) {
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
+ atomic_andnot(ZFCP_STATUS_ADAPTER_ERP_PENDING,
&adapter->status);
wake_up(&adapter->erp_done_wqh);
}
@@ -665,16 +665,16 @@ static int zfcp_erp_adapter_strat_fsf_xconf(struct zfcp_erp_action *erp_action)
int sleep = 1;
struct zfcp_adapter *adapter = erp_action->adapter;
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
+ atomic_andnot(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
for (retries = 7; retries; retries--) {
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+ atomic_andnot(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
&adapter->status);
write_lock_irq(&adapter->erp_lock);
zfcp_erp_action_to_running(erp_action);
write_unlock_irq(&adapter->erp_lock);
if (zfcp_fsf_exchange_config_data(erp_action)) {
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+ atomic_andnot(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
&adapter->status);
return ZFCP_ERP_FAILED;
}
@@ -692,7 +692,7 @@ static int zfcp_erp_adapter_strat_fsf_xconf(struct zfcp_erp_action *erp_action)
sleep *= 2;
}
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+ atomic_andnot(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
&adapter->status);
if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_XCONFIG_OK))
@@ -764,7 +764,7 @@ static void zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *act)
/* all ports and LUNs are closed */
zfcp_erp_clear_adapter_status(adapter, ZFCP_STATUS_COMMON_OPEN);
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
+ atomic_andnot(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
}
@@ -773,7 +773,7 @@ static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *act)
struct zfcp_adapter *adapter = act->adapter;
if (zfcp_qdio_open(adapter->qdio)) {
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
+ atomic_andnot(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
&adapter->status);
return ZFCP_ERP_FAILED;
@@ -784,7 +784,7 @@ static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *act)
return ZFCP_ERP_FAILED;
}
- atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &adapter->status);
+ atomic_or(ZFCP_STATUS_COMMON_OPEN, &adapter->status);
return ZFCP_ERP_SUCCEEDED;
}
@@ -948,7 +948,7 @@ static void zfcp_erp_lun_strategy_clearstati(struct scsi_device *sdev)
{
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
- atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED,
+ atomic_andnot(ZFCP_STATUS_COMMON_ACCESS_DENIED,
&zfcp_sdev->status);
}
@@ -1187,18 +1187,18 @@ static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
switch (erp_action->action) {
case ZFCP_ERP_ACTION_REOPEN_LUN:
zfcp_sdev = sdev_to_zfcp(erp_action->sdev);
- atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+ atomic_andnot(ZFCP_STATUS_COMMON_ERP_INUSE,
&zfcp_sdev->status);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
case ZFCP_ERP_ACTION_REOPEN_PORT:
- atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+ atomic_andnot(ZFCP_STATUS_COMMON_ERP_INUSE,
&erp_action->port->status);
break;
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+ atomic_andnot(ZFCP_STATUS_COMMON_ERP_INUSE,
&erp_action->adapter->status);
break;
}
@@ -1422,19 +1422,19 @@ void zfcp_erp_set_adapter_status(struct zfcp_adapter *adapter, u32 mask)
unsigned long flags;
u32 common_mask = mask & ZFCP_COMMON_FLAGS;
- atomic_set_mask(mask, &adapter->status);
+ atomic_or(mask, &adapter->status);
if (!common_mask)
return;
read_lock_irqsave(&adapter->port_list_lock, flags);
list_for_each_entry(port, &adapter->port_list, list)
- atomic_set_mask(common_mask, &port->status);
+ atomic_or(common_mask, &port->status);
read_unlock_irqrestore(&adapter->port_list_lock, flags);
spin_lock_irqsave(adapter->scsi_host->host_lock, flags);
__shost_for_each_device(sdev, adapter->scsi_host)
- atomic_set_mask(common_mask, &sdev_to_zfcp(sdev)->status);
+ atomic_or(common_mask, &sdev_to_zfcp(sdev)->status);
spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags);
}
@@ -1453,7 +1453,7 @@ void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask)
u32 common_mask = mask & ZFCP_COMMON_FLAGS;
u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED;
- atomic_clear_mask(mask, &adapter->status);
+ atomic_andnot(mask, &adapter->status);
if (!common_mask)
return;
@@ -1463,7 +1463,7 @@ void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask)
read_lock_irqsave(&adapter->port_list_lock, flags);
list_for_each_entry(port, &adapter->port_list, list) {
- atomic_clear_mask(common_mask, &port->status);
+ atomic_andnot(common_mask, &port->status);
if (clear_counter)
atomic_set(&port->erp_counter, 0);
}
@@ -1471,7 +1471,7 @@ void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask)
spin_lock_irqsave(adapter->scsi_host->host_lock, flags);
__shost_for_each_device(sdev, adapter->scsi_host) {
- atomic_clear_mask(common_mask, &sdev_to_zfcp(sdev)->status);
+ atomic_andnot(common_mask, &sdev_to_zfcp(sdev)->status);
if (clear_counter)
atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0);
}
@@ -1491,7 +1491,7 @@ void zfcp_erp_set_port_status(struct zfcp_port *port, u32 mask)
u32 common_mask = mask & ZFCP_COMMON_FLAGS;
unsigned long flags;
- atomic_set_mask(mask, &port->status);
+ atomic_or(mask, &port->status);
if (!common_mask)
return;
@@ -1499,7 +1499,7 @@ void zfcp_erp_set_port_status(struct zfcp_port *port, u32 mask)
spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags);
__shost_for_each_device(sdev, port->adapter->scsi_host)
if (sdev_to_zfcp(sdev)->port == port)
- atomic_set_mask(common_mask,
+ atomic_or(common_mask,
&sdev_to_zfcp(sdev)->status);
spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags);
}
@@ -1518,7 +1518,7 @@ void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask)
u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED;
unsigned long flags;
- atomic_clear_mask(mask, &port->status);
+ atomic_andnot(mask, &port->status);
if (!common_mask)
return;
@@ -1529,7 +1529,7 @@ void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask)
spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags);
__shost_for_each_device(sdev, port->adapter->scsi_host)
if (sdev_to_zfcp(sdev)->port == port) {
- atomic_clear_mask(common_mask,
+ atomic_andnot(common_mask,
&sdev_to_zfcp(sdev)->status);
if (clear_counter)
atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0);
@@ -1546,7 +1546,7 @@ void zfcp_erp_set_lun_status(struct scsi_device *sdev, u32 mask)
{
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
- atomic_set_mask(mask, &zfcp_sdev->status);
+ atomic_or(mask, &zfcp_sdev->status);
}
/**
@@ -1558,7 +1558,7 @@ void zfcp_erp_clear_lun_status(struct scsi_device *sdev, u32 mask)
{
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
- atomic_clear_mask(mask, &zfcp_sdev->status);
+ atomic_andnot(mask, &zfcp_sdev->status);
if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
atomic_set(&zfcp_sdev->erp_counter, 0);
diff --git a/kernel/drivers/s390/scsi/zfcp_fc.c b/kernel/drivers/s390/scsi/zfcp_fc.c
index 25d49f32c..237688af1 100644
--- a/kernel/drivers/s390/scsi/zfcp_fc.c
+++ b/kernel/drivers/s390/scsi/zfcp_fc.c
@@ -508,7 +508,7 @@ static void zfcp_fc_adisc_handler(void *data)
/* port is good, unblock rport without going through erp */
zfcp_scsi_schedule_rport_register(port);
out:
- atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
+ atomic_andnot(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
put_device(&port->dev);
kmem_cache_free(zfcp_fc_req_cache, fc_req);
}
@@ -564,14 +564,14 @@ void zfcp_fc_link_test_work(struct work_struct *work)
if (atomic_read(&port->status) & ZFCP_STATUS_PORT_LINK_TEST)
goto out;
- atomic_set_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
+ atomic_or(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
retval = zfcp_fc_adisc(port);
if (retval == 0)
return;
/* send of ADISC was not possible */
- atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
+ atomic_andnot(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
zfcp_erp_port_forced_reopen(port, 0, "fcltwk1");
out:
@@ -640,7 +640,7 @@ static void zfcp_fc_validate_port(struct zfcp_port *port, struct list_head *lh)
if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC))
return;
- atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status);
+ atomic_andnot(ZFCP_STATUS_COMMON_NOESC, &port->status);
if ((port->supported_classes != 0) ||
!list_empty(&port->unit_list))
diff --git a/kernel/drivers/s390/scsi/zfcp_fsf.c b/kernel/drivers/s390/scsi/zfcp_fsf.c
index 21ec5e2f5..522a633c8 100644
--- a/kernel/drivers/s390/scsi/zfcp_fsf.c
+++ b/kernel/drivers/s390/scsi/zfcp_fsf.c
@@ -114,7 +114,7 @@ static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req,
if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED)
return;
- atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
+ atomic_or(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
zfcp_scsi_schedule_rports_block(adapter);
@@ -204,7 +204,7 @@ static void zfcp_fsf_status_read_link_down(struct zfcp_fsf_req *req)
break;
case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE:
zfcp_fsf_link_down_info_eval(req, NULL);
- };
+ }
}
static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
@@ -345,7 +345,7 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
zfcp_erp_adapter_shutdown(adapter, 0, "fspse_3");
break;
case FSF_PROT_HOST_CONNECTION_INITIALIZING:
- atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+ atomic_or(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
&adapter->status);
break;
case FSF_PROT_DUPLICATE_REQUEST_ID:
@@ -554,7 +554,7 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req)
zfcp_erp_adapter_shutdown(adapter, 0, "fsecdh1");
return;
}
- atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
+ atomic_or(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
&adapter->status);
break;
case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
@@ -567,7 +567,7 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req)
/* avoids adapter shutdown to be able to recognize
* events such as LINK UP */
- atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
+ atomic_or(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
&adapter->status);
zfcp_fsf_link_down_info_eval(req,
&qtcb->header.fsf_status_qual.link_down_info);
@@ -1394,9 +1394,9 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
break;
case FSF_GOOD:
port->handle = header->port_handle;
- atomic_set_mask(ZFCP_STATUS_COMMON_OPEN |
+ atomic_or(ZFCP_STATUS_COMMON_OPEN |
ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
- atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_BOXED,
+ atomic_andnot(ZFCP_STATUS_COMMON_ACCESS_BOXED,
&port->status);
/* check whether D_ID has changed during open */
/*
@@ -1677,10 +1677,10 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
case FSF_PORT_BOXED:
/* can't use generic zfcp_erp_modify_port_status because
* ZFCP_STATUS_COMMON_OPEN must not be reset for the port */
- atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
+ atomic_andnot(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
shost_for_each_device(sdev, port->adapter->scsi_host)
if (sdev_to_zfcp(sdev)->port == port)
- atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
+ atomic_andnot(ZFCP_STATUS_COMMON_OPEN,
&sdev_to_zfcp(sdev)->status);
zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ACCESS_BOXED);
zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED,
@@ -1700,10 +1700,10 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
/* can't use generic zfcp_erp_modify_port_status because
* ZFCP_STATUS_COMMON_OPEN must not be reset for the port
*/
- atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
+ atomic_andnot(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
shost_for_each_device(sdev, port->adapter->scsi_host)
if (sdev_to_zfcp(sdev)->port == port)
- atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
+ atomic_andnot(ZFCP_STATUS_COMMON_OPEN,
&sdev_to_zfcp(sdev)->status);
break;
}
@@ -1766,7 +1766,7 @@ static void zfcp_fsf_open_lun_handler(struct zfcp_fsf_req *req)
zfcp_sdev = sdev_to_zfcp(sdev);
- atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
+ atomic_andnot(ZFCP_STATUS_COMMON_ACCESS_DENIED |
ZFCP_STATUS_COMMON_ACCESS_BOXED,
&zfcp_sdev->status);
@@ -1822,7 +1822,7 @@ static void zfcp_fsf_open_lun_handler(struct zfcp_fsf_req *req)
case FSF_GOOD:
zfcp_sdev->lun_handle = header->lun_handle;
- atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &zfcp_sdev->status);
+ atomic_or(ZFCP_STATUS_COMMON_OPEN, &zfcp_sdev->status);
break;
}
}
@@ -1913,7 +1913,7 @@ static void zfcp_fsf_close_lun_handler(struct zfcp_fsf_req *req)
}
break;
case FSF_GOOD:
- atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &zfcp_sdev->status);
+ atomic_andnot(ZFCP_STATUS_COMMON_OPEN, &zfcp_sdev->status);
break;
}
}
diff --git a/kernel/drivers/s390/scsi/zfcp_qdio.c b/kernel/drivers/s390/scsi/zfcp_qdio.c
index 495e1cb3a..dbf2b5470 100644
--- a/kernel/drivers/s390/scsi/zfcp_qdio.c
+++ b/kernel/drivers/s390/scsi/zfcp_qdio.c
@@ -349,7 +349,7 @@ void zfcp_qdio_close(struct zfcp_qdio *qdio)
/* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */
spin_lock_irq(&qdio->req_q_lock);
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
+ atomic_andnot(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
spin_unlock_irq(&qdio->req_q_lock);
wake_up(&qdio->req_q_wq);
@@ -384,7 +384,7 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio)
if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)
return -EIO;
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_SIOSL_ISSUED,
+ atomic_andnot(ZFCP_STATUS_ADAPTER_SIOSL_ISSUED,
&qdio->adapter->status);
zfcp_qdio_setup_init_data(&init_data, qdio);
@@ -396,14 +396,14 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio)
goto failed_qdio;
if (ssqd.qdioac2 & CHSC_AC2_DATA_DIV_ENABLED)
- atomic_set_mask(ZFCP_STATUS_ADAPTER_DATA_DIV_ENABLED,
+ atomic_or(ZFCP_STATUS_ADAPTER_DATA_DIV_ENABLED,
&qdio->adapter->status);
if (ssqd.qdioac2 & CHSC_AC2_MULTI_BUFFER_ENABLED) {
- atomic_set_mask(ZFCP_STATUS_ADAPTER_MB_ACT, &adapter->status);
+ atomic_or(ZFCP_STATUS_ADAPTER_MB_ACT, &adapter->status);
qdio->max_sbale_per_sbal = QDIO_MAX_ELEMENTS_PER_BUFFER;
} else {
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_MB_ACT, &adapter->status);
+ atomic_andnot(ZFCP_STATUS_ADAPTER_MB_ACT, &adapter->status);
qdio->max_sbale_per_sbal = QDIO_MAX_ELEMENTS_PER_BUFFER - 1;
}
@@ -427,7 +427,7 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio)
/* set index of first available SBALS / number of available SBALS */
qdio->req_q_idx = 0;
atomic_set(&qdio->req_q_free, QDIO_MAX_BUFFERS_PER_Q);
- atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &qdio->adapter->status);
+ atomic_or(ZFCP_STATUS_ADAPTER_QDIOUP, &qdio->adapter->status);
if (adapter->scsi_host) {
adapter->scsi_host->sg_tablesize = qdio->max_sbale_per_req;
@@ -499,6 +499,6 @@ void zfcp_qdio_siosl(struct zfcp_adapter *adapter)
rc = ccw_device_siosl(adapter->ccw_device);
if (!rc)
- atomic_set_mask(ZFCP_STATUS_ADAPTER_SIOSL_ISSUED,
+ atomic_or(ZFCP_STATUS_ADAPTER_SIOSL_ISSUED,
&adapter->status);
}
diff --git a/kernel/drivers/s390/scsi/zfcp_scsi.c b/kernel/drivers/s390/scsi/zfcp_scsi.c
index 75f4bfc2b..b3c6ff491 100644
--- a/kernel/drivers/s390/scsi/zfcp_scsi.c
+++ b/kernel/drivers/s390/scsi/zfcp_scsi.c
@@ -297,7 +297,6 @@ static struct scsi_host_template zfcp_scsi_host_template = {
* ZFCP_QDIO_MAX_SBALS_PER_REQ) - 2) * 8,
/* GCD, adjusted later */
.dma_boundary = ZFCP_QDIO_SBALE_LEN - 1,
- .cmd_per_lun = 1,
.use_clustering = 1,
.shost_attrs = zfcp_sysfs_shost_attrs,
.sdev_attrs = zfcp_sysfs_sdev_attrs,
diff --git a/kernel/drivers/s390/kvm/Makefile b/kernel/drivers/s390/virtio/Makefile
index 241891a57..241891a57 100644
--- a/kernel/drivers/s390/kvm/Makefile
+++ b/kernel/drivers/s390/virtio/Makefile
diff --git a/kernel/drivers/s390/kvm/kvm_virtio.c b/kernel/drivers/s390/virtio/kvm_virtio.c
index dd65c8b4c..53fb975c4 100644
--- a/kernel/drivers/s390/kvm/kvm_virtio.c
+++ b/kernel/drivers/s390/virtio/kvm_virtio.c
@@ -450,7 +450,7 @@ static int __init test_devices_support(unsigned long addr)
static int __init kvm_devices_init(void)
{
int rc;
- unsigned long total_memory_size = sclp_get_rzm() * sclp_get_rnmax();
+ unsigned long total_memory_size = sclp.rzm * sclp.rnmax;
if (!MACHINE_IS_KVM)
return -ENODEV;
@@ -497,7 +497,7 @@ static __init int early_put_chars(u32 vtermno, const char *buf, int count)
static int __init s390_virtio_console_init(void)
{
- if (sclp_has_vt220() || sclp_has_linemode())
+ if (sclp.has_vt220 || sclp.has_linemode)
return -ENODEV;
return virtio_cons_early_init(early_put_chars);
}
diff --git a/kernel/drivers/s390/kvm/virtio_ccw.c b/kernel/drivers/s390/virtio/virtio_ccw.c
index f8d8fdb26..1b831598d 100644
--- a/kernel/drivers/s390/kvm/virtio_ccw.c
+++ b/kernel/drivers/s390/virtio/virtio_ccw.c
@@ -28,6 +28,7 @@
#include <linux/io.h>
#include <linux/kvm_para.h>
#include <linux/notifier.h>
+#include <asm/diag.h>
#include <asm/setup.h>
#include <asm/irq.h>
#include <asm/cio.h>
@@ -366,9 +367,9 @@ static void virtio_ccw_drop_indicator(struct virtio_ccw_device *vcdev,
kfree(thinint_area);
}
-static inline long do_kvm_notify(struct subchannel_id schid,
- unsigned long queue_index,
- long cookie)
+static inline long __do_kvm_notify(struct subchannel_id schid,
+ unsigned long queue_index,
+ long cookie)
{
register unsigned long __nr asm("1") = KVM_S390_VIRTIO_CCW_NOTIFY;
register struct subchannel_id __schid asm("2") = schid;
@@ -383,6 +384,14 @@ static inline long do_kvm_notify(struct subchannel_id schid,
return __rc;
}
+static inline long do_kvm_notify(struct subchannel_id schid,
+ unsigned long queue_index,
+ long cookie)
+{
+ diag_stat_inc(DIAG_STAT_X500);
+ return __do_kvm_notify(schid, queue_index, cookie);
+}
+
static bool virtio_ccw_kvm_notify(struct virtqueue *vq)
{
struct virtio_ccw_vq_info *info = vq->priv;
@@ -400,12 +409,16 @@ static bool virtio_ccw_kvm_notify(struct virtqueue *vq)
static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev,
struct ccw1 *ccw, int index)
{
+ int ret;
+
vcdev->config_block->index = index;
ccw->cmd_code = CCW_CMD_READ_VQ_CONF;
ccw->flags = 0;
ccw->count = sizeof(struct vq_config_block);
ccw->cda = (__u32)(unsigned long)(vcdev->config_block);
- ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF);
+ ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF);
+ if (ret)
+ return ret;
return vcdev->config_block->num;
}
@@ -503,6 +516,10 @@ static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev,
goto out_err;
}
info->num = virtio_ccw_read_vq_conf(vcdev, ccw, i);
+ if (info->num < 0) {
+ err = info->num;
+ goto out_err;
+ }
size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN));
info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
if (info->queue == NULL) {
@@ -967,6 +984,36 @@ static struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev,
return vq;
}
+static void virtio_ccw_check_activity(struct virtio_ccw_device *vcdev,
+ __u32 activity)
+{
+ if (vcdev->curr_io & activity) {
+ switch (activity) {
+ case VIRTIO_CCW_DOING_READ_FEAT:
+ case VIRTIO_CCW_DOING_WRITE_FEAT:
+ case VIRTIO_CCW_DOING_READ_CONFIG:
+ case VIRTIO_CCW_DOING_WRITE_CONFIG:
+ case VIRTIO_CCW_DOING_WRITE_STATUS:
+ case VIRTIO_CCW_DOING_SET_VQ:
+ case VIRTIO_CCW_DOING_SET_IND:
+ case VIRTIO_CCW_DOING_SET_CONF_IND:
+ case VIRTIO_CCW_DOING_RESET:
+ case VIRTIO_CCW_DOING_READ_VQ_CONF:
+ case VIRTIO_CCW_DOING_SET_IND_ADAPTER:
+ case VIRTIO_CCW_DOING_SET_VIRTIO_REV:
+ vcdev->curr_io &= ~activity;
+ wake_up(&vcdev->wait_q);
+ break;
+ default:
+ /* don't know what to do... */
+ dev_warn(&vcdev->cdev->dev,
+ "Suspicious activity '%08x'\n", activity);
+ WARN_ON(1);
+ break;
+ }
+ }
+}
+
static void virtio_ccw_int_handler(struct ccw_device *cdev,
unsigned long intparm,
struct irb *irb)
@@ -978,6 +1025,12 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
if (!vcdev)
return;
+ if (IS_ERR(irb)) {
+ vcdev->err = PTR_ERR(irb);
+ virtio_ccw_check_activity(vcdev, activity);
+ /* Don't poke around indicators, something's wrong. */
+ return;
+ }
/* Check if it's a notification from the host. */
if ((intparm == 0) &&
(scsw_stctl(&irb->scsw) ==
@@ -993,31 +1046,7 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
/* Map everything else to -EIO. */
vcdev->err = -EIO;
}
- if (vcdev->curr_io & activity) {
- switch (activity) {
- case VIRTIO_CCW_DOING_READ_FEAT:
- case VIRTIO_CCW_DOING_WRITE_FEAT:
- case VIRTIO_CCW_DOING_READ_CONFIG:
- case VIRTIO_CCW_DOING_WRITE_CONFIG:
- case VIRTIO_CCW_DOING_WRITE_STATUS:
- case VIRTIO_CCW_DOING_SET_VQ:
- case VIRTIO_CCW_DOING_SET_IND:
- case VIRTIO_CCW_DOING_SET_CONF_IND:
- case VIRTIO_CCW_DOING_RESET:
- case VIRTIO_CCW_DOING_READ_VQ_CONF:
- case VIRTIO_CCW_DOING_SET_IND_ADAPTER:
- case VIRTIO_CCW_DOING_SET_VIRTIO_REV:
- vcdev->curr_io &= ~activity;
- wake_up(&vcdev->wait_q);
- break;
- default:
- /* don't know what to do... */
- dev_warn(&cdev->dev, "Suspicious activity '%08x'\n",
- activity);
- WARN_ON(1);
- break;
- }
- }
+ virtio_ccw_check_activity(vcdev, activity);
for_each_set_bit(i, &vcdev->indicators,
sizeof(vcdev->indicators) * BITS_PER_BYTE) {
/* The bit clear must happen before the vring kick. */