From 1330a1258dc20caa5961b07aa62482793d6f2ecf Mon Sep 17 00:00:00 2001 From: Ingo Tuchscherer Date: Fri, 4 Sep 2015 09:57:24 +0200 Subject: s390/zcrypt: enable odd RSA modulus sizes in CRT format In the past only even modulus sizes were allowed for RSA keys in CRT format. This restriction was based on limited RSA key generation on older crypto adapters that provides only even modulus sizes. This restriction is not valid any more. Revoke restrictions that crypto requests can be serviced with odd RSA modulus length in CRT format. Signed-off-by: Ingo Tuchscherer Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_api.c | 3 +-- drivers/s390/crypto/zcrypt_cca_key.h | 2 +- drivers/s390/crypto/zcrypt_msgtype50.c | 2 +- drivers/s390/crypto/zcrypt_pcica.c | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 4eb45546a3aa..a9603ebbc1f8 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -472,8 +472,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 diff --git a/drivers/s390/crypto/zcrypt_cca_key.h b/drivers/s390/crypto/zcrypt_cca_key.h index 1f42f103c761..ca0cdbe46368 100644 --- a/drivers/s390/crypto/zcrypt_cca_key.h +++ b/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/drivers/s390/crypto/zcrypt_msgtype50.c b/drivers/s390/crypto/zcrypt_msgtype50.c index 334e282f255b..6c9af1332967 100644 --- a/drivers/s390/crypto/zcrypt_msgtype50.c +++ b/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 diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c index 7a743f4c646c..2f220208da63 100644 --- a/drivers/s390/crypto/zcrypt_pcica.c +++ b/drivers/s390/crypto/zcrypt_pcica.c @@ -138,7 +138,7 @@ static int ICACRT_msg_to_type4CRT_msg(struct zcrypt_device *zdev, int mod_len, short_len, long_len; mod_len = crt->inputdatalength; - short_len = mod_len / 2; + short_len = (mod_len + 1) / 2; long_len = mod_len / 2 + 8; if (mod_len <= 128) { -- cgit v1.2.3 From 9a21268360f5438796fa935e0b2e9317187ef8f4 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Fri, 4 Sep 2015 13:52:44 +0200 Subject: s390/dasd: mark DASD devices as non rotational We were able to reduce the CPU overhead of big paging scenarios when announcing our paging disks as non-rotational. Almost all dasd devices are implemented in storage servers with cache, raid, striping and lots of magic. There is no point in optimizing the disk schedulers and swap code for a single platter moving arm rotational disks. Given the complexity of the setup and the fact that this change is mostly to disable the additional overhead in swap code, lets keep the other functionality unchanged and do not disable the this device as entropy source - unlike other non-rotational devices. Suggested-by: Christian Ehrhardt Reviewed-by: David Hildenbrand Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index f73d2f579a7e..a263c10359e1 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -3030,6 +3030,7 @@ 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); blk_queue_logical_block_size(block->request_queue, block->bp_block); blk_queue_max_hw_sectors(block->request_queue, max); -- cgit v1.2.3 From 279b8f9a0f3ea3399764047d487dfdd8f7bc9795 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Sep 2015 19:50:25 +0200 Subject: s390/cio: cleanup cmb_enable sysfs attr Ensure that we hold the ccwlock when accessing private data. Return errors that occur during measurement enabling to userspace. Apply some cleanups while at it. Signed-off-by: Sebastian Ott Reviewed-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cmf.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 23054f8fa9fc..3543c486dcdc 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -1171,23 +1171,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 +1200,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) { -- cgit v1.2.3 From 1bc6664bdfb949bc69a08113801e7d6acbf6bc3f Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 15 Sep 2015 13:11:42 +0200 Subject: s390/cio: use device_lock during cmb activation Hold the device_lock during [de]activation of the channel measurement block to synchronize concurrent usage of these functions. Signed-off-by: Sebastian Ott Reviewed-by: Martin Schwidefsky Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cmf.c | 49 +++++++++++++++++++++++++++++++++++------------ drivers/s390/cio/device.c | 2 +- 2 files changed, 38 insertions(+), 13 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 3543c486dcdc..5eeb62c3f33a 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -1226,41 +1226,66 @@ int enable_cmf(struct ccw_device *cdev) { int ret; + device_lock(&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: + 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); + + 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; } diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index dfef5e63cb7b..20b92c703944 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1797,7 +1797,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) -- cgit v1.2.3 From a6ef15652d260f754ead223d0c55434a3a39fe1d Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Sep 2015 19:51:39 +0200 Subject: s390/cio: fix use after free in cmb processing Devices with active channel measurement are included in a list. When a device is removed without deactivating channel measurement first the list_head is freed but still used. Fix this by making sure that channel measurement is deactivated during device deregistration. For devices that we deregister because they are no longer accessible deactivating channel measurement will fail. In this case we can report success because the FW will no longer access the measurement block. In addition to these steps keep an extra device reference while channel measurement is active. Signed-off-by: Sebastian Ott Reviewed-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cmf.c | 27 ++++++++++++++++----------- drivers/s390/cio/device.c | 2 ++ 2 files changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 5eeb62c3f33a..31677c075a8e 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -186,9 +186,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 +197,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 { @@ -606,12 +613,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 +627,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); } @@ -1227,6 +1227,7 @@ int enable_cmf(struct ccw_device *cdev) int ret; device_lock(&cdev->dev); + get_device(&cdev->dev); ret = cmbops->alloc(cdev); if (ret) goto out; @@ -1242,6 +1243,9 @@ int enable_cmf(struct ccw_device *cdev) cmbops->free(cdev); } out: + if (ret) + put_device(&cdev->dev); + device_unlock(&cdev->dev); return ret; } @@ -1265,6 +1269,7 @@ int __disable_cmf(struct ccw_device *cdev) sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group); cmbops->free(cdev); + put_device(&cdev->dev); return ret; } diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 20b92c703944..6aae68412802 100644 --- a/drivers/s390/cio/device.c +++ b/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; } -- cgit v1.2.3 From 616503d1d8940049841e5b6f2ab5157f37072ed9 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Sep 2015 19:52:06 +0200 Subject: s390/cio: improve locking during cmbe allocation During allocation of extended measurement blocks we check if the device is already active for channel measurement and add the device to a list of devices with active channel measurement. The check is done under ccwlock protection and the list modification is guarded by a different lock. To guarantee that both states are in sync make sure that both locks are held during the allocation process (like it's already done for the "normal" measurement block allocation). Signed-off-by: Sebastian Ott Reviewed-by: Martin Schwidefsky Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cmf.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 31677c075a8e..59b1ac24f992 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -821,42 +821,45 @@ static inline struct cmbe *cmbe_align(struct cmbe *c) 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); if (!cmbe) - return -ENOMEM; + return ret; + cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL); - if (!cmb_data) { - ret = -ENOMEM; + 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->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); @@ -869,19 +872,19 @@ 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) kfree(cmb_data->last_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); } -- cgit v1.2.3 From a5e9ca573b5fe47aef30e9c33c31b5fe7b0dfb88 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Sep 2015 19:52:31 +0200 Subject: s390/cio: fix memleak in channel measurement The measurement block for the extended measurement data is not freed when switching off per device measurement. Free the measurement block after HW stopped accessing it. Signed-off-by: Sebastian Ott Reviewed-by: Martin Schwidefsky Reviewed-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cmf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 59b1ac24f992..0d1898e51171 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -876,8 +876,10 @@ static void free_cmbe(struct ccw_device *cdev) 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); + kfree(cmb_data->hw_block); + } kfree(cmb_data); /* deactivate global measurement if this is the last channel */ -- cgit v1.2.3 From 45bf4b96e6aea81594e510fe5cd10190ff4e6cb1 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Sep 2015 19:53:01 +0200 Subject: s390/cio: use kmem_cache for extended measurement block allocation Extended measurement blocks need to be 64 byte aligned. To achieve that 128 bytes for each measurement block are allocated and an align callback returns a 64 byte aligned address inside this area. Replace this code with kmem_cache allocations. Signed-off-by: Sebastian Ott Reviewed-by: Martin Schwidefsky Reviewed-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cmf.c | 64 ++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 38 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 0d1898e51171..de7074fc6a2c 100644 --- a/drivers/s390/cio/cmf.c +++ b/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; }; @@ -321,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; @@ -432,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(); @@ -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,20 +796,9 @@ 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) { @@ -825,11 +806,11 @@ static int alloc_cmbe(struct ccw_device *cdev) struct cmbe *cmbe; int ret = -ENOMEM; - cmbe = kzalloc (sizeof (*cmbe) * 2, GFP_KERNEL); + cmbe = kmem_cache_zalloc(cmbe_cache, GFP_KERNEL); if (!cmbe) return ret; - cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL); + cmb_data = kzalloc(sizeof(*cmb_data), GFP_KERNEL); if (!cmb_data) goto out_free; @@ -837,7 +818,7 @@ static int alloc_cmbe(struct ccw_device *cdev) if (!cmb_data->last_block) goto out_free; - cmb_data->size = sizeof(struct cmbe); + cmb_data->size = sizeof(*cmbe); cmb_data->hw_block = cmbe; spin_lock(&cmb_area.lock); @@ -864,7 +845,8 @@ out_free: if (cmb_data) kfree(cmb_data->last_block); kfree(cmb_data); - kfree(cmbe); + kmem_cache_free(cmbe_cache, cmbe); + return ret; } @@ -878,7 +860,7 @@ static void free_cmbe(struct ccw_device *cdev) cdev->private->cmb = NULL; if (cmb_data) { kfree(cmb_data->last_block); - kfree(cmb_data->hw_block); + kmem_cache_free(cmbe_cache, cmb_data->hw_block); } kfree(cmb_data); @@ -902,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); @@ -1027,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 = { @@ -1041,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, }; @@ -1336,10 +1312,19 @@ int cmf_reenable(struct ccw_device *cdev) return cmbops->set(cdev, 2); } +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 @@ -1365,15 +1350,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); -- cgit v1.2.3 From ab97d211ef1863942a90d7c93dfd563f0381ca87 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Wed, 9 Sep 2015 10:29:59 +0200 Subject: s390/cio: reactivate cmf after hibernate During resume from hibernate we already reenable measurement block updates on a per device basis. In addition to that we also need to activate channel measurement globally using the set channel monitor instruction. Signed-off-by: Sebastian Ott Reviewed-by: Martin Schwidefsky Reviewed-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cmf.c | 13 +++++++++++++ drivers/s390/cio/css.c | 1 + drivers/s390/cio/device.h | 1 + 3 files changed, 15 insertions(+) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index de7074fc6a2c..b2afad5a5682 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -1312,6 +1312,19 @@ 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), diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 0268e5fd59b5..25b4ee1fb2f4 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -1089,6 +1089,7 @@ void channel_subsystem_reinit(void) if (chp) chp_update_desc(chp); } + cmf_reactivate(); } #ifdef CONFIG_PROC_FS diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 8d1d29873172..6b17119c9929 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -145,6 +145,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 -- cgit v1.2.3 From 9d49f86daba3ed22cadf01beff1b130ad25fe0bf Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 21 Sep 2015 18:40:33 +0200 Subject: s390/cio: introduce pathmask_to_pos We often need to correlate an 8 bit path mask with the position in a channel path array. Introduce and use pathmask_to_pos for that task. Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 11 ++++------- drivers/s390/cio/device_pgid.c | 4 ++-- 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 62a323539226..30c2a80c004b 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1068,8 +1068,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device) path_data->opm |= lpm; continue; /* no error */ } - /* translate path mask to position in mask */ - pos = 8 - ffs(lpm); + pos = pathmask_to_pos(lpm); kfree(private->path_conf_data[pos]); if ((__u8 *)private->path_conf_data[pos] == private->conf_data) { @@ -4671,7 +4670,7 @@ static struct dasd_conf_data *dasd_eckd_get_ref_conf(struct dasd_device *device, return conf_data; } out: - return private->path_conf_data[8 - ffs(lpum)]; + return private->path_conf_data[pathmask_to_pos(lpum)]; } /* @@ -4716,7 +4715,7 @@ static int dasd_eckd_cuir_scope(struct dasd_device *device, __u8 lpum, for (path = 0x80; path; path >>= 1) { /* initialise data per path */ bitmask = mask; - pos = 8 - ffs(path); + pos = pathmask_to_pos(path); conf_data = private->path_conf_data[pos]; pos = 8 - ffs(cuir->ned_map); ned = (char *) &conf_data->neds[pos]; @@ -4937,9 +4936,7 @@ static void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages, ((u64 *)cuir)[0], ((u64 *)cuir)[1], ((u64 *)cuir)[2], ((u32 *)cuir)[3]); 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 */ + pos = pathmask_to_pos(lpum); desc = ccw_device_get_chp_desc(device->cdev, pos); if (cuir->code == CUIR_QUIESCE) { diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 37ada05e82a5..148b3fadac00 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -133,7 +133,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 +434,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; -- cgit v1.2.3 From 1ec2772e0c3ca3159035c03165355e355efc326b Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 20 Aug 2015 17:28:44 +0200 Subject: s390/diag: add a statistic for diagnose calls Introduce /sys/debug/kernel/diag_stat with a statistic how many diagnose calls have been done by each CPU in the system. Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_diag.c | 2 ++ drivers/s390/char/diag_ftp.c | 2 ++ drivers/s390/virtio/virtio_ccw.c | 15 ++++++++++++--- 3 files changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index c062f1620c58..cb61f300f8b5 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -76,6 +77,7 @@ static inline int dia250(void *iob, int cmd) int rc; rc = 3; + diag_stat_inc(DIAG_STAT_X250); asm volatile( " diag 2,%2,0x250\n" "0: ipm %0\n" diff --git a/drivers/s390/char/diag_ftp.c b/drivers/s390/char/diag_ftp.c index 12db8db04cdd..a5ccbf6f0d36 100644 --- a/drivers/s390/char/diag_ftp.c +++ b/drivers/s390/char/diag_ftp.c @@ -15,6 +15,7 @@ #include #include #include +#include #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" diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c index e9fae30fafda..b2a1a81e6fc8 100644 --- a/drivers/s390/virtio/virtio_ccw.c +++ b/drivers/s390/virtio/virtio_ccw.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -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; -- cgit v1.2.3 From 18d1a7f675d7fb00d247eb858020da4bee199ddf Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 1 Oct 2015 14:11:35 +0200 Subject: s390/sclp: avoid merged message output The sclp console and tty code currently uses several message text objects in a single message event to print several lines with one SCCB. This causes the output of these lines to be fused into a block which is noticeable when selecting text in the operating system message panel. Instead use several message events with a single message text object each to print every line on its own. This changes the SCCB layout from struct sccb_header struct evbuf_header struct mdb_header struct go struct mto ... struct mto to struct sccb_header struct evbuf_header struct mdb_header struct go struct mto ... struct evbuf_header struct mdb_header struct go struct mto Reviewed-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp_rw.c | 136 +++++++++++++++++++++---------------------- drivers/s390/char/sclp_rw.h | 17 +++--- drivers/s390/char/sclp_tty.c | 8 +-- 3 files changed, 78 insertions(+), 83 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index 35a84af875ee..6010cd347a08 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/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/drivers/s390/char/sclp_rw.h b/drivers/s390/char/sclp_rw.h index 7a7bfc947d97..e3b0290995ba 100644 --- a/drivers/s390/char/sclp_rw.h +++ b/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/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 003663288e29..3c6e174e19b6 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/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; } -- cgit v1.2.3 From 6acbe21f473f5cff8a11546f090967459b63ab3e Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 26 Jun 2015 15:40:41 +0200 Subject: s390/zcrypt: remove duplicate low level functions ap_test_queue, ap_query_facilities, __ap_query_functions all use the same PQAP(TAPQ) command. Consolidate the three into a single ap_test_queue function that returns the AP status and the 64-bit result. The exception table entry for PQAP(TAPQ) can be avoided if the T bit for the APFT facility is set only if test_facility(15) indicated that the facility is present. Integrate ap_query_function into ap_query queue to avoid calling PQAP(TAPQ) twice. Reviewed-by: Ingo Tuchscherer Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 140 ++++++++++--------------------------------- drivers/s390/crypto/ap_bus.h | 9 --- 2 files changed, 32 insertions(+), 117 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index d78b3d629d78..613c92b22445 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -182,43 +182,26 @@ 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,25 +242,6 @@ 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) { register unsigned long reg0 asm ("0") = 0x04000000UL; @@ -296,42 +260,6 @@ static inline int __ap_query_configuration(struct ap_config_info *config) return reg1; } -/** - * 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 - */ -static int ap_query_functions(ap_qid_t qid, unsigned int *functions) -{ - struct ap_queue_status status; - - status = __ap_query_functions(qid, functions); - - if (ap_queue_status_invalid_test(&status)) - return -ENODEV; - - switch (status.response_code) { - case AP_RESPONSE_NORMAL: - 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_BUSY: - case AP_RESPONSE_OTHERWISE_CHANGED: - default: - return -EBUSY; - } -} - /** * ap_queue_enable_interruption(): Enable interruption on an AP. * @qid: The AP queue number @@ -515,17 +443,20 @@ static inline void ap_schedule_poll_timer(void) * @qid: The AP queue number * @queue_depth: Pointer to queue depth value * @device_type: Pointer to device type value + * @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; + unsigned long info; - status = ap_test_queue(qid, &t_depth, &t_device_type); + status = ap_test_queue(qid, &info); switch (status.response_code) { case AP_RESPONSE_NORMAL: - *queue_depth = t_depth + 1; - *device_type = t_device_type; + *queue_depth = (int)(info & 0xff); + *device_type = (int)((info >> 24) & 0xff); + *facilities = (unsigned int)(info >> 32); return 0; case AP_RESPONSE_Q_NOT_AVAIL: case AP_RESPONSE_DECONFIGURED: @@ -1184,6 +1115,7 @@ 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) { + struct ap_queue_status status; ap_qid_t qid; int i, nd, max_domain_id = -1; unsigned long fbits; @@ -1194,7 +1126,9 @@ static ssize_t ap_max_domain_id_show(struct bus_type *bus, char *buf) if (!ap_test_config_card_id(i)) continue; qid = AP_MKQID(i, ap_domain_index); - fbits = ap_query_facilities(qid); + status = ap_test_queue(qid, &fbits); + if (status.response_code != AP_RESPONSE_NORMAL) + continue; if (fbits & (1UL << 57)) { /* the N bit is 0, Nd field is filled */ nd = (int)((fbits & 0x00FF0000UL)>>16); @@ -1254,9 +1188,9 @@ static void ap_query_configuration(void) */ static int ap_select_domain(void) { - int queue_depth, device_type, count, max_count, best_domain; - ap_qid_t qid; - int rc, i, j; + int count, max_count, best_domain; + struct ap_queue_status status; + int i, j; /* IF APXA isn't installed, only 16 domains could be defined */ if (!ap_configuration->ap_extended && (ap_domain_index > 15)) @@ -1279,9 +1213,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++; } @@ -1438,7 +1371,8 @@ static void ap_scan_bus(struct work_struct *unused) (void *)(unsigned long)qid, __ap_scan_bus); if (ap_test_config_card_id(i)) - rc = ap_query_queue(qid, &queue_depth, &device_type); + rc = ap_query_queue(qid, &queue_depth, &device_type, + &device_functions); else rc = -ENODEV; if (dev) { @@ -1468,6 +1402,9 @@ static void ap_scan_bus(struct work_struct *unused) continue; } ap_dev->queue_depth = queue_depth; + ap_dev->raw_hwtype = device_type; + ap_dev->device_type = device_type; + ap_dev->functions = device_functions; ap_dev->unregistered = 1; spin_lock_init(&ap_dev->lock); INIT_LIST_HEAD(&ap_dev->pendingq); @@ -1475,24 +1412,12 @@ static void ap_scan_bus(struct work_struct *unused) INIT_LIST_HEAD(&ap_dev->list); setup_timer(&ap_dev->timeout, ap_request_timeout, (unsigned long) ap_dev); - switch (device_type) { - case 0: + if (ap_dev->device_type == 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; @@ -1640,12 +1565,11 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) */ static inline int ap_poll_queue(struct ap_device *ap_dev, unsigned long *flags) { - int rc, depth, type; struct ap_queue_status status; - + int rc; if (ap_dev->reset == AP_RESET_IN_PROGRESS) { - status = ap_test_queue(ap_dev->qid, &depth, &type); + status = ap_test_queue(ap_dev->qid, NULL); switch (status.response_code) { case AP_RESPONSE_NORMAL: ap_dev->reset = AP_RESET_IGNORE; @@ -1676,7 +1600,7 @@ static inline int ap_poll_queue(struct ap_device *ap_dev, unsigned long *flags) if ((ap_dev->reset != AP_RESET_IN_PROGRESS) && (ap_dev->interrupt == AP_INTR_IN_PROGRESS)) { - status = ap_test_queue(ap_dev->qid, &depth, &type); + status = ap_test_queue(ap_dev->qid, NULL); if (ap_using_interrupts()) { if (status.int_enabled == 1) ap_dev->interrupt = AP_INTR_ENABLED; diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 00468c8d0781..2e0a21f84f3d 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -75,15 +75,6 @@ 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) -- cgit v1.2.3 From 889875a14f0a3205e78613b3e78ecc4efc187d74 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 26 Jun 2015 16:55:35 +0200 Subject: s390/zcrypt: fix memory leak with ap configuration data The ap_query_configuration function allocates the ap_config_info structure, but there is no code to free the structure. Allocate the structure in the module_init function and free it again in module_exit. While we are at it simplify a few functions in regard to the ap configuration data. Reviewed-by: Ingo Tuchscherer Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 247 ++++++++++++++++++++----------------------- drivers/s390/crypto/ap_bus.h | 3 - 2 files changed, 117 insertions(+), 133 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 613c92b22445..689f46cbf594 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -63,7 +63,6 @@ static void ap_interrupt_handler(struct airq_struct *airq); static void ap_reset(struct ap_device *ap_dev, unsigned long *flags); static void ap_config_timeout(unsigned long ptr); static int ap_select_domain(void); -static void ap_query_configuration(void); /* * Module description. @@ -115,6 +114,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. */ @@ -242,12 +243,19 @@ ap_queue_interruption_control(ap_qid_t qid, void *ind) return reg1_out; } -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" @@ -260,6 +268,63 @@ static inline int __ap_query_configuration(struct ap_config_info *config) return reg1; } +/** + * ap_init_configuration(): Allocate and query configuration array. + */ +static void ap_init_configuration(void) +{ + if (!ap_configuration_available()) + return; + + ap_configuration = kzalloc(sizeof(*ap_configuration), GFP_KERNEL); + if (!ap_configuration) + return; + if (ap_query_configuration() != 0) { + kfree(ap_configuration); + ap_configuration = NULL; + return; + } +} + +/* + * 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); +} + /** * ap_queue_enable_interruption(): Enable interruption on an AP. * @qid: The AP queue number @@ -450,6 +515,10 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type, { struct ap_queue_status status; 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) { @@ -457,6 +526,10 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type, *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: @@ -952,51 +1025,6 @@ void ap_bus_force_rescan(void) } 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. */ @@ -1009,21 +1037,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, @@ -1115,38 +1142,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) { - struct ap_queue_status status; - 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); - status = ap_test_queue(qid, &fbits); - if (status.response_code != AP_RESPONSE_NORMAL) - continue; - 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); } @@ -1163,24 +1164,6 @@ static struct bus_attribute *const ap_bus_attrs[] = { NULL, }; -/** - * 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. * @@ -1192,16 +1175,12 @@ static int ap_select_domain(void) struct ap_queue_status status; int i, j; - /* IF APXA isn't installed, only 16 domains could be defined */ - if (!ap_configuration->ap_extended && (ap_domain_index > 15)) - return -EINVAL; - /* * 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; @@ -1365,16 +1344,14 @@ static void ap_scan_bus(struct work_struct *unused) if (ap_select_domain() != 0) { return; } + for (i = 0; i < AP_DEVICES; i++) { qid = AP_MKQID(i, ap_domain_index); dev = bus_find_device(&ap_bus_type, NULL, (void *)(unsigned long)qid, __ap_scan_bus); - if (ap_test_config_card_id(i)) - rc = ap_query_queue(qid, &queue_depth, &device_type, - &device_functions); - else - rc = -ENODEV; + rc = ap_query_queue(qid, &queue_depth, &device_type, + &device_functions); if (dev) { ap_dev = to_ap_dev(dev); spin_lock_bh(&ap_dev->lock); @@ -1902,9 +1879,10 @@ 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) @@ -1933,11 +1911,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. @@ -1946,11 +1937,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); @@ -1980,7 +1966,6 @@ int __init ap_module_init(void) goto out_root; } - ap_query_configuration(); if (ap_select_domain() == 0) ap_scan_bus(NULL); @@ -2023,6 +2008,7 @@ out: unregister_reset_call(&ap_reset_call); if (ap_using_interrupts()) unregister_adapter_interrupt(&ap_airq); + kfree(ap_configuration); return rc; } @@ -2057,6 +2043,7 @@ void ap_module_exit(void) bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); 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/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 2e0a21f84f3d..83cc9e404a77 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -76,11 +76,8 @@ struct ap_queue_status { } __packed; -#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; } -- cgit v1.2.3 From 98733791e8ea9f700ce2c6feedcbeb7c4703359e Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 17 Jul 2015 15:01:08 +0200 Subject: s390/zcrypt: fix device unregister race The ap_queue_messsage function will call device_unregister if the unregistered field of the device has been set while trying to queue a message. This races with other device_unregister calls, e.g. from the ap_scan_bus. Remove the call to device_unregister from ap_queue_message and let ap_scan_bus deal with it. Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 689f46cbf594..80af1bd473e1 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1666,8 +1666,6 @@ void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg) rc = -ENODEV; } spin_unlock_bh(&ap_dev->lock); - if (rc == -ENODEV) - device_unregister(&ap_dev->device); } EXPORT_SYMBOL(ap_queue_message); -- cgit v1.2.3 From 83e9d5d2d45e518deb8cb843bd2e41548c6bb9b2 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 17 Jul 2015 13:43:18 +0200 Subject: s390/zcrypt: fix suspend/resume of AP bus devices If there are no devices on the AP bus there will not be a single call to the per-device ap_bus_suspend function. Even worse, there will not be a call to the per-device ap_bus_resume either and the AP will fail so resume correctly. Introduce a bus specific dev_pm_ops to suspend / resume the AP bus related things. While we are at it, simplify the power management code of the AP bus. Reviewd-by: Ingo Tuchscherer Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 326 +++++++++++++++++++++---------------------- 1 file changed, 160 insertions(+), 166 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 80af1bd473e1..8dfd1c9163a7 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -52,8 +53,6 @@ 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); @@ -614,6 +613,78 @@ static void ap_decrease_queue_count(struct ap_device *ap_dev) ap_dev->reset = AP_RESET_IGNORE; } +/** + * 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; + struct ap_device *ap_dev; + + 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; + } + + 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); + } 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 device related attributes. */ @@ -827,25 +898,11 @@ 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; @@ -854,72 +911,86 @@ static int ap_bus_suspend(struct device *dev, pm_message_t state) spin_unlock_bh(&ap_dev->lock); } while ((flags & 1) || (flags & 2)); - spin_lock_bh(&ap_dev->lock); - ap_dev->unregistered = 1; - 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. + */ + del_timer_sync(&ap_config_timer); + flush_workqueue(ap_work_queue); + 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; + } + /* 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(ap_work_queue, &ap_config_work); + wake_up(&ap_poll_wait); +} - 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) @@ -1017,6 +1088,8 @@ EXPORT_SYMBOL(ap_driver_unregister); void ap_bus_force_rescan(void) { + if (ap_suspend_flag) + return; /* reconfigure the AP bus rescan timer. */ mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ); /* processing a asynchronous bus rescan */ @@ -1102,9 +1175,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; } @@ -1309,7 +1381,8 @@ out: static void ap_interrupt_handler(struct airq_struct *airq) { inc_irq_stat(IRQIO_APB); - tasklet_schedule(&ap_tasklet); + if (!ap_suspend_flag) + tasklet_schedule(&ap_tasklet); } /** @@ -1341,9 +1414,8 @@ static void ap_scan_bus(struct work_struct *unused) int rc, i; ap_query_configuration(); - if (ap_select_domain() != 0) { + if (ap_select_domain() != 0) return; - } for (i = 0; i < AP_DEVICES; i++) { qid = AP_MKQID(i, ap_domain_index); @@ -1422,9 +1494,10 @@ static void ap_scan_bus(struct work_struct *unused) } } -static void -ap_config_timeout(unsigned long ptr) +static void ap_config_timeout(unsigned long ptr) { + if (ap_suspend_flag) + return; queue_work(ap_work_queue, &ap_config_work); ap_config_timer.expires = jiffies + ap_config_time * HZ; add_timer(&ap_config_timer); @@ -1706,7 +1779,8 @@ EXPORT_SYMBOL(ap_cancel_message); */ static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused) { - tasklet_schedule(&ap_tasklet); + if (!ap_suspend_flag) + tasklet_schedule(&ap_tasklet); return HRTIMER_NORESTART; } @@ -1777,84 +1851,6 @@ static void ap_poll_all(unsigned long dummy) __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); -} - /** * ap_request_timeout(): Handling of request timeouts * @data: Holds the AP device. @@ -1865,6 +1861,8 @@ static void ap_request_timeout(unsigned long data) { struct ap_device *ap_dev = (struct ap_device *) data; + if (ap_suspend_flag) + return; if (ap_dev->reset == AP_RESET_ARMED) { ap_dev->reset = AP_RESET_DO; @@ -1990,8 +1988,14 @@ int __init ap_module_init(void) goto out_work; } + rc = register_pm_notifier(&ap_power_notifier); + if (rc) + goto out_pm; + return 0; +out_pm: + ap_poll_thread_stop(); out_work: del_timer_sync(&ap_config_timer); hrtimer_cancel(&ap_poll_timer); @@ -2010,11 +2014,6 @@ out: return rc; } -static int __ap_match_all(struct device *dev, void *data) -{ - return 1; -} - /** * ap_modules_exit(): The module termination code * @@ -2023,7 +2022,6 @@ static int __ap_match_all(struct device *dev, void *data) void ap_module_exit(void) { int i; - struct device *dev; ap_reset_domain(); ap_poll_thread_stop(); @@ -2031,14 +2029,10 @@ void ap_module_exit(void) 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); -- cgit v1.2.3 From fcd0d1f637e57777fdc742adee6bfbcab5876295 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 23 Jul 2015 10:55:59 +0200 Subject: s390/zcrypt: cleanup AP bus timer code Set the configuration timer at the end of the ap_scan_bus function. Make use of setup_timer and remove some unnecessary add_timer, mod_timer and del_timer_sync calls. Replace the complicated timer_pending, mod_timer and add_timer code in ap_config_time_store with a simple mod_timer. Reviewd-by: Ingo Tuchscherer Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 8dfd1c9163a7..ba1f806051b7 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -52,15 +52,12 @@ /* 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 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, unsigned long *flags); -static void ap_config_timeout(unsigned long ptr); static int ap_select_domain(void); /* @@ -926,7 +923,6 @@ static void ap_bus_suspend(void) * Disable scanning for devices, thus we do not want to scan * for them after removing. */ - del_timer_sync(&ap_config_timer); flush_workqueue(ap_work_queue); tasklet_disable(&ap_tasklet); } @@ -1090,9 +1086,8 @@ void ap_bus_force_rescan(void) { if (ap_suspend_flag) return; - /* reconfigure the AP bus rescan timer. */ - mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ); /* processing a asynchronous bus rescan */ + del_timer(&ap_config_timer); queue_work(ap_work_queue, &ap_config_work); flush_work(&ap_config_work); } @@ -1150,11 +1145,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; } @@ -1415,7 +1406,7 @@ static void ap_scan_bus(struct work_struct *unused) ap_query_configuration(); if (ap_select_domain() != 0) - return; + goto out; for (i = 0; i < AP_DEVICES; i++) { qid = AP_MKQID(i, ap_domain_index); @@ -1492,6 +1483,8 @@ static void ap_scan_bus(struct work_struct *unused) else device_unregister(&ap_dev->device); } +out: + mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ); } static void ap_config_timeout(unsigned long ptr) @@ -1499,8 +1492,6 @@ static void ap_config_timeout(unsigned long ptr) if (ap_suspend_flag) return; queue_work(ap_work_queue, &ap_config_work); - ap_config_timer.expires = jiffies + ap_config_time * HZ; - add_timer(&ap_config_timer); } /** @@ -1966,10 +1957,7 @@ int __init ap_module_init(void) 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; + setup_timer(&ap_config_timer, ap_config_timeout, 0); add_timer(&ap_config_timer); /* Setup the high resultion poll timer. -- cgit v1.2.3 From f58fe336009f451748c1c337f35130a320ef923d Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 14 Sep 2015 16:59:27 +0200 Subject: s390/zcrypt: use explicit return code for flushed requests If a AP device is removed while messages are still pending, the requests are cancelled by calling the message receive function with an error pointer for the reply. The message type receive handler recognize this and create a fake hardware error TYPE82_RSP_CODE / REP82_ERROR_MACHINE_FAILURE. The message with the hardware error then causes a printk and a return code of -EAGAIN. Replace the intricate scheme with an explicit return code for this sitation and avoid the error message. Reviewd-by: Ingo Tuchscherer Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 15 ++++++--- drivers/s390/crypto/ap_bus.h | 2 ++ drivers/s390/crypto/zcrypt_msgtype50.c | 26 ++++++++------- drivers/s390/crypto/zcrypt_msgtype6.c | 59 ++++++++++++++++++++-------------- drivers/s390/crypto/zcrypt_pcica.c | 26 ++++++++------- drivers/s390/crypto/zcrypt_pcicc.c | 26 ++++++++------- 6 files changed, 88 insertions(+), 66 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index ba1f806051b7..7224bf7fa9bf 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1027,12 +1027,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); } } @@ -1690,10 +1692,12 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms 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)); + ap_msg->rc = -EINVAL; + ap_msg->receive(ap_dev, ap_msg, NULL); return -EINVAL; default: /* Device is gone. */ - ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); + ap_msg->rc = -ENODEV; + ap_msg->receive(ap_dev, ap_msg, NULL); return -ENODEV; } } else { @@ -1726,7 +1730,8 @@ void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg) if (rc == -ENODEV) ap_dev->unregistered = 1; } else { - ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); + ap_msg->rc = -ENODEV; + ap_msg->receive(ap_dev, ap_msg, NULL); rc = -ENODEV; } spin_unlock_bh(&ap_dev->lock); diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 83cc9e404a77..af9b705976bc 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -187,6 +187,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. */ @@ -219,6 +220,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/drivers/s390/crypto/zcrypt_msgtype50.c b/drivers/s390/crypto/zcrypt_msgtype50.c index 6c9af1332967..71ceee9137a8 100644 --- a/drivers/s390/crypto/zcrypt_msgtype50.c +++ b/drivers/s390/crypto/zcrypt_msgtype50.c @@ -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: diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c index 46b324ce6c7a..74762214193b 100644 --- a/drivers/s390/crypto/zcrypt_msgtype6.c +++ b/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); diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c index 2f220208da63..6af9c9fd39b1 100644 --- a/drivers/s390/crypto/zcrypt_pcica.c +++ b/drivers/s390/crypto/zcrypt_pcica.c @@ -258,10 +258,8 @@ static void zcrypt_pcica_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 */ t84h = reply->message; if (t84h->code == TYPE84_RSP_CODE) { length = min(PCICA_MAX_RESPONSE_SIZE, (int) t84h->len); @@ -302,10 +300,12 @@ static long zcrypt_pcica_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: @@ -341,10 +341,12 @@ static long zcrypt_pcica_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: diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c index 9f18876f058b..4cf2115adfc1 100644 --- a/drivers/s390/crypto/zcrypt_pcicc.c +++ b/drivers/s390/crypto/zcrypt_pcicc.c @@ -461,10 +461,8 @@ static void zcrypt_pcicc_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->cprb.cprb_ver_id == 0x01) { @@ -508,10 +506,12 @@ static long zcrypt_pcicc_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: @@ -548,10 +548,12 @@ static long zcrypt_pcicc_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: -- cgit v1.2.3 From 3f3007afda8bb731a3422430752ca91ddc102e4d Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 14 Sep 2015 17:01:23 +0200 Subject: s390/zcrypt: introduce state machine for the AP bus Replace the two fields 'unregistered' and 'reset' with a device state with 5 possible values. Introduce two events for the AP devices, device poll and device timeout. With the state machine it is easier to deal with device initialization and suspend/resume. Device polling is simpler as well, the arkane 'flags' passing is gone. Reviewd-by: Ingo Tuchscherer Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 1033 ++++++++++++++++++++---------------------- drivers/s390/crypto/ap_bus.h | 53 ++- 2 files changed, 522 insertions(+), 564 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 7224bf7fa9bf..92d3b67b3839 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -49,17 +49,6 @@ #include "ap_bus.h" -/* Some prototypes. */ -static void ap_scan_bus(struct work_struct *); -static void ap_poll_all(unsigned long); -static void ap_request_timeout(unsigned long); -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, unsigned long *flags); -static int ap_select_domain(void); - /* * Module description. */ @@ -92,12 +81,14 @@ static LIST_HEAD(ap_device_list); static struct workqueue_struct *ap_work_queue; static struct timer_list ap_config_timer; static int ap_config_time = AP_CONFIG_TIME; +static void ap_scan_bus(struct work_struct *); static DECLARE_WORK(ap_config_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; @@ -119,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 = { @@ -343,7 +336,9 @@ static int ap_queue_enable_interruption(struct ap_device *ap_dev, void *ind) case AP_RESPONSE_DECONFIGURED: case AP_RESPONSE_CHECKSTOPPED: case AP_RESPONSE_INVALID_ADDRESS: - return -ENODEV; + 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: @@ -468,37 +463,6 @@ int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) } EXPORT_SYMBOL(ap_recv); -/** - * __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_query_queue(): Check if an AP queue is available. * @qid: The AP queue number @@ -541,73 +505,404 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type, } } +/* State machine definitions and helpers */ + +static void ap_sm_wait(enum ap_wait wait) +{ + ktime_t hr_time; + + switch (wait) { + case AP_WAIT_AGAIN: + case AP_WAIT_INTERRUPT: + if (ap_using_interrupts()) + break; + if (ap_poll_kthread) { + wake_up(&ap_poll_wait); + break; + } + /* 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; + } +} + +static enum ap_wait ap_sm_nop(struct ap_device *ap_dev) +{ + return AP_WAIT_NONE; +} + +/** + * ap_sm_recv(): Receive pending reply messages from an AP device but do + * not change the state of the device. + * @ap_dev: pointer to the AP device + * + * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT + */ +static struct ap_queue_status ap_sm_recv(struct ap_device *ap_dev) +{ + struct ap_queue_status status; + struct ap_message *ap_msg; + + status = __ap_recv(ap_dev->qid, &ap_dev->reply->psmid, + ap_dev->reply->message, ap_dev->reply->length); + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + atomic_dec(&ap_poll_requests); + ap_dev->queue_count--; + if (ap_dev->queue_count > 0) + mod_timer(&ap_dev->timeout, + jiffies + ap_dev->drv->request_timeout); + list_for_each_entry(ap_msg, &ap_dev->pendingq, list) { + if (ap_msg->psmid != ap_dev->reply->psmid) + continue; + list_del_init(&ap_msg->list); + ap_dev->pendingq_count--; + ap_msg->receive(ap_dev, ap_msg, ap_dev->reply); + break; + } + case AP_RESPONSE_NO_PENDING_REPLY: + if (!status.queue_empty || ap_dev->queue_count <= 0) + break; + /* The card shouldn't forget requests but who knows. */ + atomic_sub(ap_dev->queue_count, &ap_poll_requests); + ap_dev->queue_count = 0; + list_splice_init(&ap_dev->pendingq, &ap_dev->requestq); + ap_dev->requestq_count += ap_dev->pendingq_count; + ap_dev->pendingq_count = 0; + break; + default: + break; + } + return status; +} + +/** + * ap_sm_read(): Receive pending reply messages from an AP device. + * @ap_dev: pointer to the AP device + * + * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT + */ +static enum ap_wait ap_sm_read(struct ap_device *ap_dev) +{ + struct ap_queue_status status; + + status = ap_sm_recv(ap_dev); + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + if (ap_dev->queue_count > 0) + return AP_WAIT_AGAIN; + ap_dev->state = AP_STATE_IDLE; + return AP_WAIT_NONE; + case AP_RESPONSE_NO_PENDING_REPLY: + if (ap_dev->queue_count > 0) + return AP_WAIT_INTERRUPT; + ap_dev->state = AP_STATE_IDLE; + return AP_WAIT_NONE; + default: + ap_dev->state = AP_STATE_BORKED; + return AP_WAIT_NONE; + } +} + +/** + * ap_sm_write(): Send messages from the request queue to an AP device. + * @ap_dev: pointer to the AP device + * + * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT + */ +static enum ap_wait ap_sm_write(struct ap_device *ap_dev) +{ + struct ap_queue_status status; + struct ap_message *ap_msg; + + if (ap_dev->requestq_count <= 0) + return AP_WAIT_NONE; + /* Start the next request on the queue. */ + ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list); + status = __ap_send(ap_dev->qid, ap_msg->psmid, + ap_msg->message, ap_msg->length, ap_msg->special); + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + atomic_inc(&ap_poll_requests); + ap_dev->queue_count++; + if (ap_dev->queue_count == 1) + mod_timer(&ap_dev->timeout, + jiffies + ap_dev->drv->request_timeout); + list_move_tail(&ap_msg->list, &ap_dev->pendingq); + ap_dev->requestq_count--; + ap_dev->pendingq_count++; + if (ap_dev->queue_count < ap_dev->queue_depth) { + ap_dev->state = AP_STATE_WORKING; + return AP_WAIT_AGAIN; + } + /* fall through */ + case AP_RESPONSE_Q_FULL: + ap_dev->state = AP_STATE_QUEUE_FULL; + return AP_WAIT_INTERRUPT; + case AP_RESPONSE_RESET_IN_PROGRESS: + ap_dev->state = AP_STATE_RESET_WAIT; + return AP_WAIT_TIMEOUT; + case AP_RESPONSE_MESSAGE_TOO_BIG: + case AP_RESPONSE_REQ_FAC_NOT_INST: + list_del_init(&ap_msg->list); + ap_dev->requestq_count--; + ap_msg->rc = -EINVAL; + ap_msg->receive(ap_dev, ap_msg, NULL); + return AP_WAIT_AGAIN; + default: + ap_dev->state = AP_STATE_BORKED; + return AP_WAIT_NONE; + } +} + /** - * ap_init_queue(): Reset an AP queue. + * ap_sm_read_write(): Send and receive messages to/from an AP device. + * @ap_dev: pointer to the AP device + * + * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT + */ +static enum ap_wait ap_sm_read_write(struct ap_device *ap_dev) +{ + return min(ap_sm_read(ap_dev), ap_sm_write(ap_dev)); +} + +/** + * ap_sm_reset(): Reset an AP queue. * @qid: The AP queue number * * Submit the Reset command to an AP queue. - * Since the reset is asynchron set the state to 'RESET_IN_PROGRESS' - * and check later via ap_poll_queue() if the reset is done. */ -static int ap_init_queue(struct ap_device *ap_dev) +static enum ap_wait ap_sm_reset(struct ap_device *ap_dev) { struct ap_queue_status status; status = ap_reset_queue(ap_dev->qid); switch (status.response_code) { case AP_RESPONSE_NORMAL: - ap_dev->interrupt = AP_INTR_DISABLED; - ap_dev->reset = AP_RESET_IN_PROGRESS; - return 0; 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 -EBUSY; + return AP_WAIT_TIMEOUT; case AP_RESPONSE_Q_NOT_AVAIL: case AP_RESPONSE_DECONFIGURED: case AP_RESPONSE_CHECKSTOPPED: default: - return -ENODEV; + ap_dev->state = AP_STATE_BORKED; + return AP_WAIT_NONE; } } /** - * ap_increase_queue_count(): Arm request timeout. - * @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 * - * Arm request timeout if an AP device was idle and a new request is submitted. + * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0. */ -static void ap_increase_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 == 1) { - mod_timer(&ap_dev->timeout, jiffies + timeout); - ap_dev->reset = AP_RESET_ARMED; + 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); + + 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_decrease_queue_count(): Decrease queue count. - * @ap_dev: Pointer to an AP device. + * ap_sm_setirq_wait(): Test queue for completion of the irq enablement + * @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_setirq_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); + + 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); } /** @@ -623,8 +918,6 @@ static void ap_decrease_queue_count(struct ap_device *ap_dev) static int ap_poll_thread(void *data) { DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - struct ap_device *ap_dev; set_user_nice(current, MAX_NICE); set_freezable(); @@ -638,21 +931,12 @@ static int ap_poll_thread(void *data) } set_current_state(TASK_RUNNING); remove_wait_queue(&ap_poll_wait, &wait); - if (need_resched()) { schedule(); try_to_freeze(); continue; } - - 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); + ap_tasklet_fn(0); } while (!kthread_should_stop()); return 0; } @@ -682,6 +966,57 @@ static void ap_poll_thread_stop(void) mutex_unlock(&ap_poll_thread_mutex); } +/** + * ap_queue_message(): Queue a request to an AP device. + * @ap_dev: The AP device to queue the message to + * @ap_msg: The message that is to be added + */ +void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg) +{ + /* For asynchronous message handling a valid receive-callback + * is required. */ + BUG_ON(!ap_msg->receive); + + spin_lock_bh(&ap_dev->lock); + /* Queue the message. */ + list_add_tail(&ap_msg->list, &ap_dev->requestq); + ap_dev->requestq_count++; + ap_dev->total_request_count++; + /* Send/receive as many request from the queue as possible. */ + ap_sm_wait(ap_sm_event_loop(ap_dev, AP_EVENT_POLL)); + spin_unlock_bh(&ap_dev->lock); +} +EXPORT_SYMBOL(ap_queue_message); + +/** + * ap_cancel_message(): Cancel a crypto request. + * @ap_dev: The AP device that has the message queued + * @ap_msg: The message that is to be removed + * + * Cancel a crypto request. This is done by removing the request + * from the device pending or request queue. Note that the + * request stays on the AP queue. When it finishes the message + * reply will be discarded because the psmid can't be found. + */ +void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg) +{ + struct ap_message *tmp; + + spin_lock_bh(&ap_dev->lock); + if (!list_empty(&ap_msg->list)) { + list_for_each_entry(tmp, &ap_dev->pendingq, list) + if (tmp->psmid == ap_msg->psmid) { + ap_dev->pendingq_count--; + goto found; + } + ap_dev->requestq_count--; +found: + list_del_init(&ap_msg->list); + } + spin_unlock_bh(&ap_dev->lock); +} +EXPORT_SYMBOL(ap_cancel_message); + /* * AP device related attributes. */ @@ -762,21 +1097,17 @@ static ssize_t ap_reset_show(struct device *dev, int rc = 0; spin_lock_bh(&ap_dev->lock); - switch (ap_dev->reset) { - case AP_RESET_IGNORE: - rc = snprintf(buf, PAGE_SIZE, "No Reset Timer set.\n"); + 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_RESET_ARMED: + case AP_STATE_WORKING: + case AP_STATE_QUEUE_FULL: rc = snprintf(buf, PAGE_SIZE, "Reset Timer armed.\n"); break; - case AP_RESET_DO: - rc = snprintf(buf, PAGE_SIZE, "Reset Timer expired.\n"); - break; - case AP_RESET_IN_PROGRESS: - rc = snprintf(buf, PAGE_SIZE, "Reset in progress.\n"); - break; default: - break; + rc = snprintf(buf, PAGE_SIZE, "No Reset Timer set.\n"); } spin_unlock_bh(&ap_dev->lock); return rc; @@ -791,17 +1122,12 @@ static ssize_t ap_interrupt_show(struct device *dev, int rc = 0; spin_lock_bh(&ap_dev->lock); - switch (ap_dev->interrupt) { - case AP_INTR_DISABLED: - rc = snprintf(buf, PAGE_SIZE, "Interrupts disabled.\n"); - break; - case AP_INTR_ENABLED: - rc = snprintf(buf, PAGE_SIZE, "Interrupts enabled.\n"); - break; - case AP_INTR_IN_PROGRESS: + if (ap_dev->state == AP_STATE_SETIRQ_WAIT) rc = snprintf(buf, PAGE_SIZE, "Enable Interrupt pending.\n"); - break; - } + 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; } @@ -898,16 +1224,14 @@ static int ap_uevent (struct device *dev, struct kobj_uevent_env *env) static int ap_dev_suspend(struct device *dev, pm_message_t state) { struct ap_device *ap_dev = to_ap_dev(dev); - unsigned long flags; /* 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->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; } @@ -957,7 +1281,6 @@ static void ap_bus_resume(void) xchg(ap_airq.lsi_ptr, 0); tasklet_enable(&ap_tasklet); queue_work(ap_work_queue, &ap_config_work); - wake_up(&ap_poll_wait); } static int ap_power_event(struct notifier_block *this, unsigned long event, @@ -993,24 +1316,12 @@ static int ap_device_probe(struct device *dev) { struct ap_device *ap_dev = to_ap_dev(dev); struct ap_driver *ap_drv = to_ap_drv(dev->driver); - int rc; - - ap_dev->drv = ap_drv; - - spin_lock_bh(&ap_device_list_lock); - list_add(&ap_dev->list, &ap_device_list); - spin_unlock_bh(&ap_device_list_lock); + int rc; + ap_dev->drv = ap_drv; 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); - } else { - if (ap_dev->reset == AP_RESET_IN_PROGRESS || - ap_dev->interrupt == AP_INTR_IN_PROGRESS) - __ap_schedule_poll_timer(); - } + if (rc) + ap_dev->drv = NULL; return rc; } @@ -1064,6 +1375,11 @@ 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) { @@ -1371,13 +1687,6 @@ out: return rc; } -static void ap_interrupt_handler(struct airq_struct *airq) -{ - inc_irq_stat(IRQIO_APB); - if (!ap_suspend_flag) - tasklet_schedule(&ap_tasklet); -} - /** * __ap_scan_bus(): Scan the AP bus. * @dev: Pointer to device @@ -1390,21 +1699,14 @@ 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 = 0, device_type = 0; - unsigned int device_functions; - int rc, i; + unsigned int device_functions = 0; + int rc, i, borked; ap_query_configuration(); if (ap_select_domain() != 0) @@ -1420,17 +1722,15 @@ static void ap_scan_bus(struct work_struct *unused) if (dev) { ap_dev = to_ap_dev(dev); spin_lock_bh(&ap_dev->lock); - if (rc == -ENODEV || 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; @@ -1438,16 +1738,12 @@ static void ap_scan_bus(struct work_struct *unused) if (!ap_dev) break; ap_dev->qid = qid; - rc = ap_init_queue(ap_dev); - if ((rc != 0) && (rc != -EBUSY)) { - kfree(ap_dev); - continue; - } + ap_dev->state = AP_STATE_RESET_START; + ap_dev->interrupt = AP_INTR_DISABLED; ap_dev->queue_depth = queue_depth; ap_dev->raw_hwtype = device_type; ap_dev->device_type = device_type; ap_dev->functions = device_functions; - ap_dev->unregistered = 1; spin_lock_init(&ap_dev->lock); INIT_LIST_HEAD(&ap_dev->pendingq); INIT_LIST_HEAD(&ap_dev->requestq); @@ -1463,27 +1759,37 @@ static void ap_scan_bus(struct work_struct *unused) 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); + continue; + } } out: mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ); @@ -1496,377 +1802,6 @@ static void ap_config_timeout(unsigned long ptr) queue_work(ap_work_queue, &ap_config_work); } -/** - * 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: - ap_dev->interrupt = status.int_enabled; - 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: - ap_dev->interrupt = status.int_enabled; - 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) || - (ap_dev->reset == AP_RESET_IN_PROGRESS)) - 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. - * Check if the queue has a pending reset. In case it's done re-enable - * interrupts, otherwise reschedule the poll_timer for another attempt. - * @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) -{ - struct ap_queue_status status; - int rc; - - if (ap_dev->reset == AP_RESET_IN_PROGRESS) { - status = ap_test_queue(ap_dev->qid, NULL); - switch (status.response_code) { - case AP_RESPONSE_NORMAL: - ap_dev->reset = AP_RESET_IGNORE; - if (ap_using_interrupts()) { - rc = ap_queue_enable_interruption( - ap_dev, ap_airq.lsi_ptr); - if (!rc) - ap_dev->interrupt = AP_INTR_IN_PROGRESS; - else if (rc == -ENODEV) { - pr_err("Registering adapter interrupts for " - "AP %d failed\n", AP_QID_DEVICE(ap_dev->qid)); - return rc; - } - } - /* fall through */ - case AP_RESPONSE_BUSY: - case AP_RESPONSE_RESET_IN_PROGRESS: - *flags |= AP_POLL_AFTER_TIMEOUT; - break; - case AP_RESPONSE_Q_NOT_AVAIL: - case AP_RESPONSE_DECONFIGURED: - case AP_RESPONSE_CHECKSTOPPED: - return -ENODEV; - default: - break; - } - } - - if ((ap_dev->reset != AP_RESET_IN_PROGRESS) && - (ap_dev->interrupt == AP_INTR_IN_PROGRESS)) { - status = ap_test_queue(ap_dev->qid, NULL); - if (ap_using_interrupts()) { - if (status.int_enabled == 1) - ap_dev->interrupt = AP_INTR_ENABLED; - else - *flags |= AP_POLL_AFTER_TIMEOUT; - } else - ap_dev->interrupt = AP_INTR_DISABLED; - } - - 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) && - (ap_dev->reset != AP_RESET_IN_PROGRESS)) { - 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->rc = -EINVAL; - ap_msg->receive(ap_dev, ap_msg, NULL); - return -EINVAL; - default: /* Device is gone. */ - ap_msg->rc = -ENODEV; - ap_msg->receive(ap_dev, ap_msg, NULL); - 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->rc = -ENODEV; - ap_msg->receive(ap_dev, ap_msg, NULL); - rc = -ENODEV; - } - 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_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_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, unsigned long *flags) -{ - int rc; - - 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); - if (rc == -ENODEV) - ap_dev->unregistered = 1; - else - *flags |= AP_POLL_AFTER_TIMEOUT; -} - -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, flags); - } - 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 & AP_POLL_IMMEDIATELY); - if (flags & AP_POLL_AFTER_TIMEOUT) - __ap_schedule_poll_timer(); -} - -/** - * 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; - if (ap_dev->reset == AP_RESET_ARMED) { - ap_dev->reset = AP_RESET_DO; - - if (ap_using_interrupts()) - tasklet_schedule(&ap_tasklet); - } -} - static void ap_reset_domain(void) { int i; @@ -1958,14 +1893,11 @@ int __init ap_module_init(void) goto out_root; } - if (ap_select_domain() == 0) - ap_scan_bus(NULL); - /* Setup the AP bus rescan timer. */ setup_timer(&ap_config_timer, ap_config_timeout, 0); - add_timer(&ap_config_timer); - /* 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) @@ -1985,12 +1917,13 @@ int __init ap_module_init(void) if (rc) goto out_pm; + queue_work(ap_work_queue, &ap_config_work); + 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: diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index af9b705976bc..6adcbdf225d1 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -36,9 +36,6 @@ #define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */ #define AP_POLL_TIME 1 /* Time in ticks between receive polls. */ -#define AP_POLL_IMMEDIATELY 1 /* continue running poll tasklet */ -#define AP_POLL_AFTER_TIMEOUT 2 /* run poll tasklet again after timout */ - extern int ap_domain_index; /** @@ -119,20 +116,46 @@ static inline int ap_test_bit(unsigned int *ptr, unsigned int nr) #define AP_FUNC_EP11 5 #define AP_FUNC_APXA 6 -/* - * AP reset flag 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_RESET_IN_PROGRESS 3 /* AP reset in progress */ - /* * AP interrupt states */ #define AP_INTR_DISABLED 0 /* AP interrupt disabled */ #define AP_INTR_ENABLED 1 /* AP interrupt enabled */ -#define AP_INTR_IN_PROGRESS 3 /* AP interrupt in progress */ + +/* + * 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; @@ -151,20 +174,22 @@ 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. */ -- cgit v1.2.3 From b96a9e5140d4d6253e499bd18c35a7d085ac46e2 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 14 Sep 2015 17:28:26 +0200 Subject: s390/zcrypt: remove support for PCICC and PCICA cards Remove the code for really old crypt cards, PCICC and PCICA. These cards have been out of service for several years. Reviewd-by: Ingo Tuchscherer Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/Makefile | 4 +- drivers/s390/crypto/ap_bus.c | 103 ------ drivers/s390/crypto/zcrypt_pcica.c | 422 ------------------------- drivers/s390/crypto/zcrypt_pcica.h | 115 ------- drivers/s390/crypto/zcrypt_pcicc.c | 629 ------------------------------------- drivers/s390/crypto/zcrypt_pcicc.h | 174 ---------- 6 files changed, 2 insertions(+), 1445 deletions(-) delete mode 100644 drivers/s390/crypto/zcrypt_pcica.c delete mode 100644 drivers/s390/crypto/zcrypt_pcica.h delete mode 100644 drivers/s390/crypto/zcrypt_pcicc.c delete mode 100644 drivers/s390/crypto/zcrypt_pcicc.h (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile index 771faf7094d6..57f710b3c8a4 100644 --- a/drivers/s390/crypto/Makefile +++ b/drivers/s390/crypto/Makefile @@ -3,6 +3,6 @@ # 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 +obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o zcrypt_pcixcc.o +obj-$(CONFIG_ZCRYPT) += zcrypt_cex2a.o zcrypt_cex4.o obj-$(CONFIG_ZCRYPT) += zcrypt_msgtype6.o zcrypt_msgtype50.o diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 92d3b67b3839..1d61c8698425 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1590,103 +1590,6 @@ static int ap_select_domain(void) return -ENODEV; } -/** - * 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++) { - msleep(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; -} - /** * __ap_scan_bus(): Scan the AP bus. * @dev: Pointer to device @@ -1750,12 +1653,6 @@ static void ap_scan_bus(struct work_struct *unused) INIT_LIST_HEAD(&ap_dev->list); setup_timer(&ap_dev->timeout, ap_request_timeout, (unsigned long) ap_dev); - if (ap_dev->device_type == 0) - /* device type probing for old cards */ - if (ap_probe_device_type(ap_dev)) { - kfree(ap_dev); - continue; - } ap_dev->device.bus = &ap_bus_type; ap_dev->device.parent = ap_root_device; diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c deleted file mode 100644 index 6af9c9fd39b1..000000000000 --- a/drivers/s390/crypto/zcrypt_pcica.c +++ /dev/null @@ -1,422 +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 - * Ralph Wuerthner - * - * 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 -#include -#include -#include -#include -#include - -#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 + 1) / 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 (!reply) - goto out; /* ap_msg->rc indicates the error */ - 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 = 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: - 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 = 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: - 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/drivers/s390/crypto/zcrypt_pcica.h b/drivers/s390/crypto/zcrypt_pcica.h deleted file mode 100644 index 9a59155cad51..000000000000 --- a/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 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _ZCRYPT_PCICA_H_ -#define _ZCRYPT_PCICA_H_ - -/** - * The type 4 message family is associated with a PCICA card. - * - * The four members of the family are described below. - * - * Note that all unsigned char arrays are right-justified and left-padded - * with zeroes. - * - * Note that all reserved fields must be zeroes. - */ -struct type4_hdr { - unsigned char reserved1; - unsigned char msg_type_code; /* 0x04 */ - unsigned short msg_len; - unsigned char request_code; /* 0x40 */ - unsigned char msg_fmt; - unsigned short reserved2; -} __attribute__((packed)); - -#define TYPE4_TYPE_CODE 0x04 -#define TYPE4_REQU_CODE 0x40 - -#define TYPE4_SME_FMT 0x00 -#define TYPE4_LME_FMT 0x10 -#define TYPE4_SCR_FMT 0x40 -#define TYPE4_LCR_FMT 0x50 - -/* Mod-Exp, with a small modulus */ -struct type4_sme { - struct type4_hdr header; - unsigned char message[128]; - unsigned char exponent[128]; - unsigned char modulus[128]; -} __attribute__((packed)); - -/* Mod-Exp, with a large modulus */ -struct type4_lme { - struct type4_hdr header; - unsigned char message[256]; - unsigned char exponent[256]; - unsigned char modulus[256]; -} __attribute__((packed)); - -/* CRT, with a small modulus */ -struct type4_scr { - struct type4_hdr header; - unsigned char message[128]; - unsigned char dp[72]; - unsigned char dq[64]; - unsigned char p[72]; - unsigned char q[64]; - unsigned char u[72]; -} __attribute__((packed)); - -/* CRT, with a large modulus */ -struct type4_lcr { - struct type4_hdr header; - unsigned char message[256]; - unsigned char dp[136]; - unsigned char dq[128]; - unsigned char p[136]; - unsigned char q[128]; - unsigned char u[136]; -} __attribute__((packed)); - -/** - * The type 84 response family is associated with a PCICA card. - * - * Note that all unsigned char arrays are right-justified and left-padded - * with zeroes. - * - * Note that all reserved fields must be zeroes. - */ - -struct type84_hdr { - unsigned char reserved1; - unsigned char code; - unsigned short len; - unsigned char reserved2[4]; -} __attribute__((packed)); - -#define TYPE84_RSP_CODE 0x84 - -int zcrypt_pcica_init(void); -void zcrypt_pcica_exit(void); - -#endif /* _ZCRYPT_PCICA_H_ */ diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c deleted file mode 100644 index 4cf2115adfc1..000000000000 --- a/drivers/s390/crypto/zcrypt_pcicc.c +++ /dev/null @@ -1,629 +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 - * Ralph Wuerthner - * - * 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 -#include -#include -#include -#include -#include - -#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 = cpu_to_le16(0x0070), - .cprb_ver_id = 0x41, - .func_id = {0x54,0x32}, - .checkpoint_flag= 0x01, - .svr_namel = 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 = 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 = 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 (!reply) - goto out; /* ap_msg->rc indicates the error */ - 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 = 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: - 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 = 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: - 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/drivers/s390/crypto/zcrypt_pcicc.h b/drivers/s390/crypto/zcrypt_pcicc.h deleted file mode 100644 index 7fe27e15075b..000000000000 --- a/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 - * - * 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_ */ -- cgit v1.2.3 From 8139b89d7cc5f53cff63a72c8e226385f34a70a2 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 27 Jul 2015 12:47:40 +0200 Subject: s390/zcrypt: use system work queue for ap_scan_bus There is a system work queue system_long_wq for long running work. Use this work queue for the AP bus scan loop. Reviewd-by: Ingo Tuchscherer Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 1d61c8698425..9cb3dfbcaddb 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -76,13 +76,12 @@ static DEFINE_SPINLOCK(ap_device_list_lock); static LIST_HEAD(ap_device_list); /* - * 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 void ap_scan_bus(struct work_struct *); -static DECLARE_WORK(ap_config_work, ap_scan_bus); +static DECLARE_WORK(ap_scan_work, ap_scan_bus); /* * Tasklet & timer for AP request polling and interrupts @@ -1247,7 +1246,7 @@ static void ap_bus_suspend(void) * Disable scanning for devices, thus we do not want to scan * for them after removing. */ - flush_workqueue(ap_work_queue); + flush_work(&ap_scan_work); tasklet_disable(&ap_tasklet); } @@ -1280,7 +1279,7 @@ static void ap_bus_resume(void) if (ap_airq_flag) xchg(ap_airq.lsi_ptr, 0); tasklet_enable(&ap_tasklet); - queue_work(ap_work_queue, &ap_config_work); + queue_work(system_long_wq, &ap_scan_work); } static int ap_power_event(struct notifier_block *this, unsigned long event, @@ -1406,8 +1405,8 @@ void ap_bus_force_rescan(void) return; /* processing a asynchronous bus rescan */ del_timer(&ap_config_timer); - queue_work(ap_work_queue, &ap_config_work); - flush_work(&ap_config_work); + queue_work(system_long_wq, &ap_scan_work); + flush_work(&ap_scan_work); } EXPORT_SYMBOL(ap_bus_force_rescan); @@ -1696,7 +1695,7 @@ static void ap_config_timeout(unsigned long ptr) { if (ap_suspend_flag) return; - queue_work(ap_work_queue, &ap_config_work); + queue_work(system_long_wq, &ap_scan_work); } static void ap_reset_domain(void) @@ -1784,12 +1783,6 @@ 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; - } - /* Setup the AP bus rescan timer. */ setup_timer(&ap_config_timer, ap_config_timeout, 0); @@ -1814,7 +1807,7 @@ int __init ap_module_init(void) if (rc) goto out_pm; - queue_work(ap_work_queue, &ap_config_work); + queue_work(system_long_wq, &ap_scan_work); return 0; @@ -1822,8 +1815,6 @@ out_pm: ap_poll_thread_stop(); out_work: hrtimer_cancel(&ap_poll_timer); - destroy_workqueue(ap_work_queue); -out_root: root_device_unregister(ap_root_device); out_bus: while (i--) @@ -1850,7 +1841,6 @@ void ap_module_exit(void) ap_poll_thread_stop(); del_timer_sync(&ap_config_timer); hrtimer_cancel(&ap_poll_timer); - destroy_workqueue(ap_work_queue); tasklet_kill(&ap_tasklet); bus_for_each_dev(&ap_bus_type, NULL, NULL, __ap_devices_unregister); for (i = 0; ap_bus_attrs[i]; i++) -- cgit v1.2.3 From 6933c35a307cad0b8b847042346dde68866d3d2a Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 14 Oct 2015 11:01:05 +0200 Subject: s390/dasd: fix list_del corruption after lcu changes A summary unit check occurs when the lcu updates the PAV configuration e.g. base PAV assignment or PAV mode at all. This requires the reset of the drivers internal pavgroups. Therefore the alias devices are flushed and moved via a temporary list to the active_devices list where they are not associated with a pavgroup. In conjunction with updates to the base device the pavgroup may be removed since both base_list and alias_list are empty. Unfortunately during alias flush and move to the active_device list from alias_list the pavgroup pointer is not deleted in the device private structure. This leads to a list del_corruption if another lcu_update tries to move the device in the non existent pavgroup. Fix by removing the pavgroup pointer after the alias device was moved to the active_devices list. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_alias.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index fe07f3139bf6..184b1dbeb554 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -824,8 +824,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); } -- cgit v1.2.3 From dc6e15556a9f21d640a62ae6de3cbfa68ec687a4 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 12 Oct 2015 13:00:39 +0200 Subject: s390/nmi: remove casts Remove all the casts to and from the machine check interruption code. This patch changes struct mci to a union, which contains an anonymous structure with the already known bits and in addition an unsigned long field, which contains the raw machine check interruption code. This allows to simply assign and decoce the interruption code value without the need for all those casts we had all the time. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 07fc5d9e7f10..0cc65b229a9c 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -826,11 +826,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) { -- cgit v1.2.3 From 8421d212e831cb03ba65bada57a733ad875286bf Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 26 Oct 2015 12:35:06 +0100 Subject: s390/cio: move ccw_device_call_handler device_ops.c should only contain functions that are called by ccw device drivers. Move the cio internal function ccw_device_call_handler to device_fsm.c where it's used. Remove some useless comments while at it. Acked-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.h | 2 -- drivers/s390/cio/device_fsm.c | 38 +++++++++++++++++++++++++++++++++++ drivers/s390/cio/device_ops.c | 46 ------------------------------------------- 3 files changed, 38 insertions(+), 48 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 6b17119c9929..23c1e19776d8 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -128,8 +128,6 @@ 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. */ diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 83da53c8e54c..92e03b42e661 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -730,6 +730,44 @@ static void ccw_device_boxed_verify(struct ccw_device *cdev, css_schedule_eval(sch->schid); } +/* + * 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). */ diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 6acd0b577694..5be77254547c 100644 --- a/drivers/s390/cio/device_ops.c +++ b/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 -- cgit v1.2.3 From ef12cb904e096335ef179bfe30e637a9c2464343 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 26 Oct 2015 12:38:13 +0100 Subject: s390/cio: move ccw_device_stlck functions device_ops.c should only contain functions that are called by ccw device drivers. Move the cio internal functions that handle unconditional reserve + release to device_pgid.c Acked-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.h | 3 -- drivers/s390/cio/device_ops.c | 61 -------------------------------------- drivers/s390/cio/device_pgid.c | 66 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 60 insertions(+), 70 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 23c1e19776d8..065b1be98e2c 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -125,9 +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_stlck(struct ccw_device *); /* Helper function for machine check handling. */ diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 5be77254547c..a69f702a2fcc 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -456,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/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 148b3fadac00..da246b67edfe 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -9,9 +9,10 @@ #include #include +#include #include #include -#include +#include #include #include @@ -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; +} -- cgit v1.2.3 From c7c0c9deb0a4b0715d2c09438420437b86f387c4 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Thu, 15 Oct 2015 11:10:08 +0200 Subject: s390/dasd: fix double free in dasd_eckd_read_conf The configuration data is stored per path and also the first valid configuration data per device. When dasd_eckd_read_conf is called again after a path got lost the device configuration data is cleared but possibly not the per path configuration data. This might lead to a double free when the lost path gets operational again. Fix by clearing all per path configuration data when the first valid configuration data is received and stored. Reviewed-by: Sebastian Ott Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 30c2a80c004b..e53b895a4d0f 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1032,6 +1032,21 @@ 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; @@ -1068,19 +1083,10 @@ static int dasd_eckd_read_conf(struct dasd_device *device) path_data->opm |= lpm; continue; /* no error */ } - pos = pathmask_to_pos(lpm); - kfree(private->path_conf_data[pos]); - if ((__u8 *)private->path_conf_data[pos] == - private->conf_data) { - private->conf_data = NULL; - private->conf_len = 0; - conf_data_saved = 0; - } - private->path_conf_data[pos] = - (struct dasd_conf_data *) conf_data; /* 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)) { @@ -1089,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 @@ -1146,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; } -- cgit v1.2.3 From 251afd69e3328653bae4a8ca09700971ca4000c5 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Thu, 15 Oct 2015 13:54:39 +0200 Subject: s390/dasd: fix invalid PAV assignment after suspend/resume For a valid PAV assignment the DASD driver needs to notice possibly changed configuration data. Thus the failing of read configuration data should also fail the device restore to prevent invalid PAV assignment. The failed device may get restored after additional paths get available later on. If the restore fails after the device was added to the lcu alias handling it needs to be removed from the alias handling before exiting the restore function. Reviewed-by: Sebastian Ott Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index e53b895a4d0f..3d929abb52c7 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -4435,7 +4435,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 */ @@ -4451,13 +4456,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); @@ -4468,7 +4478,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)); @@ -4479,6 +4489,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; } -- cgit v1.2.3 From ccc0e7dc708ce1bd2e1c4a2b6a180ae7459374de Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Fri, 23 Oct 2015 12:10:51 +0200 Subject: s390/dasd: fix disconnected device with valid path mask Path verification is either done via dasd_eckd_read_conf() which is triggered during online processing and resume or via do_path_verification_work() which is triggered after path events. The dasd_eckd_read_conf() version added paths unconditionally and did not check if the path mask was empty. This led to devices having the disconnected stop flag set but a valid path mask. So they where not working although they had paths validated successfully. After a resume this state could even not be solved with additional paths added. Fix by checking for an empty path mask in dasd_eckd_read_conf() and clearing the device stop bits for a newly added channel path. Reviewed-by: Sebastian Ott Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 3d929abb52c7..9083247f55a8 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1171,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 -- cgit v1.2.3 From 8f0ba83178d4f3630687296c45d593326ec2d63d Mon Sep 17 00:00:00 2001 From: Pierre Morel Date: Mon, 26 Oct 2015 10:45:04 +0100 Subject: s390/css: unneeded initialization in for_each_subchannel The ret variable is always set by the fn function. There is no need to initialize it. Signed-off-by: Pierre Morel Reviewed-By: Sascha Silbe Acked-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/css.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 25b4ee1fb2f4..2ee3053bdc12 100644 --- a/drivers/s390/cio/css.c +++ b/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); -- cgit v1.2.3 From e510f681d27d402d74dc7b81e13db118e4180937 Mon Sep 17 00:00:00 2001 From: Pierre Morel Date: Mon, 26 Oct 2015 11:13:18 +0100 Subject: s390/cio: de-duplicate subchannel validation cio_validate_io_subchannel() and cio_validate_msg_subchannel() are identical, as the called functions already take care about the differences between subchannel types. Just inline the code into the only user, cio_validate_subchannel(), instead. Signed-off-by: Pierre Morel Reviewed-by: Cornelia Huck Acked-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cio.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 0cc65b229a9c..b5620e818d6b 100644 --- a/drivers/s390/cio/cio.c +++ b/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; -- cgit v1.2.3